/*=======================================================================
  (C) Copyright 1992-2003, Fred Hutchinson Cancer Research Center
	blkprob.c  USE:  blkprob nhits search_output blocks_db [-stats]
		   OR:   blkprob blimps.cs [-mast|-rank -H n -E n -C n -sum|-gff -stats -S stp-file] 
	    eg:  blkprob 10 sample.hom blocks.dat -stats
       There are three arguments:
	  1. Maximum number of hits to report
	  2. Name of output file from a BLIMPS blocks search.
	  3. Name of blocks database searched (optional, use a nonsense
	     name to omit, eg "none").
	  4. Optionally -stats => statistics written to files blksort.dat &
	     blksort.rep (repeats)
       Tries to read files in the current directory, or in first DB directory
	if input file is a blimps.cs file:
	  1. blkprob.stp:  documentation to print out; or prints.stp
	  2. blksort.stn:  percentiles for true negative blimps scores
	  3. repeats.dat:  number of repeats according to Prosite
	  4. blksort.bias: list of blocks with biased composition

     Assumed format of input file (output of a blimps search):
       [nl]Probe Sequence:...
       [nl][n]]Target Block File:...
       [nl][nl]
       [nl][block name][sp][block #][sp][ID & DE for 60][score for 5]...etc.
       Results start on 7th line.
       Block name is in columns 1-7.
       Description is in columns 15-73.
       Score is in columns 75-79.
       Frame is in columns 80-83.
       Offset in sequence is in columns 84-89.
       Alignment to block is in columns 90-.

--------------------------------------------------------------------------
  7/8/92   J. Henikoff --- major revision of blksort.c
 12/16/98 1. Introduced hit structure.
  1/20/99 1. Added cumprob()
  3/11/99 1. 
  3/25/99 1. Don't apply cut-off to repeats.
          2. Look for blkprob.stp in first database directory
  5/14/99 1. Modified for read_results() for revised blimps 3.2.6 output.
  5/21/99 1. Check for negative NBlock, NAlign (blimps problem?)
	     Option for GFF output.
  5/25/99 1. Fixed bug in init_hits() if blimps output only has one group
  5/26/99 1. Problem in pvale_mast() fixed?; still a problem in flagging
		repeats (see assemble_hits around line 1368)
  6/12/99 1. Sequence name up to 18 chars in align_blocks()
  6/15/99 1. Memory problem in init_hits()
  6/17/99 1. Don't report repeats or other alignments if Evalue <
		CutoffRepeat
  6/17/99 2. Don't divide NAlign by 2 for DNA.
  6/29/99 1. Made -mast default, added -rank. 
	     Added hit # to end of GFF & made GFF tab-delimited.
  7/ 4/99 1. Recognize Blimps version 3.3+
  7/22/99 1. Changed type of NAlign from long long to double (for DECs)
  7/31/99 1. Don't print overlapping repeat & "other" hits
	     Fix block maps.
  8/26/99 1. More changes for DEC systems. Divide NAlign by 2 for DNA if
		results are reported for both strands.
 12/ 9/99 1. Fix memory leaks
 12/15/99 1. Accomodate ACs between 7 and 10 characters
  1/ 2/00 1. Fix output format problem
  5/21/00 1. Fix output format problem with longer ACs; remove anchor evalue
  8/21/02 1. Use sprintf() instead of kr_itoa()
  8/21/03 1. New input parameter for CutoffScore = min anchor block score
  8/22/03 1. New -H option for header (default = blkprob.stp)
 12/23/06.1. Longer sequence names
==========================================================================*/

#define EXTERN			/* Required to use blimps routines */
#include <blocksprogs.h>
#include <ctype.h>

#define NSCORE 9000		/* Maximum # of scores in .hom file */
#define MAXHIT 25		/* Default # hits to report */
#define CUTSCORE 1001		/* Min normalized anchor block score */
#define SEEK_SET 0		/* needed for UNIX fseek routine */
#define MINPAT 1000		/* Minimum blimps score for a hit */
#define MAXMAP 60		/* Maximum map width in columns */
#define MAXMAPLINE 600		/* MAXLINE in protomat.h is only 480 */
#define MAXTILES 620		/* Max. # of lines in blksort.stn */
#define MAXGROUP 2000		/* Max. # lines in repeats.dat file */
#define MAXBIAS 1000		/* Max. # lines in blksort.bias file */
#define MAXSEQ 20		/* Max. #chars in sequence name */
#define MAX_WIDTH 60		/* Max. width of block */
#define MAX_REPEAT 400		/* Max. #aas between repeats */
#define PROTEIN 1
#define DNA 3
/*		cumprob() stuff   */
#define MAXPROB 0.0005		/* upper tail prob */
#define MAXSCORE 10000
#define MAXCOL 100		/* Max matrix width */
struct score_struct {
	double ways, prob;
};

struct hom {			/* search results structure */
   char ac[MAXAC+1];		/* block name, eg. BL00094A */
   char fam[MAXAC+1];		/* family name, eg. BL00094 */
   int min_rank;		/* minimum rank for this group of blocks */
   int rank;			/* rank of this result in search */
   int frame;			/* frame of alignment */
   int strength;		/* block strengh */
   int score;			/* normalized blimps score */
   double cumprob;		/* cumprob of raw score */
   double minprob;		/* min cumprob */
   long offset;			/* offset of alignment */
   int width;			/* width of block */
   char title[25];		/* block description */
   char aa[MAX_WIDTH];	   	/* alignment to block */
   int hit_flag;		/* in assembled hit */
   int repeat_flag;		/* allowed repeat for hit */
   int other_flag;		/* other non-overlapping alignment */
   int map_flag;		/* used by map_blocks() */
				/* 0=>not mapped, 1=>mapped */
   struct blocks_info *binfo;	/* blocks info if in hit */
};

/*	Contains all the info needed to produce all the different reports */
/*  The rank_ values are for the block with the min rank (old blksort);
    the prob_ values are for the block with min cumprob    */
struct hit {
   char fam[MAXAC+1];			/* family name part of AC */
   char de[MAXLINE];
   int strand;
   int min_res, max_res;		/* indices into results array */
   int maxrep;				/* max # repeats for this ac */
   double mast_pvalue;			/* pvalue based on mast alg */
   int prob_res;			/* min cumprob block's results index */
   double prob_min;			/* min cumprob  */
   int rank_res;			/* results index for min rank block */
   int rank_min;			/* min rank for blocks in hit */
   double rank_ptile;      		/* ptile for lowest rank */
   double rank_pvalue;     		/* pvalue based on ranks */
   double evalue;			/* for sorting */
   int nblock;				/* # blocks in group */
   int nblkhit;				/* # blocks in hit */
   struct blocks_info *prob;		/* pointer to min cumprob block*/
   struct blocks_info *rank;		/* pointer to min rank block */
   struct blocks_info *binfo;
};
struct blocks_info {			/* block information needed for a hit */
   char ac[MAXAC+1];         		/* accession number */
   int nseq, width, strength;		/* #seqs, width, strength */
   int minprev, maxprev;		/* distances from prev block */
   int bias;				/* flag if block is biased */
   int hit_flag;			/* flag if block is included in hit */
   int query_res;			/* index into Results[] for query */
   int nrep;				/* number of repeats for block */
   char closest_name[SNAMELEN];		/* name of seq closest to query */
   long closest_offset;			/* offset of seq closest to query */
   char closest_aa[MAX_WIDTH];		/* aas for seq closest to query */
   struct blocks_info *next;
};
struct blocks_list {			/* a full set of blocks */
   char fam[MAXAC+1];			/* family part of AC  */
   Block *block;
   Matrix *pssm;
   struct blocks_info *binfo;
   struct blocks_list *next;
};


struct files_list {
   FILE *fdat;
   char datname[FNAMELEN];
   char prevfam[MAXAC+1];		/* family name part of AC ??? */
   long prevdat;
   struct files_list *next;
};

/*---- Functions in blkprob.c -----*/
int read_config();
void print_blurb();
int get_info();

void open_dbs();
void close_dbs();
struct files_list *make_flist();

void insert_flist();
int read_tiles();			/* read percentiles file */
int read_repeats();			/* read repeats.dat */
int read_bias();			/* read blksort.bias */
void read_results();

int init_hits();
void init_binfo();
struct blocks_list *read_fam();
int group_results();
int assemble_hits();
int assemble_hits_rank();
int print_hits();
void map_blocks();
void align_blocks();

int get_repeats();
double get_ptile();
void check_overlap();
double hypergeo();
int cumprob();
void pvalues();
double pvalue_rank();
double pvalue_mast();
double qfast();
int distance_okay();
long prev_dist();
int distance();
long compute_loc();
void closest_seq();
void consensus();
/*		sorting routines   */
int strandcmp();		
int framecmp();		
int mincmp();	
int tempcmp();
int dtempcmp();
int stempcmp();
int stempcmp2();
int hitcmp();
int hitrank();
int hitfam();
int hitprob();
int hitprank();
int hitmpv();
int hiteval();
/*		blocks_list routines, should be put in a library */
struct blocks_list *make_blist();
void insert_blist();
void free_blist();
/*		blocks_info routines, should be put in a library */
struct blocks_info *make_binfo();
void insert_binfo();
void free_binfo();

/*-------Functions in protomat.c --------*/
struct split_name *split_names();
/*  void kr_itoa(); */

/*------------Global variables -------------*/
int NRead;
int NCumprob;
char Version[12] = "12/23/06.1";/* Version number */
int BlimpsVer;			/* Blimps version */
char Query[SNAMELEN];		/* Query sequence name */
char Qfilename[FNAMELEN];	/* Query file */
unsigned long QLen;		/* Query length */
int NBlock;			/* # blocks searched */
double NAlign;			/* # alignments done */
double Strands;			/* # of strands reported */
double CutoffRepeat;		/* Single block hit cuttoff */
double CutoffExp;		/* Cutoff evalue */
int CutoffScore;		/* Min anchor block score */
int SeqType;			/* =1 if amino acid, =3 if nucleotide */
int MaxHit;			/* # hits to report */
int MaxHits;			/* Max size of hits array */
int Ntiles;			/* # of percentiles read in */
float Tiles[MAXTILES];		/* %-tiles values for scores 1000-1620 */
int Stats;			/* statistics flag */
int Summary;			/* summary output only flag */
int GFF;			/* GFF output flag */
int Mast;			/* use Mast-style evalues */
char HomName[FNAMELEN];		/* Search file name for stats file */
double CumProb[MAXSCORE];	/* Score distribution */
char StpFile[80];		/* Name of header file */

struct working {
  char ac[MAXAC+1];
  int num;
} Repeats[MAXGROUP], Bias[MAXBIAS];	/* Working area */
int NRepeat;			/* # of repeat values read in */
int MaxRepeat;			/* max # of repeats in any group */
int NBias;			/* # of bias values read in */

int Config_Flag;		/* 1st arg is blimps config file */
char DatDir[FNAMELEN];		/* Directory of database files */

struct hom *Results;		/* Blimps search results */
int NResult;

/*======================================================================*/
int main(argc, argv)
int argc;
char *argv[];
{
   FILE *fhom, *fcf;
   char homfile[FNAMELEN], datfile[FNAMELEN], ctemp[FNAMELEN];
   char db[FNAMELEN];
   int i, ngrp, ndb, nhom, nhit;
   struct split_name *datsplit;
   struct files_list *flist, *newdat;
   struct hit *hits;

   ErrorLevelReport = 4;			/* Blimps error level */
   printf("\nBLKPROB Version %s\n", Version);
   if (argc < 2)
   {
      printf("COPYRIGHT 1992-8 Fred Hutchinson Cancer Research Center\n");
      printf("USAGE: blkprob <nhits> <blimps_file> <blocks_file> [-H n -E n -C n -mast|-rank -sum|-gff -stats]\n");
      printf("    <nhits>       = number of hits to report, use 0 for default\n");
      printf("    <blimps_file> = blimps search output file\n");
      printf("    <blocks_file> = blocks database searched by blimps\n");
      printf("OR:    blkprob <blimps.cs>\n");
      printf("    <blimps.cs> = name of blimps configuration file\n");
      printf("Other options\n");
      printf("    -H n            maximum number of hits to report\n");
      printf("    -E n            cutoff expected value for anchor blocks\n");
      printf("    -C n            min cutoff raw score for anchor blocks\n");
      printf("    -mast           order results by MAST-type e-values\n");
      printf("    -rank           order results by RANK-statistic e-values\n");
      printf("    -sum            output summary only (one line per hit)\n");
      printf("    -gff		  Sanger Center's GFF output\n");
      printf("    -stats          produces <Query>.dat statistics file\n");
      printf("    -S filename     header file (default=blkprob.stp)\n");
   }
   Config_Flag = NO;
   fcf = NULL;
   MaxHit = 0;

   /*-----------arg 1: # hits to report or config file---------------------*/
   ctemp[0] = '\0';
   flist = make_flist();
   ndb = 0;
   if (argc > 1) strcpy(ctemp, argv[1]);
   else
   {
      printf("\nEnter number of hits to report or ");
      printf("blimps configuration file name [%d]: ", MaxHit);
      gets(ctemp);
   }
   if (!strlen(ctemp)) strcpy(ctemp, "0");
   for (i=0; i<strlen(ctemp); i++)
          if (!isdigit(ctemp[i])) Config_Flag = YES;
   if (Config_Flag)
   {
         if ( (fcf=fopen(ctemp, "r")) == NULL)
         {
            printf("\nBLKPROB: Cannot open configuration file %s\n", ctemp);
            exit(-1);
         }
         else
	 { 
            ndb = read_config(fcf, homfile, flist);
            if (ndb > 0) strcpy(datfile, flist->next->datname);
         }
    }   /* end of read parameters from blimps config file */
    else
    {   /* parameters are on the command line */
          MaxHit = atoi(ctemp);

         /*------------- arg 2:  .hom file -------------------------------*/
         if (argc > 2)
            strcpy(homfile, argv[2]);
         else
         {
            printf("\nEnter name of file containing blocks search results: ");
            gets(homfile);
         }
         /*------------- arg 3:  blocks database file -----------------------*/
         db[0] = '\0';
         if (argc > 3)
            strcpy(datfile, argv[3]);
         else
         {
            printf("\nEnter name of blocks database searched: ");
            gets(datfile);
         }
         if (strlen(datfile))
         {
            newdat = make_flist();
            strcpy(newdat->datname, datfile);
            insert_flist(flist, newdat);
            ndb++;
         }

    }  /* end of non-config file input */

    /*------------- Check for other options----------------------------*/
    Stats = Summary = GFF = NO;
    Mast = YES;
    CutoffExp = 1.0;		/* default expected value is 1 per search*/
    CutoffScore = CUTSCORE;
    StpFile[0] = '\0';
    for (i=2; i<argc; i++)
    {
       if (strcasecmp(argv[i], "-mast") == 0) Mast = YES;
       else if (strcasecmp(argv[i], "-rank") == 0) Mast = NO;
       else if (strcasecmp(argv[i], "-stats") == 0) Stats = YES;
       else if (strcasecmp(argv[i], "-sum") == 0) Summary = YES;
       else if (strcasecmp(argv[i], "-gff") == 0) GFF = YES;
            else if (strncasecmp(argv[i], "-E", 2) == 0 && argc > i)
                 {
			CutoffExp = atof(argv[i+1]);
                        if (CutoffExp < 0.0) CutoffExp = 1.0;
                        if (CutoffExp > 100.0) CutoffExp = 100.0;
                 }
                 else if (strncasecmp(argv[i], "-H", 2) == 0 && argc > i)
                      {
			MaxHit = atoi(argv[i+1]);
                        if (MaxHit < 0) MaxHit = 0;
                      }
                 else if (strncasecmp(argv[i], "-C", 2) == 0 && argc > i)
                      {
			CutoffScore = atoi(argv[i+1]);
                        if (CutoffScore < 0) CutoffScore = 0;
                      }
                 else if (strncasecmp(argv[i], "-S", 2) == 0 && argc > i)
                      {
                        strcpy(StpFile, argv[i+1]);
                      }
    }
    if (Summary && GFF) Summary = NO;

   /*  Get the directory of the first datbase file */
   datsplit = split_names(datfile);
   strncpy(DatDir, datfile, datsplit->dir_len);
   DatDir[datsplit->dir_len] = '\0';
   
   /*-------------------------------------------------------------------*/
   /*-----------------Open the search results --------------------------*/
   if ( (fhom=fopen(homfile, "r")) == NULL)
   {
      printf("\nBLKPROB: Cannot open file blimps file %s\n", homfile);
      exit(-1);
   }
   nhom = get_info(fhom, homfile);    /* returns # lines in blimps file */

   /*--- Set MaxHit based on sequence length & type to report at least
	  1 hit per 1000kb of DNA sequence ---*/
   if (MaxHit <= 0)
   {
      if (SeqType == DNA)
      {
         MaxHit = (int) QLen/1000 + 1;
         if (MaxHit < MAXHIT) MaxHit = MAXHIT;
      }
      else  MaxHit = MAXHIT;
   }
   MaxHits = MaxHit;

   /*---------------Open the blocks database files---------------------*/
   open_dbs(flist);

   /*---------------------------------------------------------------------*/
   if (!GFF) print_blurb(ndb);
   printf("\nQuery=%s\nSize=%ld ", Qfilename, QLen);
   if (SeqType == PROTEIN) printf("Amino Acids\n");
   else if (SeqType == DNA) printf("Base Pairs\n");
   if (NBlock > 0) printf("Blocks Searched=%d\n", NBlock); 
   if (NBlock < 1)
   {
      printf("Blocks searched is negative, cannot use for statistics.\n");
      NBlock = 1;
   }
   if (NAlign > 0.0) printf("Alignments Done=%16.0f\n", NAlign); 
   if (NAlign < 1.0)
   {
      printf("Alignments done is negative, cannot use for statistics.\n");
      NAlign = 1.0;
   }
   printf("Cutoff combined expected value for hits=%3.0f\n", CutoffExp);
   printf("Cutoff block expected value for repeats/other=%3.0f\n", 
	   CutoffRepeat);

   /*---------------------------------------------------------------------*/
   frq_qij();			/* load the default PSSM-making files */
   Ntiles = read_tiles();
   NRepeat = read_repeats();
   NBias = read_bias();

   Results = (struct hom *) malloc(nhom * sizeof(struct hom));
   if (Results == NULL)
   {  printf("\nResults: OUT OF MEMORY\n\n"); exit(-1); }
   read_results(fhom);
   fclose(fhom);
   
   ngrp = group_results();
   /*  MaxHits is a crude way to limit memory use, should really
	allocate ngrp hits  */
   MaxHits  = ngrp;
   if (MaxHits > MaxHit) MaxHits = 2*MaxHit; /* extra slots for equal scores */
   hits = (struct hit *) malloc((MaxHits+1) * sizeof(struct hit));
   if (hits == NULL)
   {  printf("\nhits: OUT OF MEMORY\n\n"); exit(-1); }
   /*  Retrieve blocks, compute individual block probabilities, closest seq */
   nhit = init_hits( hits);
   NAlign /= Strands;

/*
printf("NResult=%d ngrp=%d nhit=%d\n\n", NResult, ngrp, nhit);
*/
   if (nhit < 1)
   { printf("No hits found\n"); exit(-1);   }

NRead = NCumprob = 0;
   if (flist->next != NULL) init_binfo(flist, nhit, hits);
/*
printf("NRead=%d NCumprob=%d\n", NRead, NCumprob);
*/
   close_dbs(flist);

   /* Piece together compatible blocks from potential hits & compute hit prob*/
   assemble_hits(nhit, hits);

   pvalues(nhit, hits);

   printf("\n%d possible hits reported\n", print_hits(nhit, hits) );

   exit(0);
}  /*  end of main */
/*============================================================================
  Read a blimps config file:  OU=blimps output file, DB=blocks db(s) searched
  How to override default MaxHit from here - NU ?
=============================================================================*/
int read_config(fcf, homfile, flist)
FILE *fcf;
char homfile[FNAMELEN];
struct files_list *flist;
{
   char line[MAXLINE], keyword[20], *ptr;
   int ndb;
   struct files_list *newdat;

   ndb = 0;
   homfile[0] = '\0';
   /*   Read until end of file or until "//" line is reached   */
   while ( !feof(fcf) && fgets(line, MAXLINE, fcf) != NULL &&
           (strncmp(line, "//", 2)) )
   {
      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, "OU", 2) == 0)
            {
               strcpy(homfile, ptr);
            }
            else if (strncasecmp(keyword, "DB", 2) == 0)
            {
               newdat = make_flist();
               strcpy(newdat->datname, ptr);
               insert_flist(flist, newdat);
               ndb++;
            }
         }   /* end of if ptr */
      }  /* end of if ptr */
   }  /* end of fcf */

   return (ndb);
}  /* end of read_config */
/*======================================================================*/
void print_blurb(ndb)
int ndb;
{
   FILE *fstp;
   char fname[FNAMELEN], line[MAXLINE];

   
   /*  First, look for a user file */
   if ((fstp=fopen(StpFile, "r")) == NULL)
   {
      /* If only one database, look for blkprob.stp first in its directory,
      then in the current directory, otherwise look in the current
      directory only */
      if (ndb == 1)
      {   sprintf(fname, "%sblkprob.stp", DatDir); }
      else
      {   strcpy(fname, "blkprob.stp");  }
      if ((fstp=fopen(fname, "r")) == NULL)
      {
         if (ndb == 1)
         {
            strcpy(fname, "blkprob.stp");
            fstp=fopen(fname, "r");
         }
      }
   }

   if (fstp == NULL)
   {
      printf("\n========================================");
      printf("=======================================");
      printf("\nHere are your search results from the BLOCKS searcher.");
      printf("\n========================================");
      printf("=======================================");
      printf("\n");
   }
   else
   {
      while (!feof(fstp) && fgets(line, MAXLINE, fstp) != NULL)
         printf("%s", line);
      fclose(fstp);
   }

}  /* end of print_blurb */
/*=====================================================================
     Set global variables from the preface of the blimps results file
=========================================================================*/
int get_info(fhom, homfile)
FILE *fhom;
char homfile[FNAMELEN];
{
   char line[MAXLINE], db[FNAMELEN];
   char *ptr, *ptr1;
   int i, j, nrec, done;
   long fpos;
   struct split_name *homsplit;

   homsplit = split_names(homfile);
   strncpy(HomName, homfile + homsplit->dir_len, homsplit->name_len);
   HomName[homsplit->name_len] = '\0';
   db[0] = Qfilename[0] = Query[0] = '\0';
   NBlock = 0; QLen = (unsigned long) 0;
   NAlign = 0.0;
   SeqType = PROTEIN;		/* amino acid is default type */
   BlimpsVer = 326;		/* Blimps format changed with 3.2.6 */
   done = NO;
   while (!done &&
	  !feof(fhom) && fgets(line,MAXLINE,fhom) != NULL)
   {
      if (strstr(line, "Version 3.2.5")) BlimpsVer = 325;
      if (strstr(line, "Version 3.2.4")) BlimpsVer = 325;
      if (strstr(line, "Version 3.2.3")) BlimpsVer = 325;
      if (strlen(line) > SNAMELEN && strstr(line, "Probe Sequence:") != NULL)
      {   /*  "Probe Sequence: query  Probe Size: length" */
         ptr = strstr(line, "Probe Sequence:");
         /* Copy up to SNAMELEN characters to Query name */
         j = strlen(ptr); if (j > FNAMELEN) j = FNAMELEN;
	 strncpy(Qfilename, ptr+16, j); Qfilename[j] = '\0';

         /*  Get rid of tabs, etc. put first SNAMELEN chars in Query */
         if (Qfilename[0] == '>') i = 1; else i = 0;
         j = 0;
         for (i=0; i<strlen(Qfilename); i++)
         {
            if (Qfilename[i] == '\t' || Qfilename[i] == '\r' ||
                Qfilename[i] == '\n')
            { Qfilename[i] = ' '; }
            if (j < SNAMELEN) { Query[j++] = Qfilename[i];  }
         }
         Query[j] = '\0';
         /*  Terminate Query at first space  */
         j = 0;
         while (j < strlen(Query) && Query[j] != ' ')
                 j++;
         Query[j] = '\0';

         ptr = strstr(line, "Size:");
         if (ptr != NULL)
         {
            if (strstr(ptr, "Base Pair") != NULL) SeqType = DNA;
            ptr1 = strtok(ptr, ":"); ptr1 = strtok(NULL, " \n\r");
            QLen = atol(ptr1);
         }
      }
      else if (strlen(line) > SNAMELEN && strstr(line, "Size") != NULL)
      {   /* Probe Size: length Amino Acids */
         ptr = strstr(line, ":"); 
         if (ptr != NULL)
         {
            if (strstr(ptr, "Base Pair") != NULL) SeqType = DNA;
            ptr1 = strtok(ptr, " ");
            if (ptr1 != NULL)
            {
               ptr1 = strtok(NULL, " \n\r");
               if (ptr1 != NULL) QLen = atol(ptr1);
            }
         }
      } 
      else if (strlen(line) > SNAMELEN && strstr(line, "Target") != NULL)
      {   /* Target File (s): database */
/*	 strcpy(db, &line[20]); db[strlen(db)-1] = '\0'; */
         ptr = strstr(line, ":"); 
         if (ptr != NULL)
         {
            ptr1 = strtok(ptr, " ");
            if (ptr1 != NULL)
            {
               ptr1 = strtok(NULL, " \n\r");
               if (ptr1 != NULL) strcpy(db, ptr1);
            }
         }
      }
      else if (strlen(line) > SNAMELEN && strstr(line, "Records") != NULL)
      {  /* Records Searched: nblock */
         ptr = strstr(line, ":");
         if (ptr != NULL)
         {
            ptr1 = strtok(ptr, " ");
            if (ptr1 != NULL)
            {
               ptr1 = strtok(NULL, " \n\r");
               if (ptr1 != NULL) NBlock = atoi(ptr1);
            }
         }
      }
      else if (strlen(line) > SNAMELEN && strstr(line, "Alignments") != NULL)
      {  /* Alignments Done: nalign */
         ptr = strstr(line, ":");
         if (ptr != NULL)
         {
            ptr1 = strtok(ptr, " ");
            if (ptr1 != NULL)
            {
               ptr1 = strtok(NULL, " \n\r");
               if (ptr1 != NULL) NAlign = atof(ptr1);
            }
         }
         done=YES;
      }
      else if (strncmp(line, "AC#", 3) == 0) done=YES;
   }

   /*  Don't report any single block hits for which the highest-scoring block's
       cumprob (position p-value) is surely a chance hit */
/*
   CutoffRepeat = 1.0;
   if (NAlign > 0)
   {
      CutoffRepeat = 1.0/ (double) NAlign;
      if (SeqType == DNA) {  CutoffRepeat = 2.0 / NAlign; }
      else                {  CutoffRepeat = 1.0 / NAlign; }
   }
*/
   CutoffRepeat = CutoffExp;
   if (SeqType == DNA)
   {
      CutoffRepeat *= 2.0;
   }
   Strands = 1.0;		/* Changed later if 2nd detected */

   fpos = ftell(fhom);
   nrec = 0;
   while (!feof(fhom) && fgets(line,MAXLINE,fhom) != NULL) nrec++;
   /*  Reposition file to start of results for read_results()  */
   fseek(fhom, fpos, SEEK_SET);

   return(nrec);

}   /*  end of get_info */
/*========================================================================
     Open all the files in the list of blocks databases searched
==========================================================================*/
void open_dbs(flist)
struct files_list *flist;
{
   struct files_list *fcur;

   fcur = flist->next;
   while (fcur != NULL)
   {
      if ( (fcur->fdat=fopen(fcur->datname, "r")) == NULL)
      {
         printf("\nBLKPROB: Cannot open blocks file %s", fcur->datname);
      }
      else
      {
         fcur->prevdat = ftell(fcur->fdat);
         printf("Database=%s\n", fcur->datname); 
      }
      fcur = fcur->next;
   }
}   /*  end of open_dbs */
/*========================================================================
     Close all the files in the list of blocks databases searched
==========================================================================*/
void close_dbs(flist)
struct files_list *flist;
{
   struct files_list *fcur;

   fcur = flist->next;
   while (fcur != NULL)
   {
      if (fcur->fdat != NULL) fclose(fcur->fdat);
      fcur = fcur->next;
   }
}   /* end of close_dbs */
/*======================================================================*/
int read_tiles()
{
   int ntiles, score, i;
   float tile;
   FILE *fstn;
   char fname[FNAMELEN], line[MAXLINE];

   ntiles = 0;

   /*   Look in current directory first */
   fstn=fopen("blksort.stn", "r");
   if (fstn == NULL)
   {
      sprintf(fname, "%sblksort.stn", DatDir);
      fstn=fopen(fname, "r");
   }

   if (fstn != NULL)
   {
      while (ntiles < MAXTILES && 
             !feof(fstn) && fgets(line, MAXLINE, fstn) != NULL)
      {
         if (line[0] != '>' && line[0] != '#')
         {
            sscanf(line, "%d %f", &score, &tile);
            for (i=ntiles; i<=(score-1000) ;i++)
               Tiles[i] = tile;
            ntiles = i;
         }
      }
      fclose(fstn);
   }

   return(ntiles);
}  /*  end of read_tiles */
/*======================================================================*/
int read_repeats()
{
   int nrep;
   FILE *frep;
   char line[MAXLINE], fname[FNAMELEN];

   nrep = 0;
   MaxRepeat = 0;

   /*   Look in current directory first */
   frep=fopen("repeats.dat", "r");
   if (frep == NULL)
   {
      sprintf(fname, "%srepeats.dat", DatDir);
      frep=fopen(fname, "r");
   }

   if (frep != NULL)
   {
      while (nrep < MAXGROUP && 
             !feof(frep) && fgets(line, MAXLINE, frep) != NULL)
      {
         if (line[0] != '>' && line[0] != '#')
         {
            sscanf(line, "%s %d", Repeats[nrep].ac, &Repeats[nrep].num);
            if (Repeats[nrep].num < 0) Repeats[nrep].num = 0;
            if (Repeats[nrep].num > MaxRepeat) MaxRepeat = Repeats[nrep].num;
            nrep++;
         }
      }
   }
   return(nrep);
}  /* end of read_repeats */
/*======================================================================*/
int read_bias()
{
   int nbias;
   FILE *fbias;
   char line[MAXLINE], fname[FNAMELEN];

   nbias = 0;

   /*   Look in current directory first */
   fbias = fopen("blksort.bias", "r");
   if (fbias == NULL)
   {
      sprintf(fname, "%sblksort.bias", DatDir);
      fbias = fopen(fname, "r");
   }

   if (fbias != NULL)
   {
      while (nbias < MAXBIAS && 
             !feof(fbias) && fgets(line, MAXLINE, fbias) != NULL)
      {
         if (line[0] != '>' && line[0] != '#')
         {
            sscanf(line, "%s %d", Bias[nbias].ac, &Bias[nbias].num);
            if (Bias[nbias].num < 1) Bias[nbias].num = 1;
            nbias++;
         }
      }
   }
   return(nbias);
}  /* end of read_bias */
/*=======================================================================
	Read the blimps results into memory; assumes fixed format
        Groups results by AC within each frame but doesn't determine
	 which blocks for an AC constitute a valid hit.
        Puts search results into global array Results[], sets NResult
=========================================================================*/
void read_results(fhom)
FILE *fhom;
{
   int i, done, title_len;
   int ac_pos, title_pos, str_pos, score_pos, frame_pos, off_pos, aa_pos;
   char line[MAXLINE], ctemp[8], save_ac[MAXAC+1];

   if (BlimpsVer >= 326)
   {
      ac_pos=0; title_pos=12; str_pos=66; score_pos=73; frame_pos=80; 
      off_pos=82; aa_pos=90; title_len=25;
   }
   else		/* Blimps version 3.2.5 and earlier */
   {
      ac_pos=0; title_pos=14; str_pos=63; score_pos=70; frame_pos=77; 
      off_pos=79; aa_pos=87; title_len=25;
   }
   NResult = 0;
   while (!feof(fhom) && fgets(line,MAXLINE,fhom) != NULL)
   {
      if (strlen(line) > aa_pos && NResult < NSCORE)
      {
	 save_ac[0]= '\0';
	 strncpy(save_ac, &line[ac_pos], MAXAC); save_ac[MAXAC] = '\0';
         for (i=MAXAC; i >= MINAC; i--)
            if (save_ac[i] == ' ') save_ac[i] = '\0';
	 strcpy(Results[NResult].ac, save_ac);
         i = strlen(save_ac); done = NO;
         while (!done && i >= MINAC)
         {
            if (isalpha(save_ac[i])){ save_ac[i] = '\0'; done=YES; }
            else  { i--;  }
         }
	 strcpy(Results[NResult].fam, save_ac);

	 Results[NResult].rank = Results[NResult].min_rank = NResult + 1;
	 Results[NResult].title[0] = '\0';
	 strncpy(Results[NResult].title, &line[title_pos], title_len);
	 Results[NResult].title[title_len - 1] = '\0';
         strncpy(ctemp, &line[str_pos], 6); ctemp[6] = '\0';
         Results[NResult].strength = atoi(ctemp);
	 strncpy(ctemp, &line[score_pos], 6); ctemp[6] = '\0';
	 Results[NResult].score = atoi(ctemp);
	 strncpy(ctemp, &line[frame_pos], 2); ctemp[2] = '\0';
	 Results[NResult].frame = atoi(ctemp);
	 strncpy(ctemp, &line[off_pos], 7); ctemp[7] = '\0';
	 Results[NResult].offset = atol(ctemp);
	 Results[NResult].width = 0; i=aa_pos;
	 while (line[i++] != '\n') Results[NResult].width += 1;
	 if (Results[NResult].width > MAX_WIDTH)
		Results[NResult].width=MAX_WIDTH;
	 strncpy(Results[NResult].aa, &line[aa_pos], Results[NResult].width);
	 Results[NResult].aa[Results[NResult].width] = '\0';
         /*    Initialize fields that will be used later */
         Results[NResult].hit_flag = NO;
         Results[NResult].repeat_flag = NO;
         Results[NResult].other_flag = NO;
         Results[NResult].map_flag = NO;
         /*   For sorting purposes, make this max instead of min value */
         Results[NResult].cumprob = Results[NResult].minprob = 1.0;
         Results[NResult].binfo = NULL;
	 NResult++;
      }
   }
}  /* end of read_results */

/*========================================================================
	Group results into strand,ac pairs; each group has unique min_rank
==========================================================================*/
int group_results()
{
   int i, j, ngrp, strand, save_strand, save_rank, min_rank;
   char save_fam[MAXAC+1];

   /*----------Sort by strand and block name-----------*/
   qsort(Results, NResult, sizeof(struct hom), strandcmp);

   /*--------Group the results & determine minimum rank -----------*/
   ngrp = 0;
   strcpy(save_fam, Results[0].fam); save_rank = 0;
   if (Results[0].frame < 0) save_strand = -1;
   else                   save_strand = 1;
   min_rank = Results[0].rank;
   for (i=1; i<NResult; i++)
   {
      if (Results[i].frame < 0) strand=-1; else strand = 1;
      if (save_strand != strand ||
	  strcmp(save_fam, Results[i].fam) != 0 )
      {        /* new group */
	 if (i > save_rank)
	   for (j=save_rank; j<i; j++)
	     Results[j].min_rank = min_rank;
	 strcpy(save_fam, Results[i].fam); save_rank = i;
	 if (Results[i].frame < 0) save_strand=-1; else save_strand=1;
	 min_rank = Results[i].rank;
         ngrp++;
      }
      else   /*  Same block group */
	 if (Results[i].rank < min_rank) min_rank = Results[i].rank;
   }
   /*---------------Check for final group ----------------*/
   if (i > save_rank)
   {
       for (j=save_rank; j<i; j++) Results[j].min_rank = min_rank;
       ngrp++;
   }

   return(ngrp);

}  /* end of group_results */
/*=======================================================================
      Create a hit for each strand,ac pair
      Retrieve block info if dbs
========================================================================*/
int init_hits(hits)
struct hit *hits;
{
   int ires, done, save_rank, nhit, last_strand;

   /*----------Resort by min_rank, strand, block name & rank-------------*/
   /*   This is the last time Results[] will be sorted                    */
   qsort(Results, NResult, sizeof(struct hom), mincmp);

   /*------Create a hit record for each ac,strand & add ac-related info---*/
   /*------Stop making hits if highest-ranking score is not > 1000 or
      if nhit == MaxHit, unless current score == last score --------------*/
   nhit = -1;			/* first hit is # 0 */
   save_rank = 0;		/* each strand,ac pair has unique min_rank */
   last_strand = 0;
   done = NO;
   ires = 0;
   while (!done && nhit < MaxHits && ires < NResult)
   {
      if (Results[ires].min_rank != save_rank)
      {
         /*  See if the previous hit is the last one   */
         if ( nhit >= 0 && Results[ hits[nhit].rank_res ].score < CutoffScore)
         {  done = YES; }

         else if ( nhit >=1 && nhit == MaxHit &&
 Results[ hits[nhit].rank_res ].score < Results[ hits[nhit-1].rank_res ].score)
         {  done = YES; }

         if (!done)
         {
            nhit++;
            save_rank = Results[ires].min_rank;
            strcpy(hits[nhit].fam, Results[ires].fam); 
            if (Results[ires].frame < 0) hits[nhit].strand = -1;
            else                         hits[nhit].strand = 1;
            if (last_strand == 0) last_strand = hits[nhit].strand;
            else if (Strands == 1.0 &&
                     last_strand != 0 && last_strand != hits[nhit].strand)
                 { Strands = 2.0; }

            hits[nhit].min_res = hits[nhit].max_res = ires;
            hits[nhit].rank_min = save_rank;

            hits[nhit].prob_res = hits[nhit].rank_res = 0;
            hits[nhit].rank_ptile = hits[nhit].evalue = 0.0;
            hits[nhit].rank_pvalue = hits[nhit].mast_pvalue = 0.0;
            hits[nhit].prob_min = 0.0;
            hits[nhit].nblock = hits[nhit].nblkhit = 0;

            /* Later hits will be sorted by probs, but will always
               get from hits[] to Results[] via these indices  */
            hits[nhit].rank = hits[nhit].binfo = hits[nhit].prob = NULL;

            if (Results[ires].rank == save_rank)
            {
               hits[nhit].rank_res = ires;
               hits[nhit].rank_ptile = get_ptile(Results[ires].score);
            }
        
            /* Check whether this ac allows repeats */
            hits[nhit].maxrep = get_repeats(hits[nhit].fam); 

         }   /* end of !done */
      }  /* end of new hit */
      else		/* same hit */
      {
            hits[nhit].max_res = ires;
            if (Results[ires].rank == save_rank)
            {
               hits[nhit].rank_res = ires;
               hits[nhit].rank_ptile = get_ptile(Results[ires].score);
            }
      }   /* end of same hit */
      ires++;
   }   /* end of first pass through Results for ac data */

   /*   Problem if only one result, nhit++ never gets incremented */
/*
   if (nhit == 0 && hits[nhit].rank_res >= 0) nhit++;
*/

   return(nhit+1);
}  /* end of init_hits */

/*=====================================================================
   Put block information in hits[].binfo
   hit -> Results[min_res] to Results[max_res] = all results for AC
   hit -> binfos = all database blocks for AC
   binfo -> Results[query] = highest-scoring result for block
   binfo -> Results[repeats] = lower-scoring results for block
	"score" is either Results[].rank or Results[].cumprob
======================================================================*/
void init_binfo(flist, nhit, hits)
struct files_list *flist;
int nhit;
struct hit *hits;
{
   struct blocks_list *blist, *bcur, *bprev;
   struct blocks_info *binfo;
   int ihit, ires, raw;
   double dtemp, minprob, maxraw;

   /*  More efficient to sort by family first   */
   qsort(hits, nhit, sizeof(struct hit), hitfam);

   for (ihit=0; ihit<nhit; ihit++)
   {
      /*   blist is a list of database blocks for this ac 
           pertinent information is saved in binfo         */
      blist = read_fam(flist, hits[ihit].fam);
      if (blist != NULL)
      {
         bcur = blist;
         strcpy(hits[ihit].de, bcur->block->de);
         hits[ihit].binfo = make_binfo(bcur->block);
         bcur->binfo = hits[ihit].binfo;
         hits[ihit].nblock = 1;
         bcur = bcur->next;
         while (bcur != NULL)
         {
               binfo = make_binfo(bcur->block);
               insert_binfo(binfo, hits[ihit].binfo);
               hits[ihit].nblock += 1;
               bcur->binfo = binfo;
               bcur = bcur->next;
         }

         /*  Go through Results[] for this hit & calc minprob    */
         maxraw = 0;
         minprob = 99.0;
         bprev = NULL;
         for (ires = hits[ihit].min_res; ires <= hits[ihit].max_res; ires++)
         {
            /*  Set bcur to the db block for this Results[]   */
            if (bprev != NULL &&
                   strcmp(Results[ires].ac, bprev->block->number) == 0)
            { bcur = bprev;  }
            else
            {
               bcur = blist; 
/*
printf("%d:%s vs %d:%s\n", strlen(Results[ires].ac), Results[ires].ac,
                           strlen(bcur->block->number), bcur->block->number);
*/
               while (bcur != NULL &&
                   strcmp(Results[ires].ac, bcur->block->number) != 0)
               { bcur = bcur->next; }
            }

            if (bcur != NULL && bcur->block != NULL &&
                   strcmp(Results[ires].ac, bcur->block->number) == 0)
            {
                /*  bcur is now the block that corresponds to Results[ires]
                       & bcur->binfo points to the hit->binfo record for it */
                /*  Get cumprob for Results[ires]  */
                if (bcur != bprev)
                {
                   /*  Creates global array CumProb[]  */
                   maxraw = cumprob(bcur->pssm);
NCumprob++;
                   bprev = bcur;
                }
                dtemp = (double) Results[ires].score;
                dtemp *= (double) bcur->block->percentile;
                raw = round(dtemp/1000.0);
                if (raw < 0) raw = 0;
                if (raw > maxraw) raw = maxraw;
                Results[ires].cumprob = CumProb[raw];

                if (Results[ires].rank == hits[ihit].rank_min)
                {
                      hits[ihit].rank = bcur->binfo;
                }

                /*  Could be more than one Results[] with this ac
                    want to link binfo to the first one; Results[]
			is sorted by rank within block name  */
                if (bcur->binfo->query_res < 0)
                {
                      bcur->binfo->query_res = ires;

                      if (Results[ires].cumprob < minprob)
                      {
                          minprob = Results[ires].cumprob;
                          hits[ihit].prob = bcur->binfo;
                          hits[ihit].prob_res = ires;
                          hits[ihit].prob_min = minprob;
                      }

		      /*  calc closest seq & put in bcur->binfo */
                      /*  Would rather do this in assemble_hits(), but
                          no longer have the blocks to for closest_seq() */
                      closest_seq(bcur);
                }  /*  end of query_res */
            }  /* end of bcur->binfo for this ires */
         }  /*  end of Results[ires] for hit */

         /* Go through Results[] again & set minprob for all   */
         for (ires = hits[ihit].min_res; ires <= hits[ihit].max_res; ires++)
         { 
		Results[ires].minprob = minprob;
         }  /*  end of ires  */

         /*   Done with blocks & pssms now */
         free_blist(blist);
      }  /* end of blist for hit */
   }  /* end of ihit */

}   /* end of init_binfo */

/*=========================================================================
	Look up number of repeats for ac     
==========================================================================*/
int get_repeats(fam)
char *fam;
{
   int j;

   j = 0;
   /*   Assumes repeats.dat is sorted by ac     */
   while (j < NRepeat && strcmp(Repeats[j].ac, fam) <= 0) j++;
   if (j > 0 && strcmp(Repeats[j-1].ac, fam) == 0)
	return(Repeats[j-1].num);
   else return(0);
} /* end of get_repeats  */

/*=======================================================================
      Find hits among results: anchor block must score above cutoff;
	other blocks must be in order and distanced appropriately
      Compute pvalue elsewhere: old, resort hit results by rank
				mast, just need the cumprobs
========================================================================*/
int assemble_hits_rank(nhit, hits)
int nhit;
struct hit *hits;
{
   struct blocks_info *binfo;
   struct temp *temp;
   int i, t, ihit, min, nres;

   /*  sort by min_rank */
   qsort(hits, nhit, sizeof(struct hit), hitrank);

   for (ihit = 0; ihit < nhit; ihit++)
   {
      min = hits[ihit].min_res;
      nres = hits[ihit].max_res - min + 1;
      /*------ Sort this group of hits by rank now --------------*/
      temp = (struct temp *) malloc((nres) * sizeof(struct temp));
      if (temp == NULL) 
      { printf("assemble_hits(): OUT OF MEMORY\n\n"); exit(-1); }

      for (i=0; i<nres; i++)
      {
         temp[i].value = Results[i+min].rank;
         temp[i].index = i+min;	temp[i].flag = 0;
      }
      qsort(temp, nres, sizeof(struct temp), tempcmp);

      /*----------- Piece together a set of compatible hits, highest
          rank first and compute probability of multiple blocks ----------*/
      hits[ihit].nblkhit = 0;  	/* number of blocks in the map */
      for (i=0; i <nres; i++)
      {
         t = temp[i].index;

         binfo = hits[ihit].binfo; 
         while (binfo != NULL && strcmp(Results[t].ac, binfo->ac) != 0)
                binfo = binfo->next;
         /*  Because of the sort, first matching binfo == anchor */
	 if (binfo != NULL && !binfo->hit_flag &&
             (i==0 || distance_okay(Results[t].offset, hits[ihit].binfo, binfo)) )
	 {
            hits[ihit].nblkhit += 1;
            Results[t].hit_flag = YES;
            Results[t].binfo = binfo;
            binfo->hit_flag = YES;
            binfo->query_res = t;
         }
      }  /* end of temp[i]  */
      free(temp);
   }  /* end of a hit */

   return(nhit);
}  /* end of assemble_hits_rank */
/*=======================================================================
      Find hits among results: anchor block must score above cutoff;
	other blocks must be in order and distanced appropriately
      Compute pvalue elsewhere: old, resort hit results by rank
				mast, just need the cumprobs
========================================================================*/
int assemble_hits(nhit, hits)
int nhit;
struct hit *hits;
{
   struct blocks_info *binfo;
   struct dtemp *dtemp;
   int i, t, ihit, min, nres;

   /*  sort by min cumprob, strand, block AC    */
   qsort(hits, nhit, sizeof(struct hit), hitprob);

   for (ihit = 0; ihit < nhit; ihit++)
   {
      min = hits[ihit].min_res;
      nres = hits[ihit].max_res - min + 1;
      /*------ Sort this group of hits by cumprob now --------------*/
      dtemp = (struct dtemp *) malloc((nres) * sizeof(struct dtemp));
      if (dtemp == NULL) 
      { printf("assemble_hits(): OUT OF MEMORY\n\n"); exit(-1); }

      for (i=0; i<nres; i++)
      {
         dtemp[i].value = Results[i+min].cumprob;
         dtemp[i].index = i+min;
      }
      /*   ascending sort by value == cumprob  */
      qsort(dtemp, nres, sizeof(struct dtemp), dtempcmp);

      /*----------- Piece together a set of compatible hits, highest
          cumprob first and compute probability of multiple blocks ----------*/
      hits[ihit].nblkhit = 0;  	/* number of blocks in the map */
      for (i=0; i <nres; i++)
      {
         t = dtemp[i].index;

         binfo = hits[ihit].binfo; 
         while (binfo != NULL && strcmp(Results[t].ac, binfo->ac) != 0)
                binfo = binfo->next;

         /*  Because of the sort, first matching binfo == anchor */
	 if (binfo != NULL)
         {
            if ( !binfo->hit_flag &&
                (i==0 ||
                 distance_okay(Results[t].offset, hits[ihit].binfo, binfo) ) )
	    {		/* this result is in the hit */
               hits[ihit].nblkhit += 1;
               Results[t].hit_flag = YES;
               Results[t].binfo = binfo;
               binfo->hit_flag = YES;
               binfo->query_res = t;
            }
         }  /* end of if binfo != NULL  */  
      }  /* end of dtemp[i]  */

      /*  All blocks in hit have now been flagged. Flag remaining
		non-overlapping blocks */
      /*  Sets Results[].other_flag == YES if doesn't overlap  */
      check_overlap(nres, dtemp);
      for (i=0; i <nres; i++)
      {
         t = dtemp[i].index;

         if (!Results[t].hit_flag && Results[t].other_flag)
         {
            binfo = hits[ihit].binfo; 
            while (binfo != NULL && strcmp(Results[t].ac, binfo->ac) != 0)
                binfo = binfo->next;

	    if (binfo != NULL)
            {
                if (binfo->hit_flag && binfo->nrep < hits[ihit].maxrep)
               {
                  binfo->nrep += 1;
                  Results[t].repeat_flag = YES;
                  Results[t].other_flag = NO;
                  Results[t].binfo = binfo;
               }
            }
         }
      }  /* end of dtemp[i]  */
      free(dtemp);
   }  /* end of a hit */

   return(nhit);
}  /* end of assemble_hits */

/*===================================================================
	Read through the list of databases looking for ac &
	return a list of blocks & pssms. Assumes each db is
        ordered by family name.
======================================================================*/
struct blocks_list *read_fam(flist, fam)
struct files_list *flist;
char *fam;
{
   struct blocks_list *new_list;
   struct files_list *fcur;
   Block *block;
   int famlen, len, found, done;

   new_list = make_blist();
   strcpy(new_list->fam, fam);

   famlen = strlen(fam);

   found = 0; done = NO;
   fcur = flist->next;
   while (!found && fcur != NULL)
   {
      /*  Check to see whether the previous fam for this file was
	beyond the current fam; assumes all fdats are sorted by fam */
      if (strlen(fcur->prevfam) < famlen) len = strlen(fcur->prevfam);
      else len = famlen;
      if (len < MINAC ||
          strncasecmp(fam, fcur->prevfam, len) <= 0)     /* beyond fam */
      {
         rewind(fcur->fdat);
         fcur->prevfam[0] = '\0';
         fcur->prevdat = ftell(fcur->fdat);
      }
      else
      { fseek(fcur->fdat, fcur->prevdat, SEEK_SET);   }  /* backup */

      if ( (found = read_to_block(fcur->fdat, fam)) )
      {
         strcpy(fcur->prevfam, fam);
         fcur->prevdat = ftell(fcur->fdat);
      }
      else  
      {  
         rewind(fcur->fdat);
         fcur->prevfam[0] = '\0';
         fcur->prevdat = ftell(fcur->fdat);
         fcur = fcur->next;
      }
   }  /* end of while !found */

   if (found)
   {
      while (!done && (block = read_a_block_faster(fcur->fdat)) != NULL)
      {
NRead++;
/*printf("%d %s\n", NRead, block->number); */
         if (strlen(block->number) < famlen) len = strlen(block->number);
         else                                len = famlen;

         if (strncasecmp(block->number, fam, len) == 0)
         {
	   insert_blist(new_list, block);
           fcur->prevdat = ftell(fcur->fdat);
         }
         else 
         { 
            if (strncasecmp(block->number, fam, len) > 0)
            {
               done = YES;
            }
            free_block(block);
         }
      }    /* end of read_a_block(fcur->fdat) */

      return(new_list);
   }   /* end of if found  */

   else { free_blist(new_list); return(NULL);  }

}  /* end of read_fam */

/*=======================================================================
      compute pvalues for hits
========================================================================*/
void pvalues(nhit, hits)
int nhit;
struct hit *hits;
{
   int ihit;

/*
   double dalign;
   if (SeqType == DNA) {  dalign = NAlign / 2.0; }
   else                {  dalign = NAlign; }
*/

   for (ihit=0; ihit< nhit; ihit++)
   {
      if (hits[ihit].nblkhit > 1)
         hits[ihit].rank_pvalue = pvalue_rank(&hits[ihit]);
      else
         hits[ihit].rank_pvalue = 1.0;

      hits[ihit].mast_pvalue = pvalue_mast(&hits[ihit]);

      /*   Assign an evalue to each hit */
      if (Mast)
      {   hits[ihit].evalue = (double) NBlock * hits[ihit].mast_pvalue; }
      else
      {
         hits[ihit].evalue = NAlign * hits[ihit].prob_min;

/*  This isn't really valid ....
         if (hits[ihit].nblkhit == 1)
         {   hits[ihit].evalue = NAlign * hits[ihit].prob_min;  }
         else
         {   hits[ihit].evalue = (double) NBlock * hits[ihit].rank_pvalue; }
*/
      }
   }
}    /* end of pvalues */
/*=======================================================================
      Print hits
========================================================================*/
int print_hits(nhit, hits)
int nhit;
struct hit *hits;
{
   FILE *fstats;
   int i, ihit, qres, goodhit, frame;
   long locfirst, loclast;
   char pline[MAXLINE];
   struct blocks_info *binfo;

   strcpy(pline, "blksort.dat");
   if (Stats) fstats = fopen(pline, "a");
   else       fstats = NULL;

/*
   double dalign;
   if (SeqType == DNA) {  dalign = NAlign / 2.0; }
   else                {  dalign = NAlign; }
*/


   /*   Sort hits by evalue, strand, ac  */
   qsort(hits, nhit, sizeof(struct hit), hiteval);

   if (!GFF)
   {
      /*   List of scores  */
   printf("==============================================================================\n");
      if (Mast)
      {
         printf("                                                             Combined\n");
         printf("Family                                       Strand  Blocks   E-value\n");
      }
      else
      {
         printf("                                                           Rank\n");
         printf("Family                                       Strand  Blocks   P-value\n");
      }
   }

   goodhit = 0;
   for (ihit=0; ihit< nhit; ihit++)
   {
     if (hits[ihit].evalue <= CutoffExp)
     {
         if (!GFF)
         {
            strcpy(pline, hits[ihit].de);
            if (strlen(pline) > 35) pline[35] = '\0';
            else
            {
               for (i=strlen(pline); i<35; i++) pline[i] = ' ';
               pline[35] = '\0';
            }
            printf("%-10s %s  % 2d  %2d of %-2d", 
                hits[ihit].fam, pline, hits[ihit].strand,
                hits[ihit].nblkhit, hits[ihit].nblock);
            if (Mast) { printf(" %8.2g\n", hits[ihit].evalue); }
            else 
            {
               if (hits[ihit].nblkhit > 1)
               {  printf(" %8.2g\n", hits[ihit].rank_pvalue); }
               else { printf("\n"); }
            }
         }
         goodhit++;
         /*   summary statistics */
         if (fstats != NULL)
         {
            fprintf(fstats, "%d %s %s %d %d %g %g %g %g\n",
             goodhit, Query, hits[ihit].fam, 
             hits[ihit].nblock, hits[ihit].nblkhit,
             hits[ihit].prob_min, hits[ihit].rank_pvalue,
             hits[ihit].mast_pvalue, hits[ihit].evalue);
         }
     }  /* end of goodhit */
   } /* end of ihit */
   if (fstats != NULL) fclose(fstats);
   if (Summary) return(goodhit);
 		
   /*    more detailed output */
   printf("\n==============================================================================\n");
   goodhit = 0;
   for (ihit=0; ihit< nhit; ihit++)
   {
     if (hits[ihit].evalue <= CutoffExp)
     {
       if (!GFF)
       {
         printf(">%s %d/%d blocks Combined E-value=%8.2g: %s\n", 
             hits[ihit].fam,
             hits[ihit].nblkhit, hits[ihit].nblock,
             hits[ihit].evalue,
             hits[ihit].de);
         printf("Block    Frame    Location");
         if (SeqType == DNA) printf(" (bp)      Block E-value\n");
         else                printf(" (aa)      Block E-value\n");
      }
      
      /*  Individual blocks in the hit */
      binfo = hits[ihit].binfo;
      while (binfo != NULL)
      {
         if (binfo->hit_flag)
         {
            qres = binfo->query_res;
            locfirst = compute_loc(Results[qres].frame, Results[qres].offset+1);
	    loclast = compute_loc(Results[qres].frame, 
              Results[qres].offset + Results[qres].width);
            if (!GFF)
            {
               printf("%-10s % 1d     %6ld-%-6ld        %8.2g",
                  binfo->ac, Results[qres].frame, 
                  locfirst, loclast,
                  Results[qres].cumprob * NAlign);
               if (binfo->bias) printf("       **biased**\n");
               else             printf("\n");
            }
            else		/* GFF format */
            {
               /* locfirst & loclast are bp locations */
               /* aa locations are Results[qres].offset+1,
                                   Results[qres].offset + Results[qres].width*/
               printf("%s\tBLOCKS\tsimilarity\t%ld\t%ld\t%8.2g ",
		Query, locfirst, loclast,
                Results[qres].cumprob * NAlign);
               frame = Results[qres].frame;
               if (frame < 0) 
               {   
                  frame = 0-frame; 
                  printf("-\t%d\t", frame);
               }
               else
               {  printf("+\t%d\t", frame); }
               printf("%s ", binfo->ac);
               if (binfo->bias) printf(" **biased** ");
               printf("(hit) %s\t\"%d\"\n", hits[ihit].de, goodhit+1);
            }
         }  /*  end of binfo->hit_flag  for a block in the hit */
         binfo = binfo->next;
      }  /* end of binfo for a block in the family */

      /*   Detailed info for repeats */
      if (hits[ihit].maxrep > 0)
      {
         if (!GFF) printf("Up to %d repeats expected:\n", hits[ihit].maxrep);
         for (qres=hits[ihit].min_res; qres <= hits[ihit].max_res; qres++)
         {
            if (Results[qres].repeat_flag)
            {
               locfirst = compute_loc(Results[qres].frame, 
		 Results[qres].offset+1);
	       loclast = compute_loc(Results[qres].frame, 
                 Results[qres].offset + Results[qres].width);
               if (!GFF)
               {
                  printf("%-10s % 1d     %6ld-%-6ld        %8.2g\n",
                     Results[qres].ac, Results[qres].frame, 
                     locfirst, loclast,
                     Results[qres].cumprob * NAlign);
               }
               else		/* GFF format */
               {
                  printf("%s BLOCKS similarity %ld %ld %8.2g ",
		   Query, Results[qres].offset+1,
                   Results[qres].offset + Results[qres].width,
                   Results[qres].cumprob * NAlign);
                  frame = Results[qres].frame;
                  if (frame < 0) 
                  {   
                     frame = 0-frame; 
                     printf("- %d ", frame);
                  }
                  else
                  {  printf("+ %d ", frame); }
                  printf("%s ", Results[qres].ac);
                  if (Results[qres].binfo->bias) printf(" **biased** ");
                  printf("(repeat) %s\n", hits[ihit].de);
               }
            }  /*  end of repeat for family */
         }  /* end of Result[] for family */
      }  /* end of if family has repeats */

      /*   Detailed info for other reported alignments */
      if (!GFF) printf("Other reported alignments:\n");
      for (qres=hits[ihit].min_res; qres <= hits[ihit].max_res; qres++)
      {
/*
            if (!Results[qres].hit_flag && !Results[qres].repeat_flag &&
                Results[qres].cumprob * NAlign <= CutoffRepeat)
*/
            if (Results[qres].other_flag)
            {
               locfirst = compute_loc(Results[qres].frame, 
		 Results[qres].offset+1);
	       loclast = compute_loc(Results[qres].frame, 
                 Results[qres].offset + Results[qres].width);
               if (!GFF)
               {
                  printf("%-10s % 1d     %6ld-%-6ld        %8.2g\n",
                     Results[qres].ac, Results[qres].frame, 
                     locfirst, loclast,
                     Results[qres].cumprob * NAlign);
               }
               else		/* GFF format */
               {
                  printf("%s BLOCKS similarity %ld %ld %8.2g ",
		   Query, Results[qres].offset+1,
                   Results[qres].offset + Results[qres].width,
                   Results[qres].cumprob * NAlign);
                  frame = Results[qres].frame;
                  if (frame < 0) 
                  {   
                     frame = 0-frame; 
                     printf("- %d ", frame);
                  }
                  else
                  {  printf("+ %d ", frame); }
                  printf("%s ", Results[qres].ac);
/*	No binfo for these
                  if (Results[qres].binfo->bias) printf(" **biased** ");
*/
                  printf("(other) %s\n", hits[ihit].de);
               }
            } 
      }  /* end of Result[] for family */

      if (!GFF)
      {
         if (hits[ihit].nblock > 1) map_blocks(&hits[ihit]);
         align_blocks(&hits[ihit]);

         printf("\n------------------------------------------------------------------------------\n");
      }

      goodhit++;
    }  /* end of goodhit */

   } /* end of ihit */

   return(goodhit);
}   /* end of print_hits  */
/*====================================================================
    Map a path of blocks
    The mapping is done in three phases:
    1. The blocks in the database are mapped from the 1st position of
       the first block to the last position of the last block. This
       range divided by MAXMAP (number of columns for the map to occupy
       when printed) determines the scale of the map.
    2. The blocks in the query sequence that are consistent with the
       database are mapped to the same scale as the database blocks.
       The two maps are aligned on the highest scoring block (anchor).
       The query map may occupy more than MAXMAP columns and may have
       to be truncated or compressed. A maximum of MAXLINE characters
       are mapped before compression.
    3. The blocks in the query sequence that are not consistent with the
       database are mapped to the same scale. Since these blocks may
       overlap one another, they may take more than one line. Each line
       is aligned with the left end of the map in 2.
=======================================================================*/
void map_blocks(hit)
struct hit *hit;
{
   int maxdist, i, imin, imax, pos, maxspot, qres, famlen;
   int maxseq, spot, qspot, t, done, qleft, qright;
   double scale;
   struct blocks_info *block, *anchor;
   char dbline[MAXMAPLINE], qline[MAXMAPLINE], pline[MAXMAPLINE];

   famlen = strlen(hit->fam);		/* length of family name */
   maxdist = 0;
   anchor = hit->prob;
   /*--- Determine the scale for the database blocks (# aas per space) ---*/
   block = hit->binfo;
   while (block != NULL)
   {
      /*------ Don't map distance before the first block --------------*/
      if (block != hit->binfo)
         maxdist += block->maxprev;  /* distance from previous block */
      maxdist += block->width;    /* width of this block */
      block = block->next;
   }
   scale = (double) maxdist/MAXMAP;       /* max of 60 spaces per line */
   printf("\n                         |---%5d amino acids---|",
          (int) ((double) 0.5+(scale*25.0)) );

   /*---  1. Now map the database blocks ------------------------------*/
   for (spot=0; spot<MAXMAPLINE; spot++)
   {  dbline[spot] = qline[spot] = ' '; }
   spot=0;
   maxspot = 0;
   block = hit->binfo;
   while (block != NULL)
   {
      if (block != hit->binfo)
      {
         imin = (int) ((double) 0.5+block->minprev/scale);
         imax = (int) ((double) 0.5+(block->maxprev-block->minprev)/scale);
         for (i=spot; i < spot+imin; i++) dbline[i] = ':';
         spot += imin;
         for (i=spot; i < spot+imax; i++) dbline[i] = '.';
         spot += imax;
      }
      imin = (int) ((double) 0.5+block->width/scale);
      if (imin < 1) imin = 1;
      for (i=spot; i < spot+imin; i++) strncpy(dbline+i, block->ac+famlen, 1);
      spot += imin;
      if (block==anchor) maxspot=spot;
      block = block->next;
   }
   dbline[spot] = '\0';
   printf("\n%20s ", hit->fam);
   printf("%s", dbline);

   /*---- 2. Now map the query sequence if there is one ----------------*/
   /*---Line up the highest scoring query block with the database ----*/
   /*---   Determine qspot = print position for beginning of anchor
           block in query . Line it up with maxspot = print position
           for beginning of anchor block in database line.--- */
   spot=qspot=0;
   pos=0;
   block = hit->binfo;
   while (block != NULL)
   {
      if (block->hit_flag)
      {
         qres = block->query_res;
	 imin = (int) ((double) 0.5+(Results[qres].offset-pos)/scale);
         if (imin >= 0)   /* skip over blocks in wrong order */
         {
	    if (spot+imin > MAXMAPLINE && spot+1 < MAXMAPLINE)
               qline[spot++] = '<';
            else
            {
	       for (i=spot; i<spot+imin; i++) qline[i] = ':';
	       spot += imin;
            }
	    imin = (int) ((double) 0.5+block->width/scale);
	    if (imin < 1) imin = 1;
            if (spot+imin < MAXMAPLINE)
            {
	       for (i=spot; i<spot+imin; i++) 
                   strncpy(qline+i, block->ac+famlen, 1);
	       spot += imin;
            }
	    pos = Results[qres].offset + block->width;
	    if (block == anchor) qspot = spot;
         }
      }
      block = block->next;
   }
   qline[spot] = '\0';

   /*------ If the query starts in the after the map, have to
            print some spaces first --------*/
   printf("\n%20s ", Query);
   if (qspot <= maxspot)
   {
       qleft = 0;
       for (i=qspot; i<maxspot; i++) printf(" ");
   }
   else   /*  query starts before the map */
   {
      qleft = qspot - maxspot;
      qline[0] = '<';
   }
   if ((strlen(qline) - qspot) <= (strlen(dbline) - maxspot))
      qright = strlen(qline);
   else
      qright = qspot + strlen(dbline) - maxspot;
   strncpy(pline, qline+qleft, qright-qleft+1); pline[qright-qleft+1] = '\0';
   if (qleft > 0 && (pline[0] == '.' || pline[0] == ':')) pline[0] = '<';
   if (qright != strlen(qline)) pline[strlen(pline)-1] = '>';

   printf("%s", pline);

   /*---------3. Now map the repeats wrt consistent hits ---------------*/
   done = NO; 
   while (!done)
   {
      done=YES; spot = 0;
      pos = (int) qleft * scale;   /* offset of first qline with anchor */
      for (i=0; i<MAXMAPLINE; i++) qline[i] = ' ';
      for (t = hit->min_res; t <= hit->max_res; t++)
         if (!Results[t].map_flag && 
             Results[t].repeat_flag)
         {
            imin = (int) ((double) 0.5+(Results[t].offset-pos)/scale);
            if (imin >=0)
            {
               if (spot+imin < MAXMAPLINE) 
               {
                  spot += imin;
                  imin = (int) ((double) 0.5+Results[t].width/scale);
                  if (imin < 1) imin = 1;
                  if (spot+imin < MAXMAPLINE)
                  {
                     for (i=spot; i<spot+imin; i++)
                        strncpy(qline+i, Results[t].ac+famlen, 1);
                     spot += imin;
                  }
               }
               pos = Results[t].offset + Results[t].width;
               done = NO;
               Results[t].map_flag = YES;		/* mapped */
            }
         }
      qline[spot] = '\0';
      
      if (!done && strlen(qline))
      {
         printf("\n%20s ", Query);
         /*  may have to print some spaces first */
         maxseq = MAXMAP;	/* number of positions left for sequence*/
         if (qspot <= maxspot)
         {
            for (i=qspot; i < maxspot; i++) printf(" ");
            maxseq = MAXMAP - (maxspot - qspot) + 1;
         }
         else if (qline[0] == ' ') qline[0] = '<'; 
         if (strlen(qline) > maxseq) 
         {
            if (qline[maxseq-1] == ' ') qline[maxseq-1] = '>'; 
            qline[maxseq] = '\0';
         }
         printf("%s", qline);
      }
   }

   /*--------4. Now map the inconsistent hom hits wrt consistent ones--*/
   done = NO; 
   while (!done)
   {
      done=YES; spot = 0;
      pos = (int) qleft * scale;   /* offset of first qline with anchor */
      for (i=0; i<MAXMAPLINE; i++) qline[i] = ' ';
      for (t=hit->min_res; t <= hit->max_res; t++)
         if (!Results[t].hit_flag && !Results[t].map_flag &&
              Results[t].other_flag)
         {
            imin = (int) ((double) 0.5+(Results[t].offset-pos)/scale);
            if (imin >=0)
            {
               if (spot+imin < MAXMAPLINE) 
               {
                  spot += imin;
                  imin = (int) ((double) 0.5+Results[t].width/scale);
                  if (imin < 1) imin = 1;
                  if (spot+imin < MAXMAPLINE)
                  {
                     for (i=spot; i<spot+imin; i++)
                        strncpy(qline+i, Results[t].ac+famlen, 1);
                     spot += imin;
                  }
               }
               pos = Results[t].offset + Results[t].width;
               done = NO;
               Results[t].map_flag = YES;
            }
         }
      qline[spot] = '\0';
      if (!done && strlen(qline))
      {
         printf("\n%20s ", Query);
         maxseq = MAXMAP;
         if (qspot <= maxspot)
         {
             for (i=qspot; i < maxspot; i++) printf(" ");
             maxseq = MAXMAP - (maxspot - qspot);
         }
         else if (qline[0] == ' ') qline[0] = '<'; 
         if (strlen(qline) > maxseq) 
         {
            if (qline[maxseq-1] == ' ') qline[maxseq-1] = '>'; 
            qline[maxseq] = '\0';
         }
         printf("%s", qline);
      }
   }

   printf("\n");

}  /* end of map_blocks */

/*========================================================================
	Align the query sequence with the sequence closest to it in
	each block for which it has a hit.
	blkline[] = distances, datline[] = database segment, barline[] = marks,
	homline[] = query segment, repline[] = repeats.
=========================================================================*/
void align_blocks(hit)
struct hit *hit;
{
   struct blocks_info *block;
   int i, t, savspot, repspot, spot, bspot, datmin, datmax, homdist;
   int first, famlen;
   char datline[MAXLINE], homline[MAXLINE], blkline[MAXLINE];
   char barline[MAXLINE], repline[MAXLINE], saveac[MAXAC+1];
   char ctemp[10];

   famlen = strlen(hit->fam);		/*  length of family name */
   for (i=0; i<MAXLINE; i++)
   { datline[i] = homline[i] = blkline[i] = barline[i] = repline[i] = ' ';}
   spot=datmin=datmax=homdist=0;
   strcpy(saveac, "          ");
   block= hit->binfo;
   while (block != NULL)
   {
      if (block->hit_flag)
      {
	 datmin += block->minprev;
	 datmax += block->maxprev;
         homdist = Results[ block->query_res ].offset - homdist;

	 /*--- 28 spaces for name & offset on datline & homline;
	       30 spaces for distance info on blkline ----*/
	 bspot = block->width;
	 if (bspot < 30) bspot = 30;

         /*    Check to see whether this block will fit on the current line */
         /* bspot could be 55, +28 = 83   */
	 if ((spot + bspot + 28) > 83)
         {
	    homline[spot]= datline[spot]= blkline[spot]= barline[spot]= '\0';
            repline[spot] = '\0';
	    printf("\n%s", blkline);
            printf("\n%s", datline); printf("\n%s", barline);
	    printf("\n%s\n", homline);
            for (i=0; i<MAXLINE; i++)
      { datline[i] = homline[i] = blkline[i] = barline[i] = repline[i] = ' '; }
            spot=0;
         }
         else   /*  room for another block on this line, space over */
         {  if (spot > 0) spot += 3;   }
         strncpy(blkline+spot, block->ac, strlen(block->ac));
         strncpy(datline+spot, block->closest_name, strlen(block->closest_name));
         strncpy(homline+spot, Query, strlen(Query));
	 spot+=19;		/* seq name + 1 */

	 blkline[spot] = saveac[famlen];		/* from block A-Z */
	 strncpy(blkline+spot+1, "<->", 3);
	 blkline[spot+4] = block->ac[famlen];		/* to block A-Z  */
	 /* offset starts at 1 in */
         sprintf(ctemp, "%ld", block->closest_offset);
	 strncpy(datline+spot, ctemp, strlen(ctemp));
         sprintf(ctemp, "%ld", Results[ block->query_res ].offset + 1);
	 strncpy(homline+spot, ctemp, strlen(ctemp));
         /* save location in case there are repeats */
         savspot = spot;
	 spot+=8;		/* offset + 1 */

	 bspot = spot;
	 strncpy(blkline+bspot, "(", 1); bspot++;
/*	 kr_itoa(datmin, ctemp, 10); */
         sprintf(ctemp, "%d", datmin);
	 strncpy(blkline+bspot, ctemp, strlen(ctemp));
	 bspot += strlen(ctemp);
	 strncpy(blkline+bspot, ",", 1); bspot++;
/*	 kr_itoa(datmax, ctemp, 10); */
         sprintf(ctemp, "%d", datmax);
	 strncpy(blkline+bspot, ctemp, strlen(ctemp));
	 bspot += strlen(ctemp);
	 strncpy(blkline+bspot, ")", 1); bspot++;
	 strncpy(blkline+bspot, ":", 1); bspot++;
/*	 kr_itoa(homdist, ctemp, 10); */
         sprintf(ctemp, "%d", homdist);
	 strncpy(blkline+bspot, ctemp, strlen(ctemp));
	 bspot += strlen(ctemp);

	 for (i=0; i<block->width; i++)
         {
            strncpy(datline+spot, block->closest_aa+i, 1);
	    strncpy(homline+spot, Results[ block->query_res].aa+i, 1);
            if (strncmp(datline+spot, homline+spot, 1) == 0)
                barline[spot] = '|';
	    spot++;
         }
         /*-------  Block may be narrower than offset heading.. */
         if (spot < bspot) spot = bspot;

         /*   Now look for repeats & print them */
         first = YES;
         for (t=hit->min_res; t<= hit->max_res; t++)
         {
            if (strcmp(block->ac, Results[t].ac) == 0 &&
                (Results[t].repeat_flag || Results[t].other_flag) )
            {
               if (first)  /* have to print out everything else first */
               {
	    homline[spot]= datline[spot]= blkline[spot]= barline[spot]= '\0';
	          printf("\n%s", blkline);
                  printf("\n%s", datline); printf("\n%s", barline);
	          printf("\n%s\n", homline);
                  for (i=0; i<MAXLINE; i++)
            { datline[i] = homline[i] = blkline[i] = barline[i] = ' '; }
                  spot=0;
                  first = NO;
               }  /* end of first */
               for (i=0; i<MAXLINE; i++) repline[i] = ' ';
               repspot = savspot;
/*	       kr_itoa(Results[t].offset+1, ctemp,10);  */
               sprintf(ctemp, "%d", Results[t].offset+1);
	       strncpy(repline+repspot, ctemp, strlen(ctemp));
               repspot += 8;
               strcpy(repline+repspot, Results[t].aa);
               repspot += block->width;
               repline[repspot] = '\0';
               printf("%s\n", repline);
            }
         }     /*  end of for t */
	 datmin = datmax = 0;
	 homdist = Results[ block->query_res ].offset + block->width;
	 strcpy(saveac, block->ac);
      }   /*  end of block is in the hit */
      else
      {		/*  block not in query sequence */
	 datmin += block->minprev + block->width;
	 datmax += block->maxprev + block->width;
      }
      block = block->next;
   }  /*  end of block */

   if (spot > 0)
   {
   homline[spot] = datline[spot] = blkline[spot] = barline[spot] = '\0';
   printf("\n%s", blkline);
   printf("\n%s", datline); printf("\n%s", barline);
   printf("\n%s\n", homline);
   }
}  /* end of align_blocks */
/*=======================================================================
	This is the old pvalue based on ranks; it assumes all blocks
	in the databases searched have been ordered by the query
	sequence. Ignores repeated scores.
>>>>change this so it doesn't re-assemble the hits, just uses the
Results[].hit_flag & ancho info; maybe hit_flag should even be
ordinal with anchor=1, 1st supporting=2, etc.
==========================================================================*/
double pvalue_rank(hit)
struct hit *hit;
{
   struct temp *temp;
   int i, qnblock, mblock, t, min, nres, lastrank, ntemp;
   double n, r, p, prob;

   min = hit->min_res;
   nres = hit->max_res - min + 1;

   /*-------Assuming blimps is run with repeats -------------*/
/*   qnblock *= SeqType*QLen;  */
   qnblock = hit->nblock * QLen;

   /*------ Sort this group of hits by rank --------------*/
   temp = (struct temp *) malloc((hit->nblkhit) * sizeof(struct temp));
   if (temp == NULL)
   {
      printf("\npvalue_rank(): OUT OF MEMORY\n\n"); exit(-1);
   }
   ntemp = 0;
   for (i=0; i<nres; i++)
   {
      if (ntemp < hit->nblkhit && Results[i+min].hit_flag)
      {
         temp[ntemp].value = Results[i+min].rank;
         temp[ntemp].index = i+min;
         ntemp++;
      }
   }
   qsort(temp, hit->nblkhit, sizeof(struct temp), tempcmp);

   /*----------- Piece together a set of compatible hits, highest
       rank first and compute probability of multiple blocks ----------*/
   prob = 1.0;
   mblock = 0;  	/* number of blocks in the map */
   lastrank = 0;	/* rank of previous block in the map */
   for (i=0; i<hit->nblkhit; i++)
   {
      t = temp[i].index;
      /*--- Compute probability -----*/
      if (i>0 && NBlock > 0 && QLen > 0)
      {
         mblock++;     /* number of supporting blocks so far */
         /* compute prob a block of this family could rank this high*/
         /* Call the blocks in the group "red". Compute prob. that
            this red block had this rank using sampling without
            replacement. At this point are assuming:
              Total #blocks = #db blocks-rank of last red block drawn
              Total #reds = #blocks in family - #reds drawn so far
              Total blocks drawn = rank of this red - rank of last red
              Want Prob. one of blocks drawn is red.   */
/*               n = (double) SeqType*QLen*NBlock-lastrank; */
         n = (double) QLen*NBlock-lastrank;
         r = (double) temp[i].value-lastrank;
         p = (double) qnblock-mblock;
        /*      Avoid going beyond max. hypergeo value  */
         if (p * r >= n - p + 1) r = (double) ((int) (n - p + 1) / p);

         prob *= hypergeo(n, r, p, 1.0);

         /* compute prob this block is this far from the anchor block */
         /* QLen may be in protein or dna units, distance is always
            in protein units so multiply by SeqType to convert it to
            correct units. Min dist = -1; Max dist = distance() */
  prob *= (double) SeqType*(distance(hit->rank, Results[t].binfo) + 1) / QLen;
      }

      lastrank = temp[i].value;
   }  /*  end of for i = min to max for hit */
 
   if (prob > 1.0) prob = 1.0;
   if (prob < 0.0) prob = 0.0;

   return(prob);
}  /*  end of pvalue_rank */
/*=======================================================================
	This is the pvalue used by mast, but modified to only count
	the blocks in the hit (ignores other blocks for the family)
==========================================================================*/
double pvalue_mast(hit)
struct hit *hit;
{
   int ires;
   double pvalue, nalign, result;

   pvalue = 1.0;
   for (ires = hit->min_res; ires <= hit->max_res; ires++)
   {
      if (Results[ires].hit_flag) 
      {
         nalign = (double) QLen + (double) Results[ires].width - 1.0;
         /* cumprob is the mast position p-value */
         pvalue *= Results[ires].cumprob;
         /* This is the mast sequence p-value  */
         pvalue *= nalign;
      }
   }
/*>>>>> For large queries, pvalue can be > 1 <<<<<<<<*/
   if (pvalue > 1.0) pvalue = 1.0;
   if (pvalue < 0.0) pvalue = 0.0;

   /*  For true mast, give qfast hit->nblock, not hit->nblkhit; the
	blocks not in the hit are assumed to have p = 1.0   */
   if (hit->nblkhit > 1) {   result = qfast(hit->nblkhit, pvalue);  }
   else                  {   result = pvalue;   }

/*    This is the e-value
   return( (double) NBlock * result );
*/
   /* This is the mast combined p-value */
   return(result);
}  /*  end of pvalue_mast */
/*=======================================================================*/
double get_ptile(score)
int score;
{
   int i;
   double ptile;

   ptile = 0.0;
   if (Ntiles > 0)
   {
      i = score - 1000;
      if (i < 0) i = 0;
      else if (i > Ntiles-1) i = Ntiles-1;
           { ptile = Tiles[i]; }
   }
   return(ptile);
}  /* end of get_ptile */
/*=======================================================================
	Checks Results[ires] vs Results[min] - Results[max]
	dtemp is sorted by cumprob.
========================================================================*/
void check_overlap(nres, dtemp)
int nres;
struct dtemp *dtemp;
{
   int i, j, ires, jres, over;
   long iright, jright, ileft, jleft;

   for (i=0; i < nres; i++)
   {
      ires = dtemp[i].index;
      if (!Results[ires].hit_flag &&
          Results[ires].cumprob * NAlign <= CutoffRepeat)
      {
         over = NO;
         for (j=i+1; j < nres; j++)
         {
            jres = dtemp[j].index;
            if (Results[jres].hit_flag || Results[jres].other_flag)
            {
		ileft = Results[ires].offset;
		jleft = Results[jres].offset;
		iright = Results[ires].offset + Results[ires].width;
		jright = Results[jres].offset + Results[jres].width;
                if (iright > jleft || jright > ileft) over = YES;
            }
         }
         if (!over) Results[ires].other_flag = YES;
      }
   }

}  /* end of check_overlap */
/*===================================================================
    Compute probability from hypergeometric distribution:
    Assume there are n objects, p of one kind and n-p of another.
    Then when r objects are selected at random from among the n
    objects, the prob. that k of the r are of type p is:
  
      ( p )  ( n-p )        p!               (n-p)!
      ( k )  ( r-k )       ----- * -----------------------
                          k!(p-k)!  (r-k)!( (n-p)-(r-k) )!
    -----------------  =  ---------------------------------
         ( n )                       n!
         ( r )                   ----------
                                  r!(n-r)!

       p!         r!          (n-p)!         (n-r)!
  =  -------- * --------  * ----------  * -----------------
     k!(p-k)!    (r-k)!         n!         ( (n-r)-(p-k) )!

  Which can be simplified to a product of these expressions:
   1.  k terms:
       p*(p-1)*(p-2)*...(p-k+1) / k!
   2.  k terms:
       r*(r-1)*(r-2)*...(r-k+1)
   3.  p terms:
       1/(n*(n-1)*(n-2)*...(n-p+1))
   4.  p-k terms:
       (n-r)*(n-r-1)*....(n-r-(p-k)+1)
      
=====================================================================*/
double hypergeo(n, r, p, k)
double n, r, p, k;
{
   double prob, i;

   prob = 1.0;
   if (r > 0.)
   {
   for (i=0.0; i<k; i += 1.0)  prob *= (double) (p-i) * (r-i) / (i+1);
   for (i=0.0; i<(p-k); i += 1.0) prob *= (double) (n-r-i)/(n-i);
   for (i=(p-k); i<p; i += 1.0) prob /= (double) (n-i);
   }
   return(prob);
}  /* end of hypergeo */
/*====================================================================
     Check if this block will fit between existing blocks
       Minimum distance between blocks is assumed to be 0 and
       maximum distance is 2xmaxprev for AA & 4x for DNA
       between the blocks in sequences in the blocks database,
       see prev_dist(); could pass binfo->nseq for more flexibility.
    fblock should be a list of all possible blocks, starting with
	the leftmost; nblock is one of these to be fitted in.
=====================================================================*/
int distance_okay(offset, fblock, nblock)
long offset;
struct blocks_info *fblock, *nblock;
{
   struct blocks_info *block, *lblock, *rblock;
   long mindist, maxdist;

   lblock = rblock = NULL;
   block = fblock;
   while (block != NULL && rblock == NULL)
   {
      if (block->hit_flag)	/* this block already in hit */
      {
	 if (strcmp(block->ac, nblock->ac) < 0)
	    lblock = block;	/* closest block on the left */
	 else if (strcmp(block->ac, nblock->ac) > 0 && rblock == NULL)
	    rblock = block;	/* closest block on the right */
      }
      block = block->next;
   }
   /*---------  Check the distance to the left --------------------*/
   if (lblock != NULL)
   {
      mindist = maxdist = Results[lblock->query_res].offset + (long) lblock->width;
      block = lblock->next;
      while (block != NULL && block != nblock)
      {
	 if (strcmp(block->ac, nblock->ac) < 0)
         {
            mindist += block->width;
	    maxdist += prev_dist(block) + block->width;
         }
	 block = block->next;
      }
      if (mindist-1 > offset) return(NO); /* allow overlap of 1 */
      if (maxdist + prev_dist(nblock) < offset) return(NO);
   }
   /*----------Check the distance to the right ---------------------*/
   if (rblock != NULL)
   {
      mindist = maxdist = offset + (long) nblock->width;
      block = nblock->next;
      while (block != NULL && block != rblock)
      {
	 if (strcmp(block->ac, rblock->ac) < 0)
         {
            mindist += block->width;
	    maxdist += prev_dist(block) + block->width;
         }
	 block = block->next;
      }
      if (mindist-1 > Results[rblock->query_res].offset) return(NO);
      if (maxdist + prev_dist(rblock) < Results[rblock->query_res].offset)
	 return(NO);
   }
   return(YES);
}  /*  end of distance_okay */
/*===================================================================
    Compute maximum distance preceding a block
    Twice as much distance allowed for DNA
>>> Should do something when minprev == maxprev == 0, especially
    if there are only a few sequences (eg BL00904)
=====================================================================*/
long prev_dist(block)
struct blocks_info *block;
{
   long dist;

/*
   dist = (long) block->minprev + block->maxprev;
*/
   if (SeqType == DNA) dist = (long) 4 * block->maxprev;
   else                dist = (long) 2 * block->maxprev;
   if (dist < 1) dist = 1;
   return(dist);
}  /* end of prev_dist */
/*===================================================================
     Compute maximum allowable distance between two blocks
====================================================================*/
int distance(fromblock, toblock)
struct blocks_info *fromblock, *toblock;
{
    long dist, maxdist;
    struct blocks_info *block, *lblock, *rblock;
  
    if (fromblock == NULL || toblock == NULL) return(-1);

    if (strcmp(fromblock->ac, toblock->ac) < 0)
    { lblock=fromblock; rblock=toblock;
      maxdist = QLen-(Results[fromblock->query_res].offset+fromblock->width);
    }
    else
    { lblock=toblock; rblock=fromblock;
      maxdist = Results[fromblock->query_res].offset;
    }
    if (SeqType*maxdist > QLen) maxdist = (int) QLen/SeqType;

    dist = 0;
    block=lblock->next;
    while (block != NULL && block != rblock)
    {
       dist += prev_dist(block) + block->width;
       block = block->next;
    }
    dist += prev_dist(rblock);
    /*  Don't want possible distance to be larger than possible
        sequence positions! */
    if (dist > maxdist) dist = maxdist;
    return(dist);
}  /* end of distance */
/*======================================================================
   Find the sequence closest to the last sequence in the block
      based on the number of identities
   block is blist->block; query is results[ blist->binfo->query_res ]
   compare aa_btoa[ blist->block->sequences[s].sequence[i] ]
   with    results[ blist->binfo->query_res ].aa[i], i = 1,blist->block->width
======================================================================*/
void closest_seq(blist)
struct blocks_list *blist;
{
   Block *block;
   int npair, s1, i;
   int aaint, qres, maxscore, maxs1;
   struct pair *pairs;
/*SGI   delete above declaration of pairs and use fixed below */
/*SGI   struct pair pairs[MAXSEQS*(MAXSEQS-1)/2];  */

   block = blist->block;
   qres = blist->binfo->query_res;

   npair = block->num_sequences;
/*SGI  delete malloc() and free(pairs) and used fixed allocation */
   pairs = (struct pair *) malloc(npair * sizeof(struct pair));
   if (pairs == NULL)
   {
      printf("\nclosest_seq: Unable to allocate pair structure!\n");
      exit(-1);
   }

   /*    Compute scores for all possible pairs of sequences            */
   for (s1=0; s1<block->num_sequences; s1++) 
   {
      pairs[s1].score = 0;
      for (i=0; i< block->width; i++)
      {
         aaint = aa_atob[ Results[qres].aa[i] ];
	 /* s1 is the block seq & is all caps, but s2 is the query
	    seq & it may have lower case chars in it */
	 if (aaint == block->sequences[s1].sequence[i])
		   pairs[s1].score += 1;
      }
   }  /* end of s1 */
   maxs1 = -1; maxscore = -1;
   for (s1=0; s1 < block->num_sequences; s1++)
      if (pairs[s1].score > maxscore)
      {
	 maxscore = pairs[s1].score;
	 maxs1 = s1;
      }

/*SGI   delete free(pairs) and use fixed allocation  */
      free(pairs);

      /*    Store the closest aa now; fix consensus here?  */
      strcpy(blist->binfo->closest_name, block->sequences[maxs1].name);
      blist->binfo->closest_offset = block->sequences[maxs1].position;
      for (i=0; i < block->width; i++)
	blist->binfo->closest_aa[i] = aa_btoa[ block->sequences[maxs1].sequence[i] ];
      blist->binfo->closest_aa[block->width] = '\0';
}  /*  end of closest_seq */
/*======================================================================
    If the search was of a matrix database, the sequence segment will be
    all lower case. Change any residue that matches any sequence in the
    block to upper case. The block segments are all upper case.
========================================================================*/
void consensus(res, block)
int res;
Block *block;
{
   int s1, i, aaint;
   char aatemp[2];

   /*  res is the query sequence  */
   for (s1=0; s1 < block->num_sequences; s1++)
      for (i=0; i < block->width; i++)
      {
         aaint = aa_atob[ Results[res].aa[i]  ];
         if (aaint == block->sequences[s1].sequence[i] )
         {
            strncpy(aatemp, Results[res].aa+i, 1);
            if (aatemp[0] >= 97 && aatemp[0] <= 122)
            {
               aatemp[0] -= 32;
               strncpy(Results[res].aa+i, aatemp, 1);
            }
         }
      }

}  /* end of consensus */
/*======================================================================*/
/*  Sort by strand (sign of frame) and block name */
int strandcmp(t1, t2)
struct hom *t1, *t2;
{
   int strand1, strand2;

   strand1=strand2=1;
   if (t1->frame < 0) strand1=-1; if (t2->frame < 0) strand2=-1;
   if (strand1 != strand2)  return(strand1 - strand2);
   else return (strcmp(t1->ac, t2->ac));
}  /* end of strandcmp */
/*======================================================================*/
/*  Sort by frame and block name */
int framecmp(t1, t2)
struct hom *t1, *t2;
{
   if (t1->frame != t2->frame)  return(t1->frame - t2->frame);
   else return (strcmp(t1->ac, t2->ac));
}  /* end of framecmp */
/*======================================================================*/
/*   Sort by min_rank, strand (sign of frame), block name & rank */
int mincmp(t1, t2)
struct hom *t1, *t2;
{
   int strand1, strand2;

   strand1=strand2=1;
   if (t1->frame < 0) strand1=-1; if (t2->frame < 0) strand2=-1;
   if (t1->min_rank != t2->min_rank) return(t1->min_rank - t2->min_rank);
   else if (strand1 != strand2) return(strand1 - strand2);
   else if (strcmp(t1->ac, t2->ac) != 0) return(strcmp(t1->ac, t2->ac));
   else return(t1->rank - t2->rank);
}  /* end of mincmp */
/*========================================================================*/
/*  Sort temp structure by value field */
int tempcmp(t1, t2)
struct temp *t1, *t2;
{
   return(t1->value - t2->value);
}
/*========================================================================*/
/*  Sort dtemp structure by value field, with index as tie breaker */
int dtempcmp(t1, t2)
struct dtemp *t1, *t2;
{
   double diff;

   diff = t1->value - t2->value;
   if (diff > 0.0 )     return(1);
   else if (diff < 0.0) return(-1);

   diff = t1->index - t2->index;
   if (diff > 0.0 )     return(1);
   else if (diff < 0.0) return(-1);
   else                 return(0);
}
/*========================================================================*/
/*  Sort hits by family */
int hitfam(t1, t2)
struct hit *t1, *t2;
{
   return (strcmp(t1->fam, t2->fam));
}  /* end of hitfam */
/*======================================================================*/
/*  Sort by rank_min, strand (sign of frame) and block name */
int hitrank(t1, t2)
struct hit *t1, *t2;
{
   if (t1->rank_min != t2->rank_min) return(t1->rank_min - t2->rank_min);
   else if (t1->strand != t2->strand)  return(t1->strand - t2->strand);
   else return (strcmp(t1->fam, t2->fam));
}  /* end of hitrank */
/*========================================================================*/
/*  Sort hit structure by min cumprob. strand & ac */
int hitprob(t1, t2)
struct hit *t1, *t2;
{
   double diff;

   /*   Ascending min cumprob; smaller first   */
   diff = t1->prob_min - t2->prob_min;
   if (diff < 0.0 )      {   return(-1);  }
   else
   {    
     if (diff > 0.0)  {   return(1);   }
     else 
     {
        if (t1->strand != t2->strand) {return(t1->strand - t2->strand);}
        else {return (strcmp(t1->fam, t2->fam));}
     }
   }
}  /*  end of hitprob */
/*========================================================================*/
/*  Sort hit structure by evalue, strand & ac */
int hiteval(t1, t2)
struct hit *t1, *t2;
{
   double diff;

   /*   Ascending evalue; smaller first   */
   diff = t1->evalue - t2->evalue;
   if (diff < 0.0 )      {   return(-1);  }
   else
   {    
     if (diff > 0.0)  {   return(1);   }
     else 
     {
        if (t1->strand != t2->strand) {return(t1->strand - t2->strand);}
        else {return (strcmp(t1->fam, t2->fam));}
     }
   }
}  /*  end of hiteval */
/*========================================================================*/
/*  Sort hit structure by rank pvalue, min cumprob. strand & ac */
int hitprank(t1, t2)
struct hit *t1, *t2;
{
   double diff;

   diff = t1->rank_pvalue - t2->rank_pvalue;
   if (diff < 0.0 )      {   return(-1);  }
   else
   {    
     if (diff > 0.0)  {   return(1);   }
     else 
     {

        diff = t1->prob_min - t2->prob_min;
        if (diff < 0.0 )      {   return(-1);  }
        else
        {    
          if (diff > 0.0)  {   return(1);   }
          else 
          {
             if (t1->strand != t2->strand) {return(t1->strand - t2->strand);}
             else {return (strcmp(t1->fam, t2->fam));}
          }
        }
     }
   }
}  /*  end of hitprank */
/*========================================================================*/
/*  Sort hit structure by mast pvalue. strand & ac */
int hitmpv(t1, t2)
struct hit *t1, *t2;
{
   double diff;

   /*   Ascending; smaller first   */
   diff = t1->mast_pvalue - t2->mast_pvalue;
   if (diff < 0.0 )      {   return(-1);  }
   else
   {    
     if (diff > 0.0)  {   return(1);   }
     else 
     {
        if (t1->strand != t2->strand) {return(t1->strand - t2->strand);}
        else {return (strcmp(t1->fam, t2->fam));}
     }
   }
}  /*  end of hitmpv */
/*========================================================================*/
/*  Sort hit structure by rank pvalue, rank ptile (D) & min rank */
int hitcmp(t1, t2)
struct hit *t1, *t2;
{
   double diff;

   /*   Ascending pvalue; smaller first   */
   diff = t1->rank_pvalue - t2->rank_pvalue;
   if (diff < 0.0 ) {   return(-1);  }
   else
   {    
        if (diff > 0.0)  {   return(1);   }
        else 
        {
           /* Descending ptile; larger first */
           diff = t1->rank_ptile - t2->rank_ptile;
           if (diff < 0.0) {   return(1);   }
           else
           {
                if (diff > 0.0) {   return(-1);   }
                 /* Ascending rank; smaller first */
                else             {   return(t1->rank_min - t2->rank_min);   }
           }
        }
   }
} /*  end of hitcmp */
/*=========================================================================
       frame  1:1 => bp 1,     2:1 => 2,         3:1 => 3
           bp = frame + 3 * offset
       frame -1:1 => bp QLen, -2:1 => QLen - 1, -3:l => QLen - 2 
           bp = frame + (QLen + 1) - 3 * offset
=========================================================================*/
long compute_loc(frame, offset)
int frame;
int offset;
{
   long loc;

   loc = (offset - 1) * SeqType;
   if (frame < 0) loc = (QLen + 1) - loc;
   loc += frame;
   if (frame == 0) loc++;	/* Blimps frame = 0 for protein */
   return(loc);
}   /* end of compute_loc */
/*==========================================================================
      First entry in the list is not used
============================================================================*/
struct files_list *make_flist()
{
   struct files_list *new;

   new = (struct files_list *) malloc(sizeof(struct files_list));
   new->fdat = NULL;
   new->datname[0] = '\0';
   new->prevfam[0] = '\0';
   new->prevdat = (long) 0;
   return(new);
}  /*   end of make_flist()   */
/*==========================================================================*/
void insert_flist(flist, new)
struct files_list *flist, *new;
{
   struct files_list *fcur;

   fcur = flist;
   while (fcur->next != NULL)
      fcur = fcur->next;
   fcur->next = new;
}   /* end of insert_flist  */
/*=======================================================================
	Compute cumulative prob dist for a matrix = cumprob[x] =
		prob(score >= x)
	Return cumprob[score]
        Assumes global array frequency[]
========================================================================*/
int cumprob(matrix)
Matrix *matrix;
{
  struct score_struct scores[2][MAXSCORE], *last, *this;
  struct score_struct ends[MAXSCORE], middle[MAXSCORE];
  int col, aa, minvalue, maxvalue, minscore, maxscore, mincol, maxcol;
  int x, minfirst, minlast, itemp;
  double cum, dtemp, probwt[MAXCOL];

  minscore = maxscore = 0;
  maxvalue = -1;		/* assumes no negative scores */
  minvalue = 9999;
  probwt[0] = 20.0;

  /*  This method only works for integer weights; round as papssm does */
  for (col = 0; col < matrix->width; col++)
     for (aa=1; aa <= 20; aa++)
     {
         itemp = round(matrix->weights[aa][col]);
         matrix->weights[aa][col] = (double) itemp;
     }

  for (col = 0; col < matrix->width; col++)
  {
     /*  Intialize probability weights with powers of 20 */
     if (col > 0) probwt[col] = probwt[col - 1] * 20.0;
     mincol = 9999; maxcol = -1;
     for (aa = 1; aa <= 20; aa++)
     {
        if (matrix->weights[aa][col] > maxvalue)
            maxvalue = matrix->weights[aa][col];
	if (matrix->weights[aa][col] < minvalue)
	    minvalue = matrix->weights[aa][col];
        if (matrix->weights[aa][col] > maxcol)
            maxcol = matrix->weights[aa][col];
	if (matrix->weights[aa][col] < mincol)
	    mincol = matrix->weights[aa][col];
     }
     maxscore += maxcol;
     minscore += mincol;
     if (col == 0) minfirst = mincol;
     if (col == (matrix->width - 1) ) minlast = mincol;
   }
   /*    Probability weights: weights applied to probs depending
         on how many columns of the block are aligned, the
         full alignment width gets about 19/21 = .905, alignments
         off either end of the sequence get the remainder */
   dtemp = probwt[matrix->width - 1] * 21.0 - 40.0;
   cum = 0.0;
   for (col = 0; col < matrix->width; col++)
   {
      probwt[col] = probwt[col] * 19.0 / dtemp;
      cum += probwt[col];
      if (col < matrix->width - 1) cum += probwt[col];
   }
   /* ---- Min. score could be just first or last column ----*/
   if (minfirst < minscore) minscore = minfirst;
   if (minlast < minscore)  minscore = minlast;

/*
   printf("%s: minscore=%d maxscore=%d\n",
	   matrix->number, minscore, maxscore);
*/

   if (maxscore > MAXSCORE) 
   { 
      printf("maxscore is too big, increase MAXSCORE from %d\n", maxscore);
      maxscore = MAXSCORE - 1;
   }
   /*-----------------------------------------------------------------*/
   last = scores[0]; this = scores[1];
   for (x = minvalue; x <= maxscore; x++)
   {  last[x].ways = last[x].prob = this[x].ways = this[x].prob = 0.0; 
      ends[x].ways = ends[x].prob = middle[x].ways = middle[x].prob = 0.0; }

   /*---------Initialize from first column -------------------------------*/
   col = 0;
   for (aa=1; aa <= 20; aa++)
   {
      x = matrix->weights[aa][col];
      last[x].ways += 1.0;
      last[x].prob += frequency[aa];   
   }

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

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

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

   /*--------CumProb[x] has sum from x to maxscore of prob[x]; this
	is the prob(score >= x) -------------------------------------*/
   cum = 0.0;
   for (x = maxscore; x >= minvalue; x--)
   {
      if (middle[x].ways > 0.0 || ends[x].ways > 0.0)
      {
         cum += (middle[x].prob + ends[x].prob);
      }
      CumProb[x] = cum;
   }
   return(maxscore);

}  /* end of cumprob */
/*=======================================================================
     Routines for a list of blocks
	First item in list contains first block
========================================================================*/
struct blocks_list *make_blist()
{
   struct blocks_list *new;
   
   new = (struct blocks_list *) malloc (sizeof(struct blocks_list));
   new->fam[0] = '\0';
   new->block = NULL;
   new->pssm = NULL;
   new->binfo = 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;

   /*--- Insert a new record for the current block at the end of the list ---*/
   /*    or in the first record if it's empty  */
   cur = blist;
   while (cur->next != NULL) cur = cur->next;

   if (cur->block != NULL)
   { cur->next = make_blist(); cur = cur->next; }

   strcpy(cur->fam, block->family);
   cur->block = block;
   cur->pssm = block_to_matrix(block, 3);

}  /* 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->block != NULL) free_block(cur->block);
      if (cur->pssm != NULL) free_matrix(cur->pssm);
/*	don't do this; binfos are still in use!
      if (cur->binfo != NULL) free_binfo(cur->binfo);
*/
      free(cur);
      last->next = NULL;
      free_blist(last);
   }
   else free(blist);

}  /* end of free_blist */
/*=======================================================================
     Routines for a list of blocks
	First item in list contains first block
========================================================================*/
struct blocks_info *make_binfo(block)
Block *block;
{
   struct blocks_info *new;
   int i;
   
   new = (struct blocks_info *) malloc (sizeof(struct blocks_info));
   new->ac[0] = '\0';
   new->nseq = new->width = new->strength = new->minprev = new->maxprev = 0;
   new->bias = NO;
   new->query_res = -1;
   new->hit_flag = NO;
   new->nrep = 0;
   new->closest_offset = (long) 0;
   new->closest_name[0] = '\0';
   new->closest_aa[0] = '\0';
   new->next = NULL;

   if (block != NULL)
   {
      strcpy(new->ac, block->number);
      new->nseq = block->num_sequences;
      new->width = block->width;
      new->strength = block->strength;
      new->minprev = block->min_prev;
      new->maxprev = block->max_prev;
      /*------------------ Now check for bias --------------------*/
      i = 0;
      while (i < NBias)
      {
         if (strcmp(Bias[i].ac, new->ac) == 0)
         {   new->bias = YES; i = NBias;   }
         else {   i++;  }
      }
   }

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

void insert_binfo(binfo, list)
struct blocks_info *binfo, *list;
{
   struct blocks_info *cur;

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

}  /* end of insert_binfo */

void free_binfo(binfo)
struct blocks_info *binfo;
{
   struct blocks_info *cur, *last;

   cur = last = binfo;
   while (cur->next != NULL)
   {
      last = cur;  cur = cur->next;
   }
   if (cur != binfo)
   {
      free(cur);
      last->next = NULL;
      free_binfo(last);
   }
   else free(binfo);

}  /* end of free_binfo */
/*=======================================================================
   From: Timothy L. Bailey & Michael Gribskov, "Combining evidence
	 using p-values:...", CABIOS (1998) 14.1:48-54, Fig. 3
========================================================================*/
double qfast(n, p)
int n;
double p;
{
   int i;
   double x, t, q;

   if (p <= 0.0) return(0.0);
   if (n <= 1) return(p);

   x = -log(p);
   t = q = p;
   for (i=1; i<n; i++)
   {
      t *= ( x/(double) i );
      q += t;
   }

   return(q);

}  /* end of qfast  */
