/* >>> struct best_block has fixed MAXSEQS sequences, but this is now 5433!!!
 <<<  */
/*=======================================================================
  (C) Copyright 1992-1998, Fred Hutchinson Cancer Research Center
	blksort.c  USE:  blksort #hits search_output blocks_db [-stats]
		   OR:   blksort blimps.cs
	    eg:  blksort #hits sample.hom none -stats
       There are three arguments:
          1. Number of hits to report
	  2. Name of output file from a PATMAT 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. blksort.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

 12/ 4/98 1. Removed PRINTS_FLAG; look for blksort.stp in current directory
	     first, then in 1st DB directory
 12/15/98 1. Fixed bug when additional hits with same last score
          2. Fixed biased block bug for single blocks (eg PD02218)
  5/14/99 1. Revised blimps 3.2.6 output format
  6/12/99 1. Sequence names up to 18 characters long.
  7/ 4/99 1. Recognize Blimps 3.3
 12/17/99 1. Longer AC
  6/10/00 1. Fixed problems with longer AC
  3/11/01 1. Check for MAXSEQS in read_block()
  8/21/02 1. Use sprintf() instead of kr_itoa()
 12/23/06.1. Increased seq name len
==========================================================================*/

#include "motifj.h"

#define NSCORE 5000		/* Maximum # of scores in .hom file */
#define MAXHIT 10		/* Default # hits to report */
#define SEEK_SET 0		/* needed for UNIX fseek routine */
#define MINPAT 1000		/* Minimum patmat score for a hit */
#define MAXMAP 60		/* Maximum map width in columns */
#define MAXTILES 620		/* Max. # of lines in blksort.stn */
#define MAXGROUP 1000		/* Max. # lines in repeats.dat file */
#define MAXBIAS 1000		/* Max. # lines in blksort.bias file */
#define MAX_WIDTH 60		/* Max. width of block */
#define MAX_REPEAT 400		/* Max. #aas between repeats */
#define PROTEIN 1
#define DNA 3

struct hom {			/* search results structure */
   char ac[MAXAC+1];		/* block name, eg. PS00094A */
   char fam[MAXAC];		/* family part of ac */
   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;			/* patmat score */
   long offset;			/* offset of alignment */
   int width;			/* width of block */
   char title[25];		/* block description */
   char aa[MAX_WIDTH];	   	/* alignment to block */
   int map_flag;		/* used by map_blocks() */
				/* 0=>not hit, 1=>hit, 2=>repeat*/
				/* 3=>repeat/mapped, 4=>not hit/mapped */
};

struct best_block {			/* block structure */
   char ac[MAXAC+1];         		/* accession number */
   char fam[MAXAC];			/* family part of ac */
   int nseq, width, strength;		/* #seqs, width, strength */
   int minprev, maxprev;		/* distances from prev block */
   char name[MAXSEQS][SNAMELEN+1];	/* name of seq */
   long offset[MAXSEQS];		/* offset of seq */
   char aa[MAXSEQS][MAX_WIDTH];		/* aas for seq */
   int cluster[MAXSEQS];		/* cluster # for seq */
   int ncluster[MAXSEQS];		/* #seqs in same cluster */
   long dat_pos;			/* position of block in blocks.dat*/
   int query;				/* score if last seq is a query */
   int rank;				/* patmat rank if last seq is query*/
   struct best_block *next_block;	/* next block in path */
};

struct sorttemp {			/* sorting structure */
   char ac[MAXAC+1];
   int rank, index, map_flag, path, tpath;
};

struct minmax {				/* to check repeat overlaps */
   long min;
};

struct file_list {
   FILE *fdat;
   char datname[FNAMELEN];
   long prevAC;
   struct file_list *next_flist;
};

/*---- Functions in blksort.c -----*/
int read_config();
void print_blurb();
FILE *get_info();
void open_dbs();
void close_dbs();
int read_hom();
int print_results();
int check_dat();
struct best_block *read_block();
void fill_block();
void add_query();
int check_repeats();
double hypergeo();
int distance_okay();
long prev_dist();
int distance();
void map_blocks();
void align_blocks();
int closest_seq();
void consensus();
int strandcmp();			/* sorting routine */
int framecmp();				/* sorting routine */
int mincmp();				/* sorting routine */
int tempcmp();
int stempcmp();
int stempcmp2();
int read_tiles();			/* read percentiles file */
int read_repeats();			/* read repeats.dat */
int read_bias();			/* read blksort.bias */
int compute_loc();
struct file_list *make_flist();
void insert_flist();

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

/*------------Global variables -------------*/
char Version[12] = "12/23/06.1";/* Version number */
int BlimpsVer;			/* Blimps version */
char Query[SNAMELEN+1];		/* Query sequence name */
unsigned long QLen;		/* Query length */
int NBlock;			/* # blocks searched */
int SeqType;			/* =1 if amino acid, =3 if nucleotide */
int MaxHit;			/* # hits to report */
int Ntiles;			/* # of percentiles read in */
float Tiles[MAXTILES];		/* %-tiles values for scores 1000-1620 */
int Stats;			/* statistics flag */
char HomName[FNAMELEN];		/* Search file name for stats file */
char Qfilename[FNAMELEN];	/* Query 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 */

/*======================================================================*/
int main(argc, argv)
int argc;
char *argv[];
{
   FILE *fhom, *fcf;
   char homfile[FNAMELEN], datfile[FNAMELEN], ctemp[FNAMELEN];
   char db[FNAMELEN];
   int i, nscores, ndb;
   struct split_name *datsplit;
   struct file_list *flist, *newdat;

   printf("\nBLKSORT Version %s\n", Version);
   if (argc < 2)
   {
      printf("COPYRIGHT 1992-8 Fred Hutchinson Cancer Research Center\n");
      printf("USAGE: blksort <nhits> <blimps_file> <blocks_file> [-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("    -stats          produces blksort.dat statistics file\n");
      printf("OR:    blksort <blimps.cs>\n");
      printf("    <blimps.cs> = name of blimps configuration file\n");
   }
   Stats = 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("\nBLKSORT: Cannot open configuration file %s\n", ctemp);
            exit(-1);
         }
         else
	 { 
            ndb = read_config(fcf, homfile, flist);
            if (ndb > 0) strcpy(datfile, flist->next_flist->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++;
         }

         /*------------- Option arg 4: collect stats ---------------------*/
         if (argc > 4 && strcmp(argv[4], "-stats") == 0) Stats = YES;
    }  /* end of non-config file input */

   /*  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 --------------------------*/
   fhom = get_info(homfile);

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

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

/*---------------------------------------------------------------------*/
   print_blurb();
   printf("\nQuery=%s, \n Size=%ld ", Qfilename, QLen);
   if (SeqType == PROTEIN) printf("Amino Acids");
   else if (SeqType == DNA) printf("Base Pairs");
   printf("\nBlocks Searched=%d\n", NBlock); 

/*---------------------------------------------------------------------*/
   Ntiles = read_tiles();
   NRepeat = read_repeats();
   NBias = read_bias();
   nscores = read_hom(fhom, flist);
   printf("\n%d possible hits reported\n", nscores);
   fclose(fhom);
   close_dbs(flist);
   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 file_list *flist;
{
   char line[MAXLINE], keyword[20], *ptr;
   int ndb;
   struct file_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()
{
   FILE *fstp;
   char fname[FNAMELEN], line[MAXLINE];

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

   if (fstp == NULL)
   {
      printf("\n========================================");
      printf("=======================================");
      printf("\nHere are your search results from the BLOCKS searcher.");
      printf("\nPlease report problems to webmaster@blocks.fhcrc.org");
      printf(", include your query\nand this output.");
      printf(" To obtain help, send the word HELP on a single line to");
      printf("\nblocks@blocks.fhcrc.org");
      printf("\n========================================");
      printf("=======================================");
      printf("\nCopyright (c) 1992-6 by the Fred Hutchinson Cancer");
      printf(" Research Center");
      printf("\nIf you use BLOCKS in your research, please cite:");
      printf("\nSteven Henikoff and Jorja G. Henikoff,");
      printf(" Protein Family Classification Based");
      printf("\non Searching a Database of Blocks, Genomics 19:97-107 (1994).");
      printf("\n========================================");
      printf("=======================================");
      printf("\nEach numbered result consists of one");
      printf(" or more blocks from a PROSITE or PRINTS");
      printf("\ngroup found in the query sequence. One set");
      printf(" of the highest-scoring blocks that");
      printf("\nare in the correct order and separated");
      printf(" by distances comparable to the BLOCKS");
      printf("\ndatabase is selected for analysis.");
      printf(" If this set includes multiple blocks");
      printf("\nthe probability that the lower scoring");
      printf(" blocks support the highest scoring");
      printf("\nblock is reported. Maps of the database");
      printf(" blocks and query sequence are shown:");
      printf("\n  < indicates the sequence has been");
      printf(" truncated to fit the page");
      printf("\n  : indicates the minimum distance");
      printf(" between blocks in the database");
      printf("\n  . indicates the maximum distance");
      printf(" between blocks in the database");
      printf("\nThe maps are aligned on the highest");
      printf(" scoring block. The alignment of the");
      printf("\nquery sequence with the sequence");
      printf(" closest to it in the BLOCKS database");
      printf("\nis shown. Upper case in the query");
      printf(" sequence indicates at least one");
      printf("\noccurrence of the residue in that");
      printf(" column of the block.");
      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
=========================================================================*/
FILE *get_info(homfile)
char homfile[FNAMELEN];
{
   FILE *fhom;
   char line[MAXLINE], db[FNAMELEN];
   char *ptr, *ptr1;
   int i, j, done;
   struct split_name *homsplit;

   if ( (fhom=fopen(homfile, "r")) == NULL)
   {
      printf("\nBLKSORT: Cannot open file blimps file %s\n", homfile);
      exit(-1);
   }
   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';
   BlimpsVer = 326;				/* Blimps version */
   NBlock = 0; QLen = (unsigned long) 0;
   SeqType = PROTEIN;		/* amino acid is default type */
   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 = 324;
      if (strstr(line, "Version 3.2.3")) BlimpsVer = 323;
      if (strlen(line) > 10 && 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';
         if (Qfilename[0] == '>') i = 1; else i = 0;
         j=0;
         while(i<strlen(Qfilename) && j <= SNAMELEN)
         {
            if (j < SNAMELEN && Qfilename[i] != '\t' && Qfilename[i] != '\r' &&
                Qfilename[i] != '\n' && Qfilename[i] != ' ')
                    Query[j] = Qfilename[i];
            else
            {
               Query[j] = '\0'; j = SNAMELEN+1;
            }
            i++; j++;
         }

         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) > 10 && 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) > 10 && 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) > 10 && 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);
            }
         }
         done=YES;
      }
      else if (strncmp(line, "AC#", 3) == 0) done=YES;
   }

   return(fhom);

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

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

   fcur = flist->next_flist;
   while (fcur != NULL)
   {
      if (fcur->fdat != NULL) fclose(fcur->fdat);
      fcur = fcur->next_flist;
   }
}   /* 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 < 1) Repeats[nrep].num = 1;
            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 */
/*=======================================================================*/
int read_hom(fhom, flist)
FILE *fhom;
struct file_list *flist;
{
   int i, j, rank, save_rank, min_rank, nhit, save_strand, strand;
   int ac_pos, title_pos, str_pos, score_pos, frame_pos, off_pos, aa_pos;
   int title_len, done;
   char line[MAXLINE], ctemp[8], save_ac[MAXAC+1], save_fam[MAXAC+1];
   struct hom *results;

/*------------- Load the search results into memory --------------------*/
   results = (struct hom *) malloc(NSCORE * sizeof(struct hom));
   if (results == NULL)
   {  printf("\nOUT OF MEMORY\n\n"); exit(-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;
   }
   rank = 0;
   while (!feof(fhom) && fgets(line,MAXLINE,fhom) != NULL)
   {
      if (strlen(line) > aa_pos && rank < NSCORE)
      {
	 save_ac[0]= '\0';
	 strncpy(save_ac, &line[ac_pos], MAXAC); save_ac[MAXAC] = '\0';
         for (i=0; i<MAXAC; i++)
            if (save_ac[i] == ' ') save_ac[i] = '\0';
	 strcpy(results[rank].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[rank].fam, save_ac);
	 results[rank].rank = results[rank].min_rank = rank + 1;
	 results[rank].title[0] = '\0';
	 strncpy(results[rank].title, &line[title_pos], title_len);
	 results[rank].title[title_len - 1] = '\0';
         strncpy(ctemp, &line[str_pos], 6); ctemp[6] = '\0';
         results[rank].strength = atoi(ctemp);
	 strncpy(ctemp, &line[score_pos], 6); ctemp[6] = '\0';
	 results[rank].score = atoi(ctemp);
	 strncpy(ctemp, &line[frame_pos], 2); ctemp[2] = '\0';
	 results[rank].frame = atoi(ctemp);
	 strncpy(ctemp, &line[off_pos], 7); ctemp[7] = '\0';
	 results[rank].offset = atol(ctemp);
	 results[rank].width = 0; i=aa_pos;
	 while (line[i++] != '\n') results[rank].width += 1;
	 if (results[rank].width > MAX_WIDTH)
		results[rank].width=MAX_WIDTH;
	 strncpy(results[rank].aa, &line[aa_pos], results[rank].width);
	 results[rank].aa[results[rank].width] = '\0';
         results[rank].map_flag = NO;
	 rank++;
      }
   }
/*   printf("\n%d results read", rank);
*/
   if (rank < 1) return(0);

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

/*--------Group the results & determine minimum rank -----------*/
   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<rank; 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;
      }
      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;

/*----------Resort by min rank, strand & block name -------------------*/
   qsort(results, rank, sizeof(struct hom), mincmp);

/*------------Present the results-----------------------------------*/
   nhit = print_results(flist, rank, results);
   return(nhit);

}  /* end of read_hom */
/*=======================================================================
      Print results
========================================================================*/
int print_results(flist, nresult, results)
struct file_list *flist;
int nresult;
struct hom *results;
{
   FILE *fstat, *frep;
   struct file_list *fcur;
   char save_fam[MAXAC+1], ctemp[MAXAC+1];
   int i, j, save_nresult, nprint, nhit, last_hit;
   int strand, save_strand, last_score, max_score;
   long locfirst, loclast;
   int good_score, found;

   if (Stats) fstat = fopen("blksort.dat", "a");
   else       fstat = NULL;
   if (Stats) frep = fopen("blksort.rep", "a");
   else       frep = NULL;

   strcpy(save_fam, results[0].fam); save_nresult = 0;
   if (results[0].frame < 0) save_strand=-1; else save_strand=1;
   good_score = NO;
   nhit = nprint = last_hit = 0;
   for (i=1; i<=nresult; i++)
   {
      if (results[i].frame < 0) strand=-1; else strand=1;
      if (i==nresult ||
	  (i<nresult && (save_strand != strand ||
           strcmp(save_fam, results[i].fam) != 0)) )
      {
	for (j=save_nresult; j<i; j++)  /* There are i-save_nresult blocks */
	 if (results[j].rank == results[j].min_rank &&
             results[j].score > MINPAT)
         {
            good_score=YES; max_score = results[j].score;
         }
         /*    Print previous group; can print more than MaxHit results
               if there are more than MaxHit hits with the same max_score
               as the MaxHit-th one */
         if (i > save_nresult &&  good_score &&
             (nprint < MaxHit || max_score == last_score) )
	 {
	   /*---- Report all possible hits -----*/
              nhit++;
   printf("\n%d.-----------------------------------------------------------",
             nhit); printf("-------------");
   if (SeqType == DNA)
 printf("\nBlock     Rank Frame Score Strength   Location (bp) Description\n");
   else
 printf("\nBlock     Rank Frame Score Strength   Location (aa) Description\n");
	      for (j=save_nresult; j<i; j++)
	      {
                 locfirst = compute_loc(results[j].frame, results[j].offset + 1);
                 loclast = compute_loc(results[j].frame, results[j].offset + (long) results[j].width);
	 printf("%-8s %5d  % 2d   %4d  %4d     %7ld-%7ld %s\n",
		   results[j].ac, results[j].rank, results[j].frame,
                   results[j].score,
		   results[j].strength,
                   locfirst, loclast,
		   results[j].title);
	      }
	      /*--- Compare with blocks db if requested ------*/
	      if (flist->next_flist != NULL)
	      {
		 ctemp[0] = '\0';
                 found = NO;
                 fcur = flist->next_flist;
                 while (!found && fcur != NULL)
		 {
		   if (save_nresult > 0 &&
		    strcmp(results[save_nresult].ac, results[last_hit].ac) <= 0)
			 rewind(fcur->fdat);
		   else fseek(fcur->fdat, fcur->prevAC, SEEK_SET);
		   found=check_dat(fstat, frep, fcur, save_nresult, i, results);
		   last_hit = save_nresult;
                   fcur = fcur->next_flist;
		 }
	      }   /*  end of if flist */
	      nprint++;
              last_score = max_score;
	 }  /* end of print previous group */
         save_strand = strand; 
	 if (i < nresult) strcpy(save_fam, results[i].fam);
         save_nresult = i;
	 good_score=NO;
      }   /*  end of a group of blocks */
   }  /* end of a hit */

   if (fstat != NULL) fclose(fstat);
   if (frep != NULL) fclose(frep);
   return(nhit);
}  /* end of print_results */
/*======================================================================*/
/*   Look up this group in blocks.dat                                   */
/*======================================================================*/
int check_dat(fstat, frep, fcur, min_t, max_t, results)
FILE *fstat, *frep;
struct file_list *fcur;
int min_t, max_t;
struct hom *results;
{
   struct best_block *block, *lblock, *fblock;
   int i, nblk, nrep;

   nblk = 0;
   block = fblock = lblock = NULL;

   /*----- Make a list of blocks in the database -----*/
   while ( (block = read_block(fcur->fdat, results[min_t].fam)) != NULL)
   {
      if (nblk > 0) lblock->next_block = block;
      else fblock = block;
      nblk++;
      lblock=block;
   }
   if (fblock == NULL) return(0);


   /*=======================Found blocks for this AC =====================*/
   fcur->prevAC = fblock->dat_pos;

   /*---- Add the query as the last sequence in each block in the list ---*/
   /*     P-value is computed in add_query()                              */
   /*     Blocks in hit are listed here                                   */
   add_query(fstat, min_t, max_t, results, fblock);

   /*------------------ Now check for repeats --------------------*/
   i = nrep = 0;
   while (i < NRepeat && 
          strcasecmp(Repeats[i].ac, results[min_t].fam) <= 0)
      i++;
   if (i > 0 && strcasecmp(Repeats[i-1].ac, results[min_t].fam) == 0 &&
       Repeats[i-1].num > 1)
            nrep = check_repeats(frep, min_t, max_t, results, Repeats[i-1].num - 1);

   /*--- Map the blocks if > 1 in database ----*/
   if (nblk > 1)
            map_blocks(min_t, max_t, results, fblock);
   else printf("\n");

   /*---Determine the closest block seq to the query seq & display them --*/
   align_blocks(min_t, max_t, results, fblock);

   /*---Free up some memory ----------*/
   block = fblock;
   while (block != NULL)
   {
      lblock = block;
      block = block->next_block;
      free(lblock);
   }

   return(nblk);
}  /*  End of check_dat */
/*=====================================================================
      Read the blocks database
      Assumes database entries are sorted by AC
=======================================================================*/
struct best_block *read_block(fdat, fam)
FILE *fdat;
char *fam;
{
   char line[MAXLINE], ctemp[MAXLINE], *ptr, *ptr1;
   struct best_block *block;
   int done, i, idone;

   done = NO;
   block = NULL;
   while (!done && !feof(fdat) && fgets(line, MAXLINE, fdat) != NULL)
   {
      if (strncmp(line, "AC   ", 5) == 0 && 
          strncasecmp(line+5, fam, strlen(fam)) > 0)
	        done = YES;
      else if (strncmp(line, "AC   ", 5) == 0 &&
          strncasecmp(line+5, fam, strlen(fam)) == 0)
      {
	 block = (struct best_block *) malloc(sizeof(struct best_block));
	 if (block == NULL)
	 {  printf("\nOUT OF MEMORY\n\n");  exit(-1);  }
	 block->next_block = NULL;
	 block->dat_pos = ftell(fdat);
         block->ac[0] = '\0';
         strcpy(ctemp, line);
         ptr = strtok(ctemp+5, "; \n\r\t");
         if (ptr != NULL) strcpy(block->ac, ptr);
         strcpy(block->fam, block->ac);
         i = strlen(block->fam); idone = NO;
         while (!idone && i >= MINAC)
         {
            if (isalpha(block->fam[i])) { block->fam[i] = '\0'; idone = YES; }
            else  { i--; }
         }
	 block->minprev = block->maxprev = -1;
	 block->query = NO;
         block->rank = NO;
/*
	 ptr = strtok(line+12, "(");
*/
         ptr = strtok(NULL, "(");
	 if (ptr != NULL)
	 {
	    ptr = strtok(NULL, ",");
	    if (ptr != NULL)
	    {
	       block->minprev = atoi(ptr);
	       ptr = strtok(NULL, ")");
	       if (ptr != NULL) block->maxprev = atoi(ptr);
	    }
	 }
      }
      else if (block != NULL && strncmp(line, "BL   ", 5) == 0)
      {
	 block->strength = 0;
	 ptr=strstr(line,"strength=");
	 if (ptr != NULL)
	 {
	    ptr1 = strtok(ptr, "="); ptr1=strtok(NULL, "\n\r");
	    block->strength = atoi(ptr1);
	 }
	 fill_block(fdat, block);
	 return(block);
      }
   }
   return(NULL);		/* no block found! */
}  /* end of read_block */
/*====================================================================
     Fill up the block structure
=======================================================================*/
void fill_block(fdat, block)
FILE *fdat;
struct best_block *block;
{
   int done, i, itemp, n, cluster, ncluster;
   char line[MAXLINE], *ptr, *ptr1;

   block->nseq = block->width = 0;
   cluster = ncluster = 0;
   done=NO;
   while (!done && !feof(fdat) && fgets(line,MAXLINE,fdat) != NULL)
   {
      if (strlen(line) == 1)		/* blank line => new cluster */
      {
	 /*  Set #seqs in cluster to seqs in previous cluster */
	 if (ncluster > 0)
	   for (n=0; n<block->nseq; n++)
	      if (block->cluster[n] == cluster) block->ncluster[n] = ncluster;
	 cluster++; ncluster = 0;
      }
      else if (strlen(line) > 1)
      {
         /*>>> avoid MAXSEQS abort, but this needs to be fixed <<<*/
	 if (strncmp(line, "//", 2) == 0 || block->nseq == MAXSEQS) 
         {  done=YES; }
	 else
         {  if (strlen(line) > SNAMELEN && block->nseq < MAXSEQS)
	    {
	       ptr=strtok(line, "(");
               itemp = strlen(ptr);
               if (strlen(ptr) > SNAMELEN) itemp = SNAMELEN;
	       strncpy(block->name[block->nseq], ptr, itemp);
               block->name[block->nseq][itemp] = '\0';
               /*    Get rid of trailing spaces  */
               i=0;
               while(i < itemp && block->name[block->nseq][i] != ' ') i++;
               block->name[block->nseq][i] = '\0';

	       ptr=strtok(NULL, ")");
	       block->offset[block->nseq] = atol(ptr);
	       ptr1=strtok(NULL, "\n\r");
	       i=0;
	       while (ptr1[i] == ' ') i++;
	       ptr = ptr1+i;
               ptr = strtok(ptr1+i, " \n\r\t");
	       strcpy(block->aa[block->nseq], ptr);
	       block->cluster[block->nseq] = cluster;
	       ncluster++;		/* # seqs in current cluster */
	       block->width = strlen(block->aa[block->nseq]);
	       block->nseq++;
           }
	 }
      }
   }  /* end of fdat */
   /*  Compute weights for the last cluster */
   if (ncluster > 0)
	   for (n=0; n<block->nseq; n++)
	      if (block->cluster[n] == cluster) block->ncluster[n] = ncluster;
}  /* end of fill_block */
/*=======================================================================
     Add the query sequence as the last sequence in each block for
     which it has a hit.
==========================================================================*/
void add_query(fstat, min_t, max_t, results, fblock)
FILE *fstat;
int min_t, max_t;
struct hom *results;
struct best_block *fblock;
{
   struct best_block *block, *maxblock;
   int i, j, t, imin, nblock, mblock, lastrank, maxbias;
   char ac[MAXAC+1], pline[2*MAXLINE], tline[40];
   struct temp *temp;
   double n, r, p, prob;
   float ptile;

   /*------- Need to know how many possible blocks there are ---*/
   nblock=0;
   block = fblock;
   while (block != NULL)
   {
      nblock++;
      block = block->next_block;
   }
   /*-------Assuming patmat is run with repeats -------------*/
/*   nblock *= SeqType*QLen;  */
   nblock *= QLen;

   /*------ Sort this group of hits by rank now --------------*/
   temp = (struct temp *) malloc((max_t - min_t) * sizeof(struct temp));
   if (temp == NULL)
   {
      printf("\nOUT OF MEMORY\n\n"); exit(-1);
   }
   /*   temp[i].flag == 1 for biased blocks   */
   for (i=0; i<max_t-min_t; i++)
   {
      temp[i].value = results[i+min_t].rank;
      temp[i].index = i+min_t;	temp[i].flag = 0;
      /*------------------ Now check for bias --------------------*/
      j = 0;
      t = (int) strlen(results[i+min_t].ac);
      while (j < NBias)
      {
         imin = t;
         if ( (int) strlen(Bias[j].ac) < t) imin = (int) strlen(Bias[j].ac);
         if (strncmp(Bias[j].ac, results[i+min_t].ac, imin) == 0)
         {  temp[i].flag = 1; j = NBias; }
         else {   j++;  }
      }
   }
   qsort(temp, max_t-min_t, sizeof(struct temp), tempcmp);

   /*----------- Piece together a set of compatible hits, highest
       rank first and compute probability of multiple blocks ----------*/
   prob = 1.0; maxblock = NULL; pline[0] = '\0';
   mblock = 0;  	/* number of blocks in the map */
   lastrank = 0;	/* rank of previous block in the map */
   maxbias = 0;		/* bias flag for anchor block */
   for (i=0; i<max_t-min_t; i++)
   {
      t = temp[i].index;
      block = fblock;
      while (block != NULL && t < max_t)
      {
	 strcpy(ac, results[t].ac);
	 if (strcmp(ac, block->ac) == 0 && !block->query &&
             (i==0 ||
	     distance_okay(results[t].offset, fblock, block)) )
	 {
	    /*--- Adds the query to the block  ------*/
	    strcpy(block->name[block->nseq], Query);
	    block->offset[block->nseq] = results[t].offset;
	    block->cluster[block->nseq] = -1;
	    block->ncluster[block->nseq] = 1;
	    strcpy(block->aa[block->nseq], results[t].aa);
	    block->query = results[t].score;
            block->rank = results[t].rank;
	    block->nseq++;
            results[t].map_flag = YES;

            /*--- Compute probability -----*/
            if (i==0)
            {
                maxblock = block;
                lastrank = temp[i].value;
		maxbias = temp[i].flag;
            }
            else 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) nblock-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(maxblock,block)+1) / QLen;
               strcat(pline, block->ac);
               if (temp[i].flag) strcat(pline, " (biased) ");
               else              strcat(pline, " ");
               lastrank = temp[i].value;
            }
	 }
         else if (strcmp(ac, block->ac) == 0)
            nblock--;                         /*  block doesn't fit */
	 block = block->next_block;
      }
   }  /*  end of for i = min to max for hit */
 
   /*-------------Print hit--------------------------------------------*/
   if (prob > 1.0) prob = 1.0;
   if (prob < 0.0) prob = 0.0;
   ptile = 0.0;
   if (Ntiles > 0)
   {
      i = maxblock->query - 1000;
      if (i < 0) i = 0;
      else if (i > Ntiles-1) i = Ntiles-1;
           { 
              ptile = Tiles[i];
 printf("\n%d=%.2fth percentile of anchor block scores for shuffled queries",
              maxblock->query, ptile);
           }
   }
   if (strlen(pline))
   {
      printf("\nP<%6.2g for ", prob);
      i=0;
      while (i < strlen(pline))
      {
         imin=36;
         if (i+imin > strlen(pline)) imin = strlen(pline) - i;
         strncpy(tline, pline+i, imin); tline[imin] = '\0';
         if (i==0) printf("%s", tline);
         else printf("\n              %s", tline);
         i += 36;
      }
      printf("in support of %s", maxblock->ac);
   }
   else printf("\nP not calculated for single block %s", maxblock->ac);
   if (maxbias) printf(" (biased)");
   free(temp);

   /*---------------Write hit statistics--------------------------*/
   if (fstat != NULL)
   {
      block = fblock;
      while (block != NULL)
      {
         if (block->query)
         fprintf(fstat, "%s %s %s 1 %d %d %6.2g %.16f %d %d %d %d %d %ld %.2f\n",
              HomName, Query, block->ac, maxblock->rank, mblock+1, prob, prob,
              block->rank, block->query, block->strength, block->width,
	      block->cluster[block->nseq-1], QLen, ptile);
         block = block->next_block;
      }
   }
}  /*  end of add_query */
/*=======================================================================*/
int check_repeats(fstat, min_t, max_t, results, maxrep)
FILE *fstat;
int min_t, max_t, maxrep;
struct hom *results;
{
   int i, ii, t, tt, lastrank, lastpath, mrep, report, width;
   int inew, ifirst, tpath, irep, ib;
   long min, over, diff;
   char lastac[MAXAC+1];
   struct sorttemp *stemp;
   struct minmax *overlap;
   double n, r, p, prob;

   printf("\nMaximum number of repeats (from Prosite MAX-REPEAT) = %d", maxrep);
   overlap = (struct minmax *) malloc(MaxRepeat * sizeof(struct minmax));
   if (overlap == NULL)
   {
      printf("\nOUT OF MEMORY\n\n"); exit(-1);
   }
   /*------ Sort this group of hits by block name ----------------*/
 stemp = (struct sorttemp *) malloc((max_t - min_t) * sizeof(struct sorttemp));
   if (stemp == NULL)
   {
      printf("\nOUT OF MEMORY\n\n"); exit(-1);
   }
   for (i=0; i<max_t-min_t; i++)
   {
      strcpy(stemp[i].ac, results[i+min_t].ac);
      stemp[i].rank = results[i+min_t].rank;
      stemp[i].index = i+min_t;
      stemp[i].map_flag = results[i+min_t].map_flag;
      stemp[i].path = stemp[i].map_flag;
   }
   /*------ Sort by block name and map_flag (descending) --------------*/
   /*   At this point, map_flag = 1 (in path) or 0 (not in path)       */
   /*   This sort puts the block of type that is in the path first     */
   qsort(stemp, max_t-min_t, sizeof(struct sorttemp), stempcmp);

   /*------- Mark all occurences of block names that are in the path   */
   lastac[0] = '\0';
   tpath = 0;
   for (i=0; i<max_t-min_t; i++)
   {
      if (strcmp(lastac, stemp[i].ac) != 0)
      {
         strcpy(lastac, stemp[i].ac);
         lastpath = stemp[i].path;
         tpath = stemp[i].tpath = stemp[i].index;
      }
      else
      {
          stemp[i].path = lastpath;
          stemp[i].tpath = tpath;
      }
   }
   /*------ Re-sort by block name and rank ----------------------------*/
   qsort(stemp, max_t-min_t, sizeof(struct sorttemp), stempcmp2);

   /*----------- Count repeats of each block,                     
                  and compute probability of multiple blocks ----------*/
   prob = 1.0;
   mrep = 0;		/* number of repeats  */
   lastrank = 0;	/* rank of previous block in the map */
   lastac[0] = '\0';
   report = NO;		/* only report repeats if one is in map */
                        /* only report DNA if <= MAX_REPEAT aa between */
   ifirst = -1;   	/* first block with repeats of type */
   for (i=0; i<max_t-min_t; i++)
   {
      t = stemp[i].index;
      if (strcmp(lastac, stemp[i].ac) != 0)
      {
         /*   report last block if it had repeats */
         if (strlen(lastac) && mrep > 0 && report)
         {
            /*  compute probability */
            prob = 1.0;
            irep = ifirst - inew + 1;	/* # blocks of type ranked so far */
            ib = 0;			/* # blocks of type since last */
            lastrank = stemp[ifirst].rank;
            for (ii=ifirst+1; ii < i; ii++)
            {
               ib++;
               tt = stemp[ii].index;
               if (results[tt].map_flag)
               {
                  n = (double) QLen*NBlock-lastrank;
	          r = (double) stemp[ii].rank-lastrank;
                  p = QLen - irep;
                  /*      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);
                  lastrank = stemp[ii].rank;
                  irep += ib; ib = 0;
               }
            }
            if (prob > 1.0) prob = 1.0;
            if (prob < 0.0) prob = 0.0;
/*          printf("\nR<%6.2g for ", prob);
*/
        printf("\n%d non-overlapping repeats in support of %s", mrep, lastac);
            if (fstat != NULL)
               fprintf(fstat, "%s %s %s 2 %d %d %6.2g %.16f\n",
                    HomName, Query, lastac, maxrep, mrep, prob, prob);
         }
         /*---------  new block ------------------*/
         inew = i;		/* first index for this block type */
         strcpy(lastac, stemp[i].ac);
         width = results[t].width;
         mrep = 0; report = NO; prob = 1.0;
         if (results[t].map_flag) ifirst = i;
         else ifirst = -1;
         if (stemp[i].path) report = YES;
         /*   set ii to the block that's in the path here */
         ii = stemp[i].tpath;
         overlap[0].min = results[ii].offset;
      }
      /*    Check for repeats now if block isn't in the path */
      if (!results[t].map_flag && mrep < maxrep)
      {
         over = NO;
         for (ii = 0; ii <= mrep; ii++)
         {
            min = results[t].offset;
            diff = min - overlap[ii].min;
            if (diff < 0) diff = 0 - diff;
            if (diff <= width ||
                (SeqType == DNA && diff > MAX_REPEAT))
                           over = YES;
         }
         if (!over)     /*  flag the repeats */
         {
            mrep++;
            if (report) results[t].map_flag = 2;
            overlap[mrep].min = results[t].offset;
            if (ifirst < 0) ifirst = i;
         }
         strcpy(lastac, stemp[i].ac);
      }
   }
   /*   final block */
   if (strlen(lastac) && mrep > 0 && report)
   {
       /*  compute probability */
       prob = 1.0;
       irep = ifirst - inew + 1;
       ib = 0;
       lastrank = stemp[ifirst].rank;
       for (ii=ifirst+1; ii < i; ii++)
       {
          ib++;
          tt = stemp[ii].index;
          if (results[tt].map_flag)
          {
             n = (double) QLen*NBlock-lastrank;
             r = (double) stemp[ii].rank-lastrank;
             p = QLen - irep;
             /*      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);
             lastrank = stemp[ii].rank;
             irep += ib; ib = 0;
          }
       }
       if (prob > 1.0) prob = 1.0;
       if (prob < 0.0) prob = 0.0;
/*     printf("\nR<%6.2g for ", prob);
*/
       printf("\n%d non-overlapping repeats in support of %s", mrep, lastac);
       if (fstat != NULL)
            fprintf(fstat, "%s %s %s 2 %d %d %6.2g %.16f\n",
                    HomName, Query, lastac, maxrep, mrep, prob, prob);
   }
 
   free(stemp);
   return(mrep);

}  /* end of check_repeats */
/*===================================================================
    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;
   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 minprev+maxprev = 2xthe average distance
       between the blocks in sequences in the blocks database.
=====================================================================*/
int distance_okay(offset, fblock, nblock)
long offset;
struct best_block *fblock, *nblock;
{
   struct best_block *block, *lblock, *rblock;
   long mindist, maxdist;

   lblock = rblock = NULL;
   block = fblock;
   while (block != NULL && rblock == NULL)
   {
      if (block->query)		/* query added to this block already */
      {
	 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_block;
   }
   /*---------  Check the distance to the left --------------------*/
   if (lblock != NULL)
   {
      mindist = maxdist = lblock->offset[lblock->nseq-1] + (long) lblock->width;
      block = lblock->next_block;
      while (block != NULL && block != nblock)
      {
	 if (strcmp(block->ac, nblock->ac) < 0)
         {
            mindist += block->width;
	    maxdist += prev_dist(block) + block->width;
         }
	 block = block->next_block;
      }
      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_block;
      while (block != NULL && block != rblock)
      {
	 if (strcmp(block->ac, rblock->ac) < 0)
         {
            mindist += block->width;
	    maxdist += prev_dist(block) + block->width;
         }
	 block = block->next_block;
      }
      if (mindist-1 > rblock->offset[rblock->nseq-1]) return(NO);
      if (maxdist + prev_dist(rblock) < rblock->offset[rblock->nseq-1])
	 return(NO);
   }
   return(YES);
}  /*  end of distance_okay */
/*===================================================================
    Compute maximum distance preceding a block
=====================================================================*/
long prev_dist(block)
struct best_block *block;
{
   long dist;

/*
   dist = (long) block->minprev + block->maxprev;
*/
   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 best_block *fromblock, *toblock;
{
    long dist, maxdist;
    struct best_block *block, *lblock, *rblock;
  
    if (fromblock == NULL || toblock == NULL) return(-1);

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

    dist = 0;
    block=lblock->next_block;
    while (block != NULL && block != rblock)
    {
       dist += prev_dist(block) + block->width;
       block = block->next_block;
    }
    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 */
/*====================================================================
    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 (maxblock).
       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(min_t, max_t, results, fblock)
int min_t, max_t;
struct hom *results;
struct best_block *fblock;
{
   int totblks, maxdist, i, imin, imax, pos, maxspot, maxscore;
   int ilen, maxseq, spot, qspot, t, done, qleft, qright;
   double scale;
   struct best_block *block, *maxblock;
   char fam[MAXAC+1], dbline[MAXLINE], qline[MAXLINE], pline[MAXLINE];

   totblks = maxdist = maxscore = 0;
   /*--- Determine the scale for the database blocks (# aas per space) ---*/
   block = fblock;
   strcpy(fam, block->fam);
   while (block != NULL)
   {
      totblks++;
      /*------ Don't map distance before the first block --------------*/
      if (block != fblock)
         maxdist += block->maxprev;  /* distance from previous block */
      maxdist += block->width;    /* width of this block */
      if (block->query > maxscore)
      {  maxscore = block->query; maxblock = block; }
      block = block->next_block;
   }
   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<MAXLINE; spot++)
   {  dbline[spot] = qline[spot] = ' '; }
   spot=0;
   maxspot = 0;
   block = fblock;
   while (block != NULL)
   {
      if (block != fblock)
      {
         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;
      ilen = strlen(block->ac); ilen--;
      for (i=spot; i < spot+imin; i++) strncpy(dbline+i, block->ac+ilen, 1);
      spot += imin;
      if (block==maxblock) maxspot=spot;
      block = block->next_block;
   }
   dbline[spot] = '\0';
   printf("\n%20s ", 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 = fblock;
   while (block != NULL)
   {
      if (block->query)
      {
	 imin = (int) ((double) 0.5+(block->offset[block->nseq-1]-pos)/scale);
         if (imin >= 0)   /* skip over blocks in wrong order */
         {
	    if (spot+imin > MAXLINE && spot+1 < MAXLINE)
               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 < MAXLINE)
            {
               ilen = strlen(block->ac); ilen--;
	       for (i=spot; i<spot+imin; i++) 
                   strncpy(qline+i, block->ac+ilen, 1);
	       spot += imin;
            }
	    pos = block->offset[block->nseq-1] + block->width;
	    if (block == maxblock) qspot = spot;
         }
      }
      block = block->next_block;
   }
   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 maxblock */
      for (i=0; i<MAXLINE; i++) qline[i] = ' ';
      for (t=min_t; t<max_t; t++)
         if (results[t].map_flag == 2)
         {
            imin = (int) ((double) 0.5+(results[t].offset-pos)/scale);
            if (imin >=0)
            {
               if (spot+imin < MAXLINE) 
               {
                  spot += imin;
                  imin = (int) ((double) 0.5+results[t].width/scale);
                  if (imin < 1) imin = 1;
                  if (spot+imin < MAXLINE)
                  {
                     ilen = strlen(results[t].ac); ilen--;
                     for (i=spot; i<spot+imin; i++)
                        strncpy(qline+i, results[t].ac+ilen, 1);
                     spot += imin;
                  }
               }
               pos = results[t].offset + results[t].width;
               done = NO;
               results[t].map_flag = 3;		/* 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 maxblock */
      for (i=0; i<MAXLINE; i++) qline[i] = ' ';
      for (t=min_t; t<max_t; t++)
         if (!results[t].map_flag)
         {
            imin = (int) ((double) 0.5+(results[t].offset-pos)/scale);
            if (imin >=0)
            {
               if (spot+imin < MAXLINE) 
               {
                  spot += imin;
                  imin = (int) ((double) 0.5+results[t].width/scale);
                  if (imin < 1) imin = 1;
                  if (spot+imin < MAXLINE)
                  {
                     ilen = strlen(results[t].ac); ilen--;
                     for (i=spot; i<spot+imin; i++)
                        strncpy(qline+i, results[t].ac+ilen, 1);
                     spot += imin;
                  }
               }
               pos = results[t].offset + results[t].width;
               done = NO;
               results[t].map_flag = 4;
            }
         }
      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(min_t, max_t, results, fblock)
int min_t, max_t;
struct hom *results;
struct best_block *fblock;
{
   int s, i, t, savspot, repspot, spot, bspot, datmin, datmax, homdist;
   int first, itemp;
   char datline[MAXLINE], homline[MAXLINE], blkline[MAXLINE];
   char barline[MAXLINE], repline[MAXLINE], saveac[12], ctemp[10];
   struct best_block *block;

   for (i=0; i<MAXLINE; i++)
   { datline[i] = homline[i] = blkline[i] = barline[i] = repline[i] = ' ';}
   spot=datmin=datmax=homdist=0;
   strcpy(saveac, "        ");
   block=fblock;
   while (block != NULL)
   {
      if (block->query)
      {
	 datmin += block->minprev;
	 datmax += block->maxprev;
	 homdist = block->offset[block->nseq-1] - homdist;

         consensus(block);
	 s = closest_seq(block);

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

itemp = (int) strlen(block->name[s]);
         strncpy(blkline+spot, block->ac, strlen(block->ac));
         strncpy(datline+spot, block->name[s], strlen(block->name[s]));
         strncpy(homline+spot, block->name[block->nseq-1],
              strlen(block->name[block->nseq-1]));
	 spot+=19;		/* seq name + 1 */

	 blkline[spot] = saveac[7];
	 strncpy(blkline+spot+1, "<->", 3);
	 blkline[spot+4] = block->ac[7];
/*	 kr_itoa(block->offset[s], ctemp, 10);   */
	 /* offset starts at 1 in */
	 sprintf(ctemp, "%d", block->offset[s]);
	 strncpy(datline+spot, ctemp, strlen(ctemp));
/*	 kr_itoa(block->offset[block->nseq-1]+1, ctemp,10); */
         sprintf(ctemp, "%d", block->offset[block->nseq-1]+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->aa[s]+i, 1);
	    strncpy(homline+spot, block->aa[block->nseq-1]+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=min_t; t< max_t; t++)
         {
            if ((results[t].map_flag == 3 || results[t].map_flag == 2) &&
                   strcmp(block->ac, results[t].ac) == 0)
            {
               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;
               }
               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 = block->offset[block->nseq-1] + block->width;
	 strcpy(saveac, block->ac);
      }
      else
      {		/*  block not in query sequence */
	 datmin += block->minprev + block->width;
	 datmax += block->maxprev + block->width;
      }
      block = block->next_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 */
/*======================================================================*/
/*--- Just find the sequence closest to the last sequence in the block
      based on the number of identities */
/*======================================================================*/
int closest_seq(block)
struct best_block *block;
{
   int npair, s1, s2, l1, l2, i, i1, i2;
   int maxscore, maxs1;
   struct pair *pairs;
/*SGI   delete above declaration of pairs and use fixed below */
/*SGI   struct pair pairs[MAXSEQS*(MAXSEQS-1)/2];  */

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

/*    Compute scores for all possible pairs of sequences            */
   for (s1=0; s1<block->nseq-1; s1++)   		/* col = 0, n-2     */
   {
      l1 = 0;
      s2 = block->nseq-1;   /* last seq in block == the query sequence */
      l2 = 0;
      pairs[s1].score = 0;
      pairs[s1].cluster = -1;
      for (i=0; i<=block->width; i++)
      {
	 i1 = l1+i;  i2 = l2+i;
	 /* s1 is the block seq & is all caps, but s2 is the query
	    seq & it may have lower case chars in it */
	 if (i1 >= 0 && i1 < block->width &&
	     i2 >= 0 && i2 < block->width &&
		strncasecmp(block->aa[s1]+i1, block->aa[s2]+i2, 1) == 0)
		   pairs[s1].score += 1;
      }
   }  /* end of s1 */
   maxs1 = -1; maxscore = -1;
   for (s1=0; s1 < block->nseq-1; s1++)
      if (pairs[s1].score > maxscore)
      {
	 maxscore = pairs[s1].score;
	 maxs1 = s1;
      }

/*SGI   delete free(pairs) and used fixed allocation  */
      free(pairs);
      return(maxs1);
}  /*  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(block)
struct best_block *block;
{
   int s, s1, i;
   char seqaa[2];

   s = block->nseq - 1;		/* This is the query sequence */
   for (s1=0; s1 < s; s1++)
      for (i=0; i < block->width; i++)
      {
         if (strncasecmp(block->aa[s]+i, block->aa[s1]+i, 1) == 0)
         {
            strncpy(seqaa, block->aa[s]+i, 1);
            if (seqaa[0] >= 97 && seqaa[0] <= 122)
            {
               seqaa[0] -= 32;
               strncpy(block->aa[s]+i, seqaa, 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 sorttemp structure by ac, map_flag (D) and rank field */
int stempcmp(t1, t2)
struct sorttemp *t1, *t2;
{
   int diff;

   diff = strcmp(t1->ac, t2->ac);
   if (diff != 0 ) return(diff);
   else
   {
      diff = t2->map_flag - t1->map_flag;
      if (diff != 0) return(diff);
      else return(t1->rank - t2->rank);
   }
} /*  end of stempcmp */
/*========================================================================*/
/*  Sort sorttemp structure by ac and rank field */
int stempcmp2(t1, t2)
struct sorttemp *t1, *t2;
{
   int diff;

   diff = strcmp(t1->ac, t2->ac);
   if (diff != 0 ) return(diff);
   else return(t1->rank - t2->rank);
} /*  end of stempcmp2 */
/*=========================================================================*/
   /*  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
   */
int compute_loc(frame, offset)
int frame;
int offset;
{
   int 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 file_list *make_flist()
{
   struct file_list *new;

   new = (struct file_list *) malloc(sizeof(struct file_list));
   return(new);
}  /*   end of make_flist()   */
/*==========================================================================*/
void insert_flist(flist, new)
struct file_list *flist, *new;
{
   struct file_list *fcur;

   fcur = flist;
   while (fcur->next_flist != NULL)
      fcur = fcur->next_flist;
   fcur->next_flist = new;
}   /* end of insert_flist  */
