/* COPYRIGHT 1997-2004 Fred Hutchinson Cancer Research Center, Seattle, WA, USA
   CODEHOP - COnsensus-DEgenerate Hybrid Oligonucleotide Primers
   (formerly BASE_PRIDE - Blocks ASistEd PRImer DEsign)
   This program will suggest degenerate PCR primers from blocks data.   
==========================================================================
12/ 7/97 1. Changed Base Pride to Codehop
12/30/97 1. Extend temperature calculation to non-degenerate portion of
	    core adjacent to clamp. compute_thermo().
 1/ 2/98 1. Increased CLAMP_MAX_LEN from 30 to 165bps == max block width
         2. Skip degenerate clamp residues from nt_clamp[] in compute_thermo();
		the clamp temperature is really undefined if the most common
		codon is not used (esp. if equal codon usage)
            Use nsres instead of nres when computing core degeneracy (IE,
		apply Core_Strictness).
 1/ 3/98 1. Use average entropy/enthalpy values if degen. codons in clamp.
 1/ 7/98 1. Changed criteria for best_oligo in a clump to not use clamp info.
 1/ 9/98 1. Don't use clamp strictness; if degenerate residues in clamp then
	    pick one at random (changed content of ->nt_max[]).
 1/ 9/98 1. Added clamp probability score (->clamp_prob).
 1/10/98 1. Don't use clamp strictness; if degenerate residues in clamp then
	    pick one at random (changed content of ->nt_max[]).
         2. Allow core to be 12 if ROSE=FALSE. Fixed ->clamp_prob.
 1/11/98 1. Format modifications.
         2. Added -Begin100 option: If FALSE, use core strictness to
            determine eligible starting points.
         3. Include ->clamp_prob in best_oligo determination.
 3/27/98 1. Modified compute_max() per Tim Rose.
    Never use codons AAA [0], CCC [21], GGG [42] or TTT [63] in nt_common[],
    but still are used to compute DNA PSSM - compute_max(), fix_codons().
 3/28/98 1. Adjusted core length for complements: core_len in find_oligos()
 4/13/98 1. New option to specify max length of runs (-Apolyx).
         2. Break up runs > polyx value: break_runs()
 4/14/98 1. Fixed compute_thermo(); was computing temperature incorrectly 
		for complement.
 4/16/98 1. Added nt_max[]; break_runs() once from compute_max().
	    Undid 3/27/98 change for nt_common[].
 5/ 6/98 1. Add OutOligo to be max number of oligos to show per block.
	    Activated display_oligos(). But now the oligos are always
	    printed in sorted order, not in geographical order any more.
 4/14/99 1. Error message if no blocks found in input
12/ 9/99 1. Fix memory problems
12/17/99 1. Longer AC (family name)
-----------------------Version 3.6--------------------------------------------
10/14/04.1  Problems with genetic code, incomplete blocks in input file
            Revise search for codon usage table
=================================================================================
*/

#define EXTERN

#include <blocksprogs.h>
#include "codehop.h"

#define USAGE "Usage: %s [blocks-input_file_name [output_file_name]]\n [-Ffamily_name]\n [-Ppssm_type]\n [-Ccodon_usage_file]\n [-Ggenetic_code_type]\n [-Dcore_degeneracy_parameter]\n [-Score_strictness_parameter]\n [-Lclamp_strictness_parameter]\n [-Tclamp_temperature]\n [-Nconcentration]\n [-Rose_restrictions]\n [-Most_common_codon]\n [-Verbose]\n [-Output]\n [-Begin]\n [-ApolyX_parameter]\n\n"


#define DEFAULTS "Defaults: in-\"%s\" out-\"%s\"\n family-\"%s\"\n pssm_type-\"%d\"\n codon_usage-\"%s\"\n genetic_code_type-\"%d\"\n core_degeneracy-\"%3.1f\"\n core_strictness-\"%3.1f\"\n clamp_strictness-\"%3.1f\"\n clamp-temperature-\"%3.1f\"\n concentration-\"%3.1f\"pM\n Rose-FALSE\n most_common_codon-FALSE\n verbose-FALSE\n begin_oligo-TRUE\n polyX-\"%d\"\n\n"


#define AA_ALPHABET_SHRT "ACDEFGHIKLMNPQRSTVWXY"

/*
 * Local variables and data structures
 */

#define is_res(x) (x==aa_atob['A'] || x==aa_atob['C'] || x== aa_atob['G'] ||   \
                x==aa_atob['T']) ? TRUE : FALSE
/*	Clamp temperate: a = AT content, g = GC content of clamp region */
#define cltemp(a,g) (3.0 + 4.0 * g + 2.0 * a)

void getargs() ;
void fprint_matrix1() ;
void fprint_matrix1_3spaced() ;

void fix_codons();
void process_block();

struct dna_matrix *new_dna_matrix();
void free_dna_matrix();
struct dna_matrix *unpssm_pssm();
struct dna_matrix *complement_pssm();
void compute_max();

struct oligo_list *make_oligo();
void find_oligos();
void insert_oligo();
void display_oligos();
void display_oligo();
void score_clamp();
void score_clamp_prob();
void display_consensus();
int get_clump();

double clamp_freier();
double clamp_rychlik();
void compute_thermo();
void break_runs();
int oligocmp();

/*---------------------- Global variables ----------------------------*/
char Version[12] = "10/14/04.1";		/* Version date */
Boolean  WWW_FLAG = TRUE ;

double Clamp_Strictness = MAX_STRICTNESS;	/* clamp codon strictness */
double Core_Strictness = MIN_STRICTNESS;	/* core codon strictness  */
double Core_Degeneracy = DEFAULT_CORE_DEG;	/* core degeneracy */
double Clamp_Temperature = DEFAULT_TEMP;	/* clamp temperature */
double Concentration = DEFAULT_CONC;		/* concentration in nM */
double Koncentration = DEFAULT_KONC;		/* K+ concentration in mM */
double ProdLen = DEFAULT_PRODLEN;		/* Product length */
int PolyX = DEFAULT_POLYX;			/* Max run of any nuc */
int Rose = FALSE;	/* Enforces Tim Rose's restrictions on core region */
int Most_Common_Codon = FALSE;	/* use most common codon in clamp */
int Verbose = FALSE;		/* verbose output */
int OutOligo = 0;		/* number of oligos to print */
int Begin100 = TRUE;		/* begin core on conserved residue */
int Debug = FALSE;		/* debugging output */

char  Frqname[MAXNAME]="";
char  IDname[MAXNAME]="";
int   Pssm_Type = PSSM_TYPE_DFLT, Gcode_Type = GCODE_TYPE_DFLT ;
unsigned char Gcode[64], RevGcode[64];
/*  Codons in Blimps order: use T instead of U to use amino acid routines  */
char Codons[64][3] = {
"AAA","AAC","AAG","AAT", "ACA","ACC","ACG","ACT", "AGA","AGC","AGG","AGT",
"ATA","ATC","ATG","ATT", "CAA","CAC","CAG","CAT", "CCA","CCC","CCG","CCT",
"CGA","CGC","CGG","CGT", "CTA","CTC","CTG","CTT", "GAA","GAC","GAG","GAT",
"GCA","GCC","GCG","GCT", "GGA","GGC","GGG","GGT", "GTA","GTC","GTG","GTT",
"TAA","TAC","TAG","TAT", "TCA","TCC","TCG","TCT", "TGA","TGC","TGG","TGT",
"TTA","TTC","TTG","TTT" };

/*  Breslauer, et al entropy & enthalpy for nearest neighbors, order is
     AA AC AG AT CA CC CG CT    GA GC GG GT TA TC TG TT          */
double Bres_Enthalpy[16]  = {  9.1,  6.5,  7.8,  8.6,  5.8, 11.0, 11.9,  7.8,
                              5.6, 11.1, 11.0,  6.5,  6.0,  5.6,  5.8,  9.1 };
double Bres_Entropy[16] = { 24.0, 17.3, 20.8, 23.9, 12.9, 26.6, 27.8, 20.8,
                             13.5, 26.7, 26.6, 17.3, 16.9, 13.5, 12.9, 24.0 };
/*=======================================================================*/
/*
 * main
 *   controls flow of program
 *   Parameters: argc, argv
 *   Error codes:
 */

int main(argc, argv)
     int argc;
     char *argv[];
{
  FILE  *inpf=stdin, *outf=stdout ;
  FILE  *fcod, *fqij;
  char  inpfname[MAXNAME]="stdin", outfname[MAXNAME]="stdout" ;
  char  src_fam_name[BLOCK_AC_LEN]="" ;
  char  *blimps_dir, tmp[MAXNAME] ;
  Block *block;
  int   i1, nblock, prev_num;
  char  codon_usage_file[MAXNAME] = CODON_USAGE_FILE_DFLT ;
  char  *pssm_defs[] = PSSM_DEFS ;
  char  qijname[MAXNAME]="";
  struct timeval tv;

  ErrorLevelReport = 2;		/* suppress BLIMPS errors */
  blimps_dir = getenv("BLIMPS_DIR");

  printf("CODEHOP Version %s\n", Version);
  printf("COPYRIGHT 1997-2004, Fred Hutchinson Cancer Research");
  printf(" Center, Seattle, WA, USA\n");
  if (Debug)
  {
     for (i1=0; i1< argc; i1++) printf("%s ", argv[i1]);
     printf("\n");
  }

  /* show usage and what defaults are used */
  if (argc < 2 || (argc == 2 && strstr(HELP_REQUEST, argv[1]) != NULL) )
    {
    fprintf(stderr, USAGE, argv[0]) ;
    fprintf(stderr, DEFAULTS, inpfname, outfname, "first_in_input", 
       Pssm_Type, codon_usage_file, Gcode_Type, Core_Degeneracy,
       Core_Strictness, Clamp_Strictness, Clamp_Temperature, Concentration,
       PolyX) ;
    fprintf(stderr, COMMENTS) ;
    exit(0) ;
    }
  else
     getargs(argc, argv, inpfname, outfname, src_fam_name, codon_usage_file) ;

/*printf (">>>inpfname=%s<<<\n", inpfname); */


/* check arguments */

  sprintf (tmp, " %d ", Pssm_Type) ;
  if (strstr(PSSM_TYPES, tmp) == NULL) 
     {
     fprintf(stderr, "Warning: Unspecified pssm type (%d) requested.\nUsing default pssm type (%d).\n\"%s help\" for help.\n\n",
             Pssm_Type, PSSM_TYPE_DFLT, argv[0]) ;
     Pssm_Type = PSSM_TYPE_DFLT ;
     }

  if (Gcode_Type < 0 || Gcode_Type > NUMBER_OF_GENETIC_CODES)
     {
     fprintf(stderr, "Warning: Unspecified gcode type (%d) requested.\nUsing default gcode type (%d).\n\"%s help\" for help.\n\n",
             Gcode_Type, GCODE_TYPE_DFLT, argv[0]) ;
     Gcode_Type = GCODE_TYPE_DFLT ;
     }

  if (Core_Strictness < MIN_STRICTNESS ||
      Core_Strictness > MAX_STRICTNESS)
     {
     fprintf(stderr, "Warning: Core strictness parameter requested (%3.2f) is out of range (%3.2f to %3.2f)\nUsing default value (%3.2f).\n",
             Core_Strictness, MIN_STRICTNESS, MAX_STRICTNESS, MIN_STRICTNESS) ;
     Core_Strictness = MIN_STRICTNESS ;
     }

  if (Clamp_Strictness < MIN_STRICTNESS ||
      Clamp_Strictness > MAX_STRICTNESS)
     {
     fprintf(stderr, "Warning: Clamp strictness parameter requested (%3.2f) is out of range (%3.2f to %3.2f)\nUsing default value (%3.2f).\n",
             Clamp_Strictness, MIN_STRICTNESS, MAX_STRICTNESS, MAX_STRICTNESS) ;
     Clamp_Strictness = MAX_STRICTNESS ;
     }

  if (Clamp_Strictness < Core_Strictness)
     {
     fprintf(stderr, "Warning: Clamp strictness parameter requested (%3.2f) is less than core strictness parameter (%3.2f)\nUsing default values (%3.2f, %3.2f).\n",
             Clamp_Strictness, Core_Strictness, MAX_STRICTNESS, MIN_STRICTNESS) ;
     Core_Strictness = MIN_STRICTNESS;
     Clamp_Strictness = MAX_STRICTNESS;
     }

   if (Clamp_Temperature < 0.0 )
     {
     fprintf(stderr, "Warning: Clamp temperature requested (%3.2f) is negative\nUsing default value (%3.2f).\n", Clamp_Temperature, DEFAULT_TEMP);
     Clamp_Temperature = DEFAULT_TEMP;
     }

   if (Concentration < 0.0 )
     {
     fprintf(stderr, "Warning: Concentration requested (%3.2f) is negative\nUsing default value (%3.2f).\n", Concentration, DEFAULT_CONC);
     Concentration = DEFAULT_CONC;
     }

                                                               /* open files */
/* Input */
  if (strcmp(inpfname,"stdin") !=0 && (inpf=fopen(inpfname, "r")) == NULL)
     {
     fprintf(stderr, "Cannot open input file %s\n", inpfname);
     exit(-1);
     }

/* output */
  outf = NULL;
  if (strcmp(outfname,"stdout") !=0 && (freopen(outfname, "w", stdout)) == NULL)
     {
     fprintf(stderr, "Cannot open output file %s\n", outfname);
     exit(-1);
     }


/* Amino acid frequency */
  if (blimps_dir != NULL) 
  {
       sprintf(Frqname, "%s/docs/%s", blimps_dir, "default.amino.frq");
       sprintf(IDname, "%s/docs/%s", blimps_dir, "identity.frq");
  }
  else
  {
       sprintf(Frqname, "%s", AA_FREQUENCY_FNAME) ;
       sprintf(IDname, "%s", ID_FREQUENCY_FNAME) ;
  }

/* don't load_frequencies() yet because we equal freqs of 1 for nuc pssm */

  if (blimps_dir != NULL) sprintf(qijname, "%s/docs/", blimps_dir);

  if (Pssm_Type == 30) strcat(qijname, "default.iij"); /*average score*/
  else                 strcat(qijname, "default.qij");

  Qij = NULL;
  if ( (fqij=fopen(qijname, "r")) != NULL)
     { Qij = load_qij(fqij); fclose(fqij); }
  RTot = LOCAL_QIJ_RTOT;

/*  Gcode[i] = aa # with i in Blimps = ACGT order */
  init_gcode(&gcodes[Gcode_Type], Gcode, RevGcode);

/* Codon usage, look in current dir, then in blimps_dir, then try default */
/*printf (">>>blimps_dir=%s<<<\n", blimps_dir); */
  if ((fcod=fopen(codon_usage_file, "r")) == NULL)
  {
     fprintf(stderr, "Cannot open codon usage file %s\n", codon_usage_file);

     /*  Try to find it elsewhere */
     if (blimps_dir != NULL) 
     {
        strcpy(tmp, codon_usage_file);
        sprintf(codon_usage_file, "%s/docs/%s", blimps_dir, tmp); 
        fprintf(stderr, "...trying %s ...\n", codon_usage_file);
        if ((fcod = fopen(codon_usage_file, "r")) == NULL)
        {
           fprintf(stderr, "Cannot open codon usage file %s\n", codon_usage_file);

           sprintf(codon_usage_file, CODON_USAGE_FILE_DFLT);
           fprintf(stderr, "...trying %s ...\n", codon_usage_file);
           if ((fcod = fopen(codon_usage_file, "r")) == NULL)
           {
              fprintf(stderr, "Cannot open codon usage file %s\n", codon_usage_file);
              strcpy(tmp, codon_usage_file);
              sprintf(codon_usage_file, "%s/docs/%s", blimps_dir, tmp); 
              fprintf(stderr, "...trying %s ...\n", codon_usage_file);
              if ((fcod = fopen(codon_usage_file, "r")) == NULL)
              {
                 fprintf(stderr, "Cannot open codon usage file %s\n", codon_usage_file);
                 exit(-1);
              }
           }
        }
     }
     else  /* no $BLIMPS_DIR */
     {
        sprintf(codon_usage_file, CODON_USAGE_FILE_DFLT);
        fprintf(stderr, "...trying %s ...\n", codon_usage_file);
        if ((fcod = fopen(codon_usage_file, "r")) == NULL)
        {
           fprintf(stderr, "Cannot open codon usage file %s\n", codon_usage_file);
           exit(-1);
        }
     }
  } /* end of codon usage file */

  load_codons(fcod);
  /*  Fix up the codons for use here, they are in Codon_Usage[64]  */
  fix_codons();

/*----------------------------------------------------------------------*/
/* print parameters used */
     printf( 
       "\nParameters:\n Amino acids PSSM calculated with %s\n and back-translated with %s genetic code\n and codon usage table \"%s\"\n Maximum core degeneracy %3.0f    Core strictness %3.2f\n Clamp strictness %3.2f   Target clamp temperature %3.2f C\n DNA Concentration %3.2f nM   Salt Concentration %3.2f mM\n Codon boundary %d   Most common codon %d\n Verbose %d   Output %d\n Begin %d   PolyX %d\n",
        pssm_defs[Pssm_Type], gcodes[Gcode_Type].name, codon_usage_file, Core_Degeneracy, Core_Strictness, Clamp_Strictness, Clamp_Temperature, Concentration, Koncentration, Rose, Most_Common_Codon, Verbose, OutOligo, Begin100, PolyX) ;
     printf("Suggested CODEHOPS: The degenerate region (core) is printed in lower case,\nthe non-degenerate region (clamp) is printed in upper case.\n");

/*-------------------------------------------------------------------------*/
/*  Initialize the random number generator */
   gettimeofday(&tv, NULL);
   srand(tv.tv_sec^tv.tv_usec);

  /*  Process all blocks, or all blocks with specified AC. Sequences and
      their number can differ from block to block  */
  nblock = 0;
  prev_num = -1;
  while ((block = read_a_block(inpf)) != NULL)
  {
      /* Trying to make sure blocks are from the same group here */
      /* sometimes users cut-paste incomplete blocks and we abort...*/
      if ( (!strlen(src_fam_name) ||
            !strncasecmp(src_fam_name, block->family, strlen(src_fam_name)) )
          && (prev_num < 0 || block->num_sequences == prev_num) )
      {	         
         process_block(block); 
         nblock++;  
         prev_num = block->num_sequences;
      }
      free_block(block);
  }
  if (nblock < 1) printf("\nNo correctly formatted blocks found to process\n");
   
  fclose(inpf);
  if (outf != NULL) fclose(outf);

  exit(0);
}  /* end of main */

/*****************************************************************************
 * get input and output file names and other program parameters.
 *
 * All parameters, except explicit input and output file names, 
 * should be preceded by a "-".
 * A sole "-" (implicitly) specifies a default for a file name.
 * The first explicit or implicit file name will be assigned to the input file
 * and the second to the output file.
 *
 * NOTE:  Most of the returned variables are global...
 *
 *****************************************************************************/

void getargs(argc, argv, inpfname, outfname, fam_name, codon_usage_fname)

int    argc;
char   *argv[];
char   inpfname[], outfname[], fam_name[], codon_usage_fname[] ;
{

   int i1 ;
   int fnames=0 ;
   char *chr_ptr ;


   for(i1=1; i1<argc; i1++)                       /* go through all arguments*/
   {
   if (Debug) printf("%s\n", argv[i1]);
      if (argv[i1][0] != '-')                        /* presumed a file name */
	 {
         if (fnames == 0)                    /* first name is for input file */
	    {
            strcpy(inpfname,argv[i1]) ;
            fnames++ ;
	    }
         else if (fnames == 1)             /* second name is for output file */
	    {
            strcpy(outfname,argv[i1]) ;
            fnames++ ;
	    }
         else                                           /* probably an error */
            fprintf(stderr, 
            "Warning: Unrecognized parameter - \"%s\".\n", 
            argv[i1]);
	 }
      else
	 {
         switch (toupper(argv[i1][1]))
	    {
            case 'A' :
               chr_ptr = argv[i1] + 2 ;
               PolyX = atoi(chr_ptr) ;
               break ;
            case 'B' :
               Begin100 = FALSE;
               break ;
            case 'C' :
               chr_ptr = argv[i1] + 2 ;
               strcpy(codon_usage_fname,chr_ptr) ;
               break ;
            case 'D' :
               chr_ptr = argv[i1] + 2 ;
               Core_Degeneracy = atof(chr_ptr) ;
               break ;
            case 'F' :
               chr_ptr = argv[i1] + 2 ;
               strcpy(fam_name,chr_ptr) ;
               break ;
            case 'G' :
               chr_ptr = argv[i1] + 2 ;
               Gcode_Type = atoi(chr_ptr) ;
               break ;
            case 'L' :
               chr_ptr = argv[i1] + 2 ;
               Clamp_Strictness = atof(chr_ptr) ;
               break ;
            case 'M' :
               Most_Common_Codon = TRUE;
               break ;
            case 'N' :
               chr_ptr = argv[i1] + 2 ;
               Concentration = atof(chr_ptr) ;
               break ;
            case 'O' :
               chr_ptr = argv[i1] + 2 ;
               OutOligo = atoi(chr_ptr) ;
               if (OutOligo < 1) OutOligo = 1;
               break ;
            case 'P' :
               chr_ptr = argv[i1] + 2 ;
               Pssm_Type = atoi(chr_ptr) ;
               break ;
            case 'R' :
               Rose = TRUE;
               break ;
            case 'S' :
               chr_ptr = argv[i1] + 2 ;
               Core_Strictness = atof(chr_ptr) ;
               break ;
            case 'T' :
               chr_ptr = argv[i1] + 2 ;
               Clamp_Temperature = atof(chr_ptr) ;
               break ;
            case 'V' :
               Verbose = TRUE;
               break ;
	    case '\0' :                           /* parameter is just a "-" */
               fnames++ ;
               break ;
	    default :
               fprintf(stderr, 
               "Warning: Unrecognized parameter - \"%s\".\n", 
               argv[i1]);
	    }
         }
   }  /* end of args */

   /*  Specified Verbose overrides OutOligo  */
/*
   if (OutOligo > 0) Verbose = FALSE;
*/
   if (Verbose) OutOligo = 0;
   if (!Verbose && OutOligo < 1) OutOligo = 1;

   return ;
}  /*   end of getargs */

/*==================================================================
      Process a block
====================================================================*/
void process_block(block)
Block *block;
{
Matrix *matrix;
struct dna_matrix *nuc_matrix, *rev_matrix;

        /* load the frequencies for converting blocks to matrices */
/* this has to be done each time since we load other values for converting the nuc PSSM */
	load_frequencies(Frqname);	/* freqs for AA PSSM */

        matrix = block_to_matrix(block, Pssm_Type) ;
/*output_matrix(matrix, stdout, FLOAT_OUTPUT, AA_SEQ); */

        /* convert the aa PSSM to a nuc PSSM */
        load_frequencies(IDname);	/*  use 1.0 */

        nuc_matrix = unpssm_pssm(matrix);
        printf("\nProcessing Block %s\n", block->number);
        /* Compute nt_core[], nt_common[], nt_max[],  nt_clamp[],
	 nres[], nsres[] & max_weight[] for each position of matrix */
        compute_max(nuc_matrix, matrix);
	/* Print nt_common[] == the consenus amino acids */
        display_consensus(nuc_matrix);
/*fprint_matrix1(nuc_matrix, stdout, nt_adegen[ALPHABET].list) ;*/
        find_oligos(nuc_matrix);

        rev_matrix = complement_pssm(nuc_matrix);
        printf("\nProcessing Complement of Block %s\n", block->number);
        compute_max(rev_matrix, matrix);
        display_consensus(nuc_matrix);
        find_oligos(rev_matrix);

        free_matrix(matrix);
        free_dna_matrix(nuc_matrix);
        free_dna_matrix(rev_matrix);

}  /* end of process_block */

/*=====================================================================
 * unpssm_pssm
 *   Translates an animo acid pssm into a  DNA pssm of degenerate
 *    codons
 *   Parameters:
 *     Matrix *pssm: the amino acid pssm 
 *   Return code: a pointer to the new DNA pssm
 *   Notes: this method only works because ACGT are valid amino acid codes
 */

struct dna_matrix *unpssm_pssm(pssm)
     Matrix *pssm;
{
  Matrix *tmp_pssm;
  struct dna_matrix *dna_pssm;
  Block *tmp_block;

  int i, aa, pos, cod, res;
  unsigned char c;

  /* make sure it is an AA PSSM: can only tell this if the undefined
     pointer is set, which is usually isn't */
/*
  if ( pssm->undefined != AA_SEQ ) {
    sprintf(ErrorBuffer, 
    "unpssm_pssm(): Not an amino acid pssm, not unpssming.");
    ErrorReport(PROGRAM_ERR_LVL);
    sprintf(ErrorBuffer, 
	    "                      Returning the original pssm.\n");
    ErrorReport(PROGRAM_ERR_LVL);
    return pssm;
  }
*/

   /* Allocate the DNA pssm  */
   dna_pssm = new_dna_matrix(3 * pssm->width);
   dna_pssm->block = pssm->block;
   dna_pssm->strand = 1;

   /* Make the DNA block, it's 3 wide, used for each PSSM column  */
   tmp_block = new_block(3, 64);
   /*  Each block "sequence" is a codon  */
   for (cod = 0; cod < 64; cod++) 
   {
         for (i=0; i< 3; i++)
         {
            c = Codons[cod][i];
            tmp_block->sequences[cod].sequence[i] = aa_atob[c];
         }
   }


   /*  Compute the DNA PSSM one AA PSSM position at a time */
   for (pos = 0; pos < pssm->width; pos++)
   {
      /* Set the "sequence" weight for each codon  =
         AA PSSM weight for aa x codon usage   */
      for (cod=0; cod < 64; cod++)
      {
         aa = Gcode[cod];
         tmp_block->sequences[cod].weight = pssm->weights[aa][pos];
         tmp_block->sequences[cod].weight *= Codon_Usage[cod];
/*
printf("cod=%d aa=%d pssm=%f usage=%f weight=%f\n",
  cod, aa, pssm->weights[aa][pos], Codon_Usage[cod],
  tmp_block->sequences[cod].weight);
*/
      }
      /*  Convert the block to a PSSM, odds ratios with freqs = 1,
          normalized to add to 100 for each position. May want to
          use other PSSM types ?   */
      tmp_pssm = block_to_matrix(tmp_block, Pssm_Type);

      /* Move the weights to the DNA PSSM, adjust residue index  */
      for (i=0; i< 3; i++)	/* i is the position within the codon */
         for (aa = 0; aa < 25; aa++)
            if (is_res(aa))
               for (res = 0; res < 4; res++)
                  if (nt_btoa[res] == aa_btoa[aa])
                     dna_pssm->weights[res][3*pos+i] =
                     tmp_pssm->weights[aa][i];

      free_matrix(tmp_pssm);
   }   /* end of AA PSSM position */

   free_block(tmp_block);
   return(dna_pssm);

}  /*  end of unpssm_pssm */
/*====================================================================
	Allocate memory for a new DNA PSSM
=====================================================================*/
struct dna_matrix *new_dna_matrix(length)
int length;
{
   struct dna_matrix *new;
   int res;

   new = (struct dna_matrix *) malloc(sizeof(struct dna_matrix));
   new->length = length;
   new->weights[0] = malloc(4*length*sizeof(MatType));
   for (res=0; res< 4; res++) new->weights[res] = new->weights[0] + res*length;
   new->max_weight = malloc(length*sizeof(MatType));
   new->nres = malloc(length*sizeof(int));
   new->nsres = malloc(length*sizeof(int));
   new->nt_core = malloc(length*sizeof(int));
   new->nt_clamp = malloc(length*sizeof(int));
   new->nt_common = malloc(length*sizeof(int));
   new->nt_max = malloc(length*sizeof(int));

   return(new);
}  /* end of new_dna_matrix */
/*====================================================================
	De-allocate memory for a new DNA PSSM
=====================================================================*/
void free_dna_matrix(dna_matrix)
struct dna_matrix *dna_matrix;
{
   free(dna_matrix->nt_max);
   free(dna_matrix->nt_common);
   free(dna_matrix->nt_clamp);
   free(dna_matrix->nt_core);
   free(dna_matrix->nsres);
   free(dna_matrix->nres);
   free(dna_matrix->max_weight);
   free(dna_matrix->weights[0]);
   free(dna_matrix);

}  /* end of free_dna_matrix */
/*=================================================================
	Reverse complement a DNA pssm
	complement offset = (DNA length-1) - offset
	forward aa# = (DNA length - 1 - complement_offset) / 3
===================================================================*/
struct dna_matrix *complement_pssm(pssm)
struct dna_matrix *pssm;
{
    struct dna_matrix *rev;
    int nuc, pos, revpos;

    rev = new_dna_matrix(pssm->length);
    rev->block = pssm->block;
    rev->strand = -1;

    for (pos=0; pos < rev->length; pos++)
    {
       revpos = pssm->length - pos - 1;
       for (nuc=0; nuc < 4; nuc++)
       { 
          if (nuc == nt_atob['A'])
              rev->weights[nuc][revpos] = pssm->weights[nt_atob['T']][pos];
          else if (nuc == nt_atob['T'])
              rev->weights[nuc][revpos] = pssm->weights[nt_atob['A']][pos];
          else if (nuc == nt_atob['C'])
              rev->weights[nuc][revpos] = pssm->weights[nt_atob['G']][pos];
          else if (nuc == nt_atob['G'])
              rev->weights[nuc][revpos] = pssm->weights[nt_atob['C']][pos];
       }
    }
    return(rev);
}  /*  end of complement_pssm */
/*==================================================================
      Find degenerate region & extend 
      Be sure pssm->length is >= CORE_MIN_LEN before calling this routine
      Core degenerate region is 11 nucleotides from 3' to 5', 
      followed by 21 nuc. clamp continuing 3' to 5' (left side of pssm
      is the 5' end, so have to read it backwards).

      For each DNA pssm, should pre-compute two arrays (in rgr array?)
	For each position: # diff. nucs
	For each position: consensus nuc
=====================================================================*/
void find_oligos(pssm)
struct dna_matrix *pssm;
{
   int pos, posmod3, len, pos1, i, new_core, core_len;
   int new_clump, clamp_len, noligo, nclump;
   double ln4, max_score, core_score;
   double a_count, t_count, g_count, c_count;
   double entropy, enthalpy;
   struct oligo_list *oligo_clump, *oligo, *best_oligo, *prev_oligo, *tmp_oligo;
   char dna[CORE_MAX_LEN + CLAMP_MAX_LEN];

   ln4 = log(4.0);
   max_score = log(Core_Degeneracy) / ln4;
   nclump = noligo = 0;
   /*    List of oligos in a clump */
   if (!Verbose) oligo_clump = make_oligo(0);

/*
   printf("Best oligo for %s", pssm->block->number);
   if (pssm->strand < 0) printf(" Complement\n");
   else printf("\n");
*/

   best_oligo = NULL;			/* Highest scoring oligo for PSSM */
   prev_oligo = NULL;			/* Previous oligo found for PSSM */
   /*  Temporary storage area for clamp temperate calculations  */
   tmp_oligo = make_oligo(CORE_MAX_LEN + CLAMP_MAX_LEN);

/*>>>>> Rose conditions:  
	  first TWO positions of core are conserved (not just one) ???
          core ends on codon boundary => core_last % 3 == 0 
          minus strand core region is always 12 long, not 11, and always
            starts on the 1st res of a codon => core_first % 3 == 2 
          plus strand core region can be 12 long if the 3rd res of the 1st
            codon is conserved (eg W) => core_first % 3 == 2,
            otherwise it is 11 long and starts on the 2nd res of a 
            codon => core_first % 3 == 1
<<<<<<<<<<*/
   if (pssm->length >= CORE_MIN_LEN)
   {
      /*   Go from 3' to 5'     */
      for (pos = pssm->length - 1; pos >= CORE_MIN_LEN; pos--)
      {
         posmod3 = pos % 3;
         /*   Start core region with a conserved column; optionally
              apply core strictness to determine this */
         if ( ( Begin100 && pssm->nres[pos] == 1) ||
              (!Begin100 && pssm->nsres[pos] == 1)  )
         { 
            if (!Rose ||
                (Rose && 
                 ( (pssm->strand < 0 && posmod3 == 2)   ||
                   (pssm->strand > 0 && posmod3 >  0)  )  )   )
            {
               /* nt_max contains residue with max weight   */
               dna[0] = nt_btoa[ pssm->nt_max[pos] ];	/* 1st position */
               dna[1] = '\0';
               pos1 = pos - 1;  len = 1;

               /*  Core length is pre-determined:
                   Forward: If core starts on a codon boundary, 
			make it 12 long, otherwise it will be 11 long 
		   Complement: If starts on codon boundary 11 else 12
	       */
               core_len = CORE_MIN_LEN;
               if (pssm->strand > 0 && posmod3 == 2) core_len = CORE_MAX_LEN;
	       if (pssm->strand < 0 && posmod3 == 2 && Rose)
			 core_len = CORE_MAX_LEN;
	       if (pssm->strand < 0 && posmod3 != 2) core_len = CORE_MAX_LEN;
               
               core_score = 0.0; 
               /* Try to extend the core region to its predetermined length */
               while (pos1 >= 0 && core_score <= max_score &&
                      len < core_len)
               {
                  core_score += log( (double) pssm->nsres[pos1]) / ln4 ;
                  dna[len] = nt_adegen[ pssm->nt_core[pos1] ].residue;
                  pos1--; len++;
               }  /* end of while pos1 */

               dna[len] = '\0';

               if (Debug) printf("end:   pos=%d, len=%d, core_score=%.1f\n",
                       pos, len, core_score);
               if ( (len == core_len) && (core_score <= max_score) )
               {
                  if (Debug)
                     printf("oligo start: pos=%d, len=%d, core_score=%.1f,",
                       pos, len, core_score);

                  /* For Rose f this core region starts on the same amino acid as for the
                     previous oligo, then don't save this one */
                  if (Rose && prev_oligo != NULL &&
                      (prev_oligo->core_first / 3) ==  (pos / 3) )
                            new_core = FALSE;
                  else      new_core = TRUE;

/*>>> Following code only reports core if min clamp length fits in block
                  if ( new_core && (len + CLAMP_MIN_LEN) <= pos + 1)
<<<<<*/
                  if ( new_core )
                  {
                     /*  Use temporary oligo space for temperate calcs */
                     tmp_oligo->pssm = pssm;
                     tmp_oligo->strand = pssm->strand;
		     tmp_oligo->core_first = pos;			/* 3'end */
		     tmp_oligo->core_last = pos - core_len + 1;	/* 5'end */
                     tmp_oligo->core_score = core_score;

                     /*   Now add the clamp region  */
                     /* NOTE: clamp is built from 3' to 5', but NN temperature
                         calc has to be done from 5' to 3' */
                     tmp_oligo->clamp_temp = tmp_oligo->clamp_temp_nn = 0.0;
                     a_count = t_count = g_count = c_count = 0.0;
                     clamp_len = 0; 

                     /*-----------------------------------------------------*/
                     while ( pos1 >= 0 && len < core_len + CLAMP_MAX_LEN &&
                             tmp_oligo->clamp_temp_nn < Clamp_Temperature )
                     {
			i = pssm->nt_clamp[pos1];
                        dna[len] = nt_adegen[ i ].residue;

                        /*  update temperature stuff */
                        if ( dna[len] == 'A' )      a_count += 1.0;
                        else if ( dna[len] == 'T' ) t_count += 1.0;
                        else if ( dna[len] == 'G' ) g_count += 1.0;
                        else if ( dna[len] == 'C' ) c_count += 1.0;

                        if (clamp_len > 0)  /* need at least 2 for temp calc */
                        {
                           tmp_oligo->clamp_first = tmp_oligo->core_last - 1; /* 3'end */
                           tmp_oligo->clamp_last = pos1;	/* 5'end */

                           tmp_oligo->clamp_temp = 
                               cltemp((a_count+t_count), (g_count+c_count));
                           /*  Calc entropy & enthalpy from 5' to 3'*/
                           compute_thermo(tmp_oligo, &entropy, &enthalpy);
                           /*  Get a nearest neighbor temp */
                           tmp_oligo->clamp_temp_nn = clamp_rychlik(entropy, enthalpy, 
                                    (g_count/clamp_len), (c_count/clamp_len));
                        }
                        pos1--; len++; clamp_len++;
                     }  /* end of while pos1 */
                     /*-----------------------------------------------------*/

                     dna[len] = '\0';

                     if (Debug) printf("\n last pos=%d, core score=%.2f %s\n", 
                             (pos1 + 1), core_score, dna );

                     oligo = make_oligo(pos - pos1 + 1);

                     oligo->pssm = pssm;
                     oligo->strand = pssm->strand;
		     oligo->core_first = pos;			/* 3'end */
		     oligo->core_last = pos - core_len + 1;	/* 5'end */
                     oligo->core_score = core_score;
                     oligo->clamp_first = oligo->core_last - 1; /* 3'end */
                     oligo->clamp_last = pos1 + 1;		/* 5'end */

                     /* >>> compute_thermo(), etc. give same
			results as above for tmp_oligo - why do it again? <<< */
                     oligo->clamp_temp = 
                         cltemp((a_count+t_count), (g_count+c_count));
                     /*  Recalc entropy & enthalpy from 5' to 3'*/
                     compute_thermo(oligo, &entropy, &enthalpy);
                     /*  Get a nearest neighbor temp */
                     oligo->clamp_temp_nn = clamp_rychlik(entropy, enthalpy, 
                                    (g_count/clamp_len), (c_count/clamp_len));

		     /* NOTE: assumes Core_Strictness <= Clamp_Strictness */
                     score_clamp(oligo, Core_Strictness, Clamp_Strictness);
		     score_clamp_prob(oligo);

                     /*  Clump stuff only needs to be done if !Verbose  */
                     if (!Verbose)
                     {
		        new_clump = get_clump(oligo, prev_oligo);
                        if (new_clump)
                        {
		   	   display_oligos(oligo_clump);
                           oligo_clump->next_oligo = NULL;
                           nclump++; noligo = 0;
                        }
		        oligo->clump = nclump;
                        noligo++;
     		        oligo->number = noligo;
                        insert_oligo(oligo_clump, oligo);
                     }
                     else { display_oligo(oligo); }

                     prev_oligo = oligo;		/* save previous */
		     /*  Update best */
                     /*  If same total score, check for best core */
                     if ( best_oligo == NULL ||
                          (best_oligo != NULL &&
                           ((oligo->core_score < best_oligo->core_score) ||
                            (oligo->core_score == best_oligo->core_score &&
                             oligo->clamp_prob > best_oligo->clamp_prob))
                           )
                         )
                               best_oligo = oligo;
                  }  /* end of a good oligo new_core */
               }   /* end of a good core + room for extension */
            } /*  end of conserved pos */
         }  /* end of Rose check */
      } /* end of pos */

      if (best_oligo == NULL) printf("No suggested primers found.\n");
      else if (!Verbose)  /*  Print last clump */
           {
              display_oligos(oligo_clump);
              oligo_clump->next_oligo = NULL;
           }
   }
   else
   {
        printf("Block is too narrow\n");
   }

   free(tmp_oligo);
   if (!Verbose) free(oligo_clump);

}  /* end of find_oligos */
/*=======================================================================
	len isn't used here ... why pass it?
========================================================================*/
struct oligo_list *make_oligo(len)
int len;
{
   struct oligo_list *new;

   new = (struct oligo_list *) malloc(sizeof(struct oligo_list));

   new->core_first = new->core_last = new->clamp_first = new->clamp_last = 0;
   new->core_score = new->clamp_score = new->clamp_prob = 0.0;
   new->clamp_temp = new->clamp_temp_nn = 0.0;
   new->pssm = NULL;
   new->strand = new->out_order = 0;
   new->number = new->clump = -1;
   new->next_oligo = NULL;
   new->pssm = NULL;

   return (new);
}  /* end of make_oligo */
/*=======================================================================
	Insert oligo into a sorted list, sorted by
        clump and number.
        Lower key is better, so complements get sorted first
        First oligo in list isn't used
========================================================================*/
void insert_oligo(list, new)
struct oligo_list *list, *new;
{
   struct oligo_list *cur;
   double curkey, newkey;
   int done;

/*
   newkey = (double) new->strand * (new->core_score + new->clamp_prob);
   newkey = (double) new->core_score;
*/
   /*  Assumes never more than 100 clumps per block */
   newkey = (double) 100 * new->clump + new->number;

   cur = list;		/* first element in list is a dummy */

   done = FALSE;
   while (!done && cur->next_oligo != NULL)
   {
/*
      curkey = (double) cur->strand * (cur->core_score + cur->clamp_prob);
      curkey = (double) cur->next_oligo->core_score;
*/
      curkey = (double) 100 * cur->next_oligo->clump + cur->next_oligo->number;
      if (newkey < curkey)
      {
         new->next_oligo = cur->next_oligo;
         cur->next_oligo = new;
         done = TRUE;
      }
      cur = cur->next_oligo;
   }
   if (!done) cur->next_oligo = new;

   
}  /* end of insert_oligo */
/*===================================================================
      Compute max residue & consensus residues for each column of
      a DNA PSSM: 
       maxaa = highest scoring res in position of AA pssm
       nt_core[] = highest scoring res in DNA pssm (degenerate, for core)
       nt_common[] = most common codon from maxaa (nondegenerate, for clamp)
       nt_max[] = highest scoring res in DNA pssm (degenerate, for clamp)
       nt_clamp[] = either nt_common[] or nt_max[]
       nres[] = actual number of residues in position
       nsres[] = number of residues using strictness
       max_weight[] = max nuc weight in position (used in score_clamp() )
===================================================================*/
void compute_max(dna_pssm, aa_pssm)
struct dna_matrix *dna_pssm;
Matrix *aa_pssm;
{
   int pos, res, dna_res[3], i, ic, aapos, maxaa, maxcod;
   char ctemp[6];
   double maxwt, dtemp;

   for (pos = 0; pos < dna_pssm->length; pos++)
   {
         /*-------------------------------------------------------------*/
         /*  Get the codon corresponding to the consensus amino acid  &
             save it for display in the non-degenerate (clamp) region */
         if ( (pos%3) == 0 )
         {
             aapos = pos / 3;
             if (dna_pssm->strand < 0) aapos = aa_pssm->width - 1 - aapos;
             /*   Get consensus aa from amino acid PSSM */
             maxwt = 0.0; maxaa = -1;
             for (res=1; res <= 20; res++)
                if (aa_pssm->weights[res][aapos] > maxwt)
                {
                   maxwt = aa_pssm->weights[res][aapos];
                   maxaa = res;
                }
      
             /*   Get codon corresponding to maxaa
                  from codon usage table, put it in pos, pos+1, pos+2 ,
		  should be a better way to find all codons for an aa,
                  see aa_acodon[] in gcode.h */
             maxwt = 0.0; maxcod = -1;
             for (ic=0; ic < 64; ic++)
             {
/* >>>> Too severe? AAACTA should be okay if PolyX >= 3
  	      Don't consider AAA [0], CCC [21], GGG [42] or TTT [63] 
              Only affects most common codons for clamp, not clamp if
		taken from DNA PSSM; see fix_codons() 
                if ( (ic%21) > 0 &&
<<<< */
                if (Gcode[ic] == maxaa && Codon_Usage[ic] > maxwt)
                {
                   maxwt = Codon_Usage[ic]; maxcod = ic;
                }
             }
             if (maxcod >= 0 && maxcod < 64)
             {
                dna_pssm->nt_common[pos]   = nt_atob[ Codons[maxcod][0] ];
                dna_pssm->nt_common[pos+1] = nt_atob[ Codons[maxcod][1] ];
                dna_pssm->nt_common[pos+2] = nt_atob[ Codons[maxcod][2] ];
                if (dna_pssm->strand < 0)	/* complement */
                {
                   for (i=0; i< 3; i++)  /* save the forward values */
                   {
                      dna_res[i] = dna_pssm->nt_common[i+pos];
                   }
                   for (i=0; i< 3; i++)  /* reverse & complement */
                   {
                      if (dna_res[i] == nt_atob['A'])
                       dna_pssm->nt_common[2-i+pos] = nt_atob['T'];
                      else if (dna_res[i] == nt_atob['T'])
                       dna_pssm->nt_common[2-i+pos] = nt_atob['A'];
                      else if (dna_res[i] == nt_atob['C'])
                       dna_pssm->nt_common[2-i+pos] = nt_atob['G'];
                      else if (dna_res[i] == nt_atob['G'])
                       dna_pssm->nt_common[2-i+pos] = nt_atob['C'];
                   }
                }
             }
             
         }  /*  end of amino acid */

         /*-------------------------------------------------------------*/
         /*  Get the number of residues in this col of the DNA pssm &
             the max weight in each col, which will be used later to
             apply the strictness criteria */
         /*  Also puts  all the residues with the max weight in ctemp,
             but these aren't currently used */
         dna_pssm->nres[pos] = dna_pssm->nsres[pos] = 0;
         dna_pssm->max_weight[pos] = 0.0;
         for (res=0; res < 4; res++)
         {
            if (dna_pssm->weights[res][pos] > 0.0)
            {
                dna_pssm->nres[pos] += 1;
                if (dna_pssm->weights[res][pos] > dna_pssm->max_weight[pos])
                {
                   dna_pssm->max_weight[pos] = dna_pssm->weights[res][pos];
                   ctemp[0] = nt_btoa[res];
                   ctemp[1] = '\0';
                   ic = 1;
                }
                else if (dna_pssm->weights[res][pos] ==
                         dna_pssm->max_weight[pos])
                { 
                   ctemp[ic++]=  nt_btoa[res];
                   ctemp[ic] = '\0';
                }
             }
         }     /* end of for res */

         /*-------------------------------------------------------------*/
         /*   Get the degenerate residue for the core region */
         /*  ctemp contains all the residues with weight/max > Core_Strictness */
         ctemp[0] = '\0'; ic = 0;
         for (res=0; res < 4; res++)
         {
            dtemp = dna_pssm->weights[res][pos] / dna_pssm->max_weight[pos];
            if ( (Core_Strictness  < 1.0 && dtemp  > Core_Strictness) ||
                 (Core_Strictness == 1.0 && dtemp >= Core_Strictness) )
            {
               ctemp[ic++] = nt_btoa[res]; ctemp[ic] = '\0';
            }
         }
         dna_pssm->nsres[pos] = (int) strlen(ctemp);
         ic = 0;
         while (ic < 16 &&
                         (strcmp(ctemp, nt_adegen[ic].list) != 0)) ic++;
                  dna_pssm->nt_core[pos] = ic;   /* nt_adegen[ic].residue */

         /*-------------------------------------------------------------*/
         /*   Get the nuc with max weight from the PSSM */
         /*  ctemp contains the residues with weight/max > Clamp_Strictness */
         ctemp[0] = '\0'; ic = 0;
         for (res=0; res < 4; res++)
         {
            dtemp = dna_pssm->weights[res][pos] / dna_pssm->max_weight[pos];
            if ( (Clamp_Strictness  < 1.0 && dtemp >  Clamp_Strictness) ||
                 (Clamp_Strictness == 1.0 && dtemp >= Clamp_Strictness) )
            {
               ctemp[ic++] = nt_btoa[res]; ctemp[ic] = '\0';
            }
         }
         /*  Pick a residue a random */
         i = (rand() >> 3) % strlen(ctemp);
         dna_pssm->nt_max[pos] = nt_atob[ ctemp[i] ];

/*>>>>>>>>> Don't want degen residues in the clamp: 
         ic = 0;
         while (ic < 16 &&
                         (strcmp(ctemp, nt_adegen[ic].list) != 0)) ic++;
                  dna_pssm->nt_max[pos] = ic;  
<<<<<<*/

         /*---------------------------------------------------------------*/
         /*   Now set up the clamp depending on what the user wants there */
         if (Most_Common_Codon)
         { dna_pssm->nt_clamp[pos] = dna_pssm->nt_common[pos];}
         else                  
         { dna_pssm->nt_clamp[pos] = dna_pssm->nt_max[pos];   }
   }  /* end of pos */


   /*   Edit nt_clamp[] and break up runs  */
   break_runs(dna_pssm);

}  /* end of compute_max */

/*=============================================================================
     First oligo is the list isn't used
     Scan through all oligos for a clump & flag the best three for printing
=============================================================================*/
void display_oligos(oligos)
struct oligo_list *oligos;
{
   struct oligo_list *cur;
   struct sort_array *stemp;
   int i, noligo;

   noligo = 0;

   /*   Count the number of oligos */
   noligo = 0;
   cur = oligos;
   while (cur->next_oligo != NULL)
   {  
      noligo++;
      cur = cur->next_oligo;
   }

   /*  Make an array for sorting */
   stemp = (struct sort_array *) malloc(noligo * sizeof(struct sort_array));
   noligo = 0;
   cur = oligos;
   while (cur->next_oligo != NULL)
   {  
      stemp[noligo].oligo = cur->next_oligo;
      stemp[noligo].clump = cur->next_oligo->clump;
      stemp[noligo].number = cur->next_oligo->number;
      stemp[noligo].core_score = cur->next_oligo->core_score;
      stemp[noligo].clamp_score = cur->next_oligo->clamp_score;
      noligo++;
      cur = cur->next_oligo;
   }

   /*  Sort by clump and core_score  */
   qsort(stemp, noligo, sizeof(struct sort_array), oligocmp);

   /*  Fill in output order - should do something about ties */
   for (i=0; i<noligo; i++)
   {
      stemp[i].oligo->out_order = i;
   }
   /*  Print out all oligos with same score as last one  */
   for (i=OutOligo; i< noligo; i++)
   {
      if (stemp[i].core_score == stemp[OutOligo-1].core_score)
 		stemp[i].oligo->out_order = OutOligo-1;
   }

   cur = oligos;
   while (cur->next_oligo != NULL)
   {  
      cur = cur->next_oligo;
      if (cur->out_order < OutOligo) display_oligo(cur);
   }

   free(stemp);
}   /*  end of display_oligos  */

/*=============================================================================
  Sample data: Forward: core_first=31, core_last=21, clamp_first=20, _last= 0
               Comple (cpos)       65            55              54        34
               pos                 15            25              26        46
=============================================================================*/
void display_oligo(oligo)
struct oligo_list *oligo;
{
   int pos, cpos, res;

   if (Debug)
   {
      printf("%s", oligo->pssm->block->number);
      if (oligo->strand < 0) printf(" Complement\n");
      else printf("\n");
   }

   /*  Print the oligo */
   for (pos=0; pos < oligo->pssm->length; pos++)
   {
      if (oligo->strand > 0)  /* Plus is printed 5' to 3' */
      {
          /*  Use degenerate codon in the core & non-degen in the clamp */
          if (pos < oligo->clamp_last) { printf(" "); }
          else if (pos >= oligo->clamp_last && pos <= oligo->clamp_first)
          {
              res = oligo->pssm->nt_clamp[pos];
              printf("%c", nt_btoa[res]);
          }
          else if (pos >= oligo->core_last && pos <= oligo->core_first)
          {  res = oligo->pssm->nt_core[pos];
             printf("%c", tolower( nt_btoa[res]) ); }
      }
      else     	/*  complement  is printed 3' to 5' */
      {
          cpos = oligo->pssm->length - 1 - pos;    /* complement position */
          if (cpos > oligo->core_first) { printf(" "); }
          else if (cpos >= oligo->clamp_last && cpos <= oligo->clamp_first)
          {
              res = oligo->pssm->nt_clamp[cpos];
              printf("%c", nt_btoa[res]);
          }
          else if (cpos >= oligo->core_last && cpos <= oligo->core_first)
          { res = oligo->pssm->nt_core[cpos];
            printf("%c", tolower( nt_btoa[res]) ); }
      }
   }   /* end of pos */

/*
   if (oligo->strand > 0) printf(" -3\'  %.2f + %.2f = %.2f", 
           oligo->core_score, oligo->clamp_prob, 
          (oligo->core_score + oligo->clamp_prob) );
   else  printf(" -5\'  %.2f + %.2f = %.2f", 
           oligo->core_score, oligo->clamp_prob, 
          (oligo->core_score + oligo->clamp_prob) );
*/
   if (oligo->strand > 0) printf(" -3\'  "); 
   else                   printf(" -5\'  ");
   printf("Core: ");
   printf("degen=%.0f", pow(4.0, oligo->core_score));
   printf(" len=%d  Clamp: score=%d, len=%d temp=% .1f",
           (oligo->core_first - oligo->core_last + 1),
            round(oligo->clamp_prob),
           (oligo->clamp_first - oligo->clamp_last + 1),
            oligo->clamp_temp_nn );
   if (oligo->clamp_temp_nn < Clamp_Temperature)
      printf(" *** CLAMP NEEDS EXTENSION\n");
   else printf("\n");
   

}  /* end of display_oligo  */
/*=======================================================================
    Scoring an oligo:
	strictness parameters = a number between 0 and 1
         0.0 is most degenerate, count all nucs in position
         1.0 is least degenerate, count only highest nuc in position
	 0 => position score == # diff nucs in position
	>0 => position score == # diff nucs in position with
				weight >= max weight
	If strict_low != strict_high, strictness is varied across
	clamp, from strict_low at the start (next to the core) to strict_high
=======================================================================*/
void score_clamp(oligo, strict_low, strict_high)
struct oligo_list *oligo;
double strict_low, strict_high;
{
   int pos, res, clamp_len, nres;
   double ln4, strict, strict_inc, dtemp;

   ln4 = log(4.0);
   clamp_len = oligo->clamp_first - oligo->clamp_last;
   strict_inc = (strict_high - strict_low) / clamp_len;

   oligo->clamp_score = 0.0;
   for (pos = oligo->clamp_last, strict = strict_high;
        pos <= oligo->clamp_first; pos++, strict -= strict_inc)
   {
      nres = 0;
      for (res=0; res < 4; res++)
      {
         /*   Strictness of 0.0 is handled separately */
         dtemp = oligo->pssm->weights[res][pos] / oligo->pssm->max_weight[pos];
         if ( (strict >  0.0 && dtemp >= strict) ||
              (strict <= 0.0 && dtemp >  strict) )
                  nres++;
      }
      oligo->clamp_score += log((double) nres) / ln4;
   }

}  /*  end of score_clamp */
/*=======================================================================
   Clamp probability score: (really isn't a probability ...)
       Weighted average of max weight in DNA PSSM columns.
       Weight is distance from core/clamp boundary: (n+1-i)/n where n
	is the clamp length.
       Total weight = sum(i=1,n){(n+1-i)/n} = sum(i=1,n){i/n} = n(n+1)/2n
		= (n+1)/2
  Sample oligos:
	strand	core_first core_last clamp_first clamp_last
	1	91		81	80		65
	-1	46		36	35		15
=======================================================================*/
void score_clamp_prob(oligo)
struct oligo_list *oligo;
{
   int pos, res, clamp_len, ipos;
   double dtemp, maxwt;

   clamp_len = oligo->clamp_first - oligo->clamp_last + 1;

   oligo->clamp_prob = 0.0;
   ipos = 0;
   for (pos = oligo->clamp_first; pos >= oligo->clamp_last; pos--)
   {
      maxwt = 0.0;
      for (res=0; res < 4; res++)
      {
         if (oligo->pssm->weights[res][pos] > maxwt)
             maxwt = oligo->pssm->weights[res][pos];
      }
      ipos++;  /* ipos is distance away from core/clamp boundary */
      dtemp =  ((double) clamp_len + 1 - ipos) / ((double) clamp_len);
/*
      oligo->clamp_prob += ( dtemp * log(maxwt / 100.0) );
*/
      oligo->clamp_prob += ( dtemp * maxwt );
   }
   dtemp = ((double) (clamp_len + 1)) / 2.0 ;	/* total weight */
   oligo->clamp_prob /= dtemp;

}  /*  end of score_clamp_prob */
/*=====================================================================*/
void display_consensus(dna_pssm)
struct dna_matrix *dna_pssm;
{
   int pos, res;

   /*   Print the consensus amino acid */
   if (dna_pssm->strand > 0)
   {
      for (pos=0; pos < dna_pssm->length; pos++)
      {
          if ((pos%3) == 2 )
          {
             res = codon2aa(Gcode, dna_pssm->nt_common[pos-2], 
		dna_pssm->nt_common[pos-1], dna_pssm->nt_common[pos]);
             printf("%c  ", aa_btoa[res]);
          }
      }
   }
   printf("\n");

}  /* end of display_consensus */
/*=================================================================
     Determine whether an oligo belongs in a new clump 
     It does if its core region doesn't overlap with the
     previous oligo. core_first is always > core_last:
   
	same clump:    old_last = 104	 old_first = 114
		    new_last = 102    new_first = 112
        different clump:
new_last = 90    new_first = 100
=================================================================*/
int get_clump(new_oligo, old_oligo)
struct oligo_list *new_oligo, *old_oligo;
{
   if (old_oligo == NULL || new_oligo == NULL ||
       new_oligo->core_first < old_oligo->core_last)
	return TRUE;
   else	return FALSE;

}   /*  end of get_clump */
/*=================================================================
    Equation [2] from Freier, et al, PNAS 83:9373-9377
=================================================================*/
double clamp_freier(entropy, enthalpy)
double entropy, enthalpy;
{
   double tm, top, bot;

   top = 0.0 - 1000.0 * enthalpy;
   /*   Assumes concentration is given in nM units  */
   bot = 1.987 * (log(Concentration/4.0) + log(0.000000001));
   bot -= entropy;
   if (bot < 0.0)
      tm = top / bot - 273.15;
   else
      tm = 0.0;

   return(tm);
}  /* end of clamp_freier */
/*=================================================================
    Equation [] from Rychlik, et al (1990), NAR 18:6409
	Ta = 0.3Tm_primer + 0.7Tm_product - 14.9
=================================================================*/
double clamp_rychlik(entropy, enthalpy, g_percent, c_percent)
double entropy, enthalpy, g_percent, c_percent;
{
   double tm_primer, salt;

   /*  Assumes Koncentration is in mM = 10-3 */
   salt = 16.6 * ( log10(Koncentration) + log10(0.001) );
   tm_primer = clamp_freier(entropy, enthalpy) + salt;

/* NOTE: tm_prod & t_a give bad temps ! Aren't used here 
   tm_prod = 0.41 * (g_percent + c_percent) + salt - 675 / ProdLen;
   ta = 0.3 * tm_primer + 0.7 * tm_prod - 14.9;
*/

   return(tm_primer);
}  /* end of clamp_rychlik */
/*=================================================================
   Compute entropy & enthalpy for an oligo clamp
	clamp_last is the 5' end, clamp_first is the 3' end
	Compute from the 5' to the 3' end
    5' clamp_last, clamp_first, core_last, core_first 3'
=================================================================*/
void compute_thermo(oligo, entropy, enthalpy)
struct oligo_list  *oligo;
double *entropy, *enthalpy;
{
   int pos, prev_res, res, bi, done;
   int firstpos, lastpos;

   res = prev_res = -99;
   *entropy = 10.8;  *enthalpy = 0.0;
   done = FALSE;
   firstpos = oligo->clamp_last; lastpos = oligo->core_first;
   for (pos=firstpos; pos <= lastpos; pos++)
   {
      /*  pos is the actual position in the array */
      if (pos >= oligo->clamp_last && pos <= oligo->clamp_first)
      {
          res = oligo->pssm->nt_clamp[pos];
          /* Be careful: nt_clamp[] could be a degenerate residue! */
          if ( (prev_res >= 0 && prev_res < 4) && 
               (res >= 0 && res < 4) )
          {
             bi = prev_res * 4 + res;
             *entropy += Bres_Entropy[bi]; 
             *enthalpy += Bres_Enthalpy[bi];
          }
          else if ( (prev_res >= 0 && prev_res < 16) && 
               (res >= 0 && res < 16) )
          {
             *entropy += 20.3;			/* average values */
             *enthalpy += 8.1;
          }
          prev_res = res;
      }
      /*  Continue into the core region until a degenerate position */
      if (pos >= oligo->core_last && !done)
      {
          if (oligo->pssm->nsres[pos] > 1) { done = TRUE; }
          else
          {
             res = oligo->pssm->nt_clamp[pos];
             if (prev_res >= 0 && prev_res < 4) 
             {
                bi = prev_res * 4 + res;
                *entropy += Bres_Entropy[bi]; 
                *enthalpy += Bres_Enthalpy[bi];
             }
             prev_res = res;
          }
      }
   }  
}   /* end of compute_thermo */

/*=====================================================================
    Reads along nt_clamp[].
	When max number of nuc of any type is passed, breaks the
	run by replacing the lowest scoring member of the run with the
	next highest value from the DNA PSSM, even if the
	clamp is made from the most common codons.
=======================================================================*/
void break_runs(pssm)
struct dna_matrix *pssm;
{
   int pos, firstpos, lastpos, prev_res, res, i, done, nrun, minpos, maxres;
   double minwt, maxwt;

   res = prev_res = -99;
   done = FALSE;
   firstpos = 0; lastpos = pssm->length;
   pos = firstpos; nrun = 1;
   while (!done && pos < lastpos)
   {
      /*  pos is the actual position in the array */
      res = pssm->nt_clamp[pos];
      /* Be careful: nt_clamp[] could be a degenerate residue! */
      if (res >= 0 && res < 4) 
      {
         if (res == prev_res) nrun++;
         else                 nrun = 1;
      }
      prev_res = res;
      if (nrun > PolyX)
      {
         /* Find the position with the lowest weight for the res */
         minpos = pos-nrun+1;
         minwt = pssm->weights[res][minpos];
         for (i= pos-nrun+2; i < pos; i++)
         {
            if (pssm->weights[res][i] < minwt)
            {
               minpos = i;
               minwt = pssm->weights[res][i];
            }
         }
         /* Find the next highest weight and use it instead */
/*>>>>> What if nres = 1 in this position???   */
         maxwt = -99.9;
         maxres = -1;
         for (i=0; i<4; i++)
         {
            if (i != res && pssm->weights[i][minpos] > maxwt)
            {
               maxwt = pssm->weights[i][minpos];
               maxres = i;
            }
         }
         pssm->nt_clamp[minpos] = maxres;
         /*  Go back to the beginning of the run */
         pos = pos-nrun; nrun = 1;
      } /* end of a run */
      pos++;
   }  
}  /* end of break_runs() */

/*=====================================================================
     Revise the codon usage table: As read, the 64 usages add up to 1.0,
	want the entries for the codons for each AA to add up to 1.0
     Assumes the global table Codon_Usage[64] has been loaded
=======================================================================*/
void fix_codons()
{
   int aa, ic;
   double totaa;

   for (aa=1; aa<=20; aa++)
   {
      totaa = 0.0;
      for (ic=0; ic < 64; ic++)
         if (Gcode[ic] == aa)  totaa += Codon_Usage[ic];
      for (ic=0; ic < 64; ic++)
         if (Gcode[ic] == aa)  Codon_Usage[ic] /= totaa;
   }

   /*    Set AAA, CCC, GGG & TTT to zero so they are never used in clamp */
/*  Affects values in DNA PSSM as well as in clamp here
   Codon_Usage[0] = Codon_Usage[21] = Codon_Usage[42] = Codon_Usage[63] = 0.0;
*/

}  /* end of fix_codons */

/*=====================================================================
 * fprint_matrix1
 *
 *  Prints a Matrix data structure to a file in a generic format. 
 *  adapted from fprint_matrix
 *
 *   Parameters: 
 *     Matrix *matrix:   the matrix to print
 *     FILE   *omfp:     the output matrix file pointer
 *     char   *alphabet: the printed matrix residues and their order
 *   Error Codes: none
 *
 */

void fprint_matrix1(matrix,omfp,alphabet)
     struct dna_matrix *matrix;
     FILE *omfp;
     char *alphabet ;
{
  int  pos, low, high, matrix_print_width ;
  char *ptr ;


  if (WWW_FLAG)
     matrix_print_width = WWW_MATRIX_PRINT_WIDTH ;
  else
     matrix_print_width = SHL_MATRIX_PRINT_WIDTH ;

  high = matrix_print_width;
  for (low=0; 
       low<high && low<matrix->length; 
       low = (high+=matrix_print_width) - matrix_print_width) {

  fprintf(omfp,"\n%s", matrix->block->number);
  if (matrix->strand < 0) fprintf(omfp, " Complement\n");
  else fprintf(omfp, "\n");

  if (matrix->length > 99) {
    fprintf(omfp,"  |");
    for (pos=low; pos<high &&  pos<matrix->length; pos++) {
      if (pos+1 > 99) {
	fprintf(omfp,"% 4d", (pos+1-(((pos+1)/1000)*1000)) / 100);
      }
      else {
	fprintf(omfp,"    ");
      }
    }
  fprintf(omfp,"\n");
  }

  if (matrix->length > 9) {
    fprintf(omfp,"  |");
    for (pos=low; pos<high &&  pos<matrix->length; pos++) {
      if (pos+1 > 9) {
	fprintf(omfp,"% 4d", (pos+1-(((pos+1)/100)*100)) / 10);
      }
      else {
	fprintf(omfp,"    ");
      }
    }
  fprintf(omfp,"\n");
  }
  
  fprintf(omfp,"  |");
  for (pos=low; pos<high &&  pos<matrix->length; pos++) {
    fprintf(omfp,"%4d", ((pos-((pos/10)*10))+1)%10);
  }
  
  fprintf(omfp,"\n");
  fprintf(omfp,"--+");
  for (pos=low; pos<high &&  pos<matrix->length; pos++) {
    fprintf(omfp,"----");
  }

  fprintf(omfp,"\n");

  for (ptr=alphabet; *ptr != '\0'; ptr++)
      {
      fprintf(omfp,"%c |", *ptr);
      for (pos=low; pos<high &&  pos<matrix->length; pos++) 
          {
          fprintf(omfp,"% 4d", 
                  round(matrix->weights[nt_atob[*ptr]][pos]));
          }
      fprintf(omfp,"\n");
      }

  }

}  /* end of fprint_matrix1 */

/*====================================================================
 * fprint_matrix1_3spaced
 *   Prints a Matrix data structure to a file.
 *  Positions are spaced to be 2 empty positions apart so
 *  that if the matrix positions will be aligned with the positions in 
 *  nucleotide derived matrices
 *
 *  Adapted from fprint_matrix1 and fprint_matrix_3spaced
 *
 *   Parameters: 
 *     Matrix *matrix:  the matrix to print
 *     FILE   *omfp:   the output matrix file pointer
 *     char   *alphabet: the printed matrix residues and their order
 *   Error Codes: none
 */

void fprint_matrix1_3spaced(matrix,omfp,alphabet)
     struct dna_matrix *matrix;
     FILE *omfp;
     char *alphabet ;
{
  int pos;
  int low, high;
  char *ptr;

  int matrix_print_width ;

  if (WWW_FLAG)
     matrix_print_width = WWW_MATRIX_PRINT_WIDTH ;
  else
     matrix_print_width = SHL_MATRIX_PRINT_WIDTH ;

/* divide print width by 3 since it is specified in actual positions
   and in this procedure each position takes the printed space of 3 */
  matrix_print_width = floor(matrix_print_width/3) ;

  high = matrix_print_width;
  for (low=0; 
       low<high && low<matrix->length; 
       low = (high+=matrix_print_width) - matrix_print_width) {

  fprintf(omfp,"\n");

/* modified following statements to 
  print matrix column number starting from 1 not 0 and properly show numbers >99 */

  fprintf(omfp,"%s\n", matrix->block->number);

  if (matrix->length > 99) {
    fprintf(omfp,"  |");
    for (pos=low; pos<high &&  pos<matrix->length; pos++) {
      if (pos+1 > 99) {
	fprintf(omfp,"% 4d", (pos+1-(((pos+1)/1000)*1000)) / 100);
      }
      else {
	fprintf(omfp,"    ");
      }
    }
  fprintf(omfp,"\n");
  }

  if (matrix->length > 9) {
    fprintf(omfp,"  |");
    for (pos=low; pos<high &&  pos<matrix->length; pos++) {
      if (pos+1 > 9) {
	fprintf(omfp,"% 4d        ", (pos+1-(((pos+1)/100)*100)) / 10);
      }
      else {
	fprintf(omfp,"            ");
      }
    }
  fprintf(omfp,"\n");
  }
  
  fprintf(omfp,"  |");
  for (pos=low; pos<high &&  pos<matrix->length; pos++) {
    fprintf(omfp,"%4d        ", ((pos-((pos/10)*10))+1)%10);
  }
  
  fprintf(omfp,"\n");
  fprintf(omfp,"--+");
  for (pos=low; pos<high &&  pos<matrix->length; pos++) {
    fprintf(omfp,"------------");
  }

  fprintf(omfp,"\n");
  

  for (ptr=alphabet; *ptr != '\0'; ptr++)
      {
      fprintf(omfp,"%c |", *ptr);
      for (pos=low; pos<high &&  pos<matrix->length; pos++) 
          {
          fprintf(omfp,"% 4d        ", 
                  round(matrix->weights[nt_atob[*ptr]][pos]));
          }
      fprintf(omfp,"\n");
      }

  }

} /*  end of fprint_matrix1_3spaced */
/*=========================================================================
	Sort by clump, core_score & clamp_score (D)
=========================================================================*/
int oligocmp(s1, s2)
struct sort_array *s1, *s2;
{
   if (s1->clump != s2->clump) return(s1->clump - s2->clump);
   /*  core_score is double, smaller is better */
   else if (s1->core_score > s2->core_score) return(1);
   else if (s1->core_score < s2->core_score) return(-1);
   /*  clamp_score is double, bigger is better */
   else if (s2->clamp_score > s1->clamp_score) return(1);
   else if (s2->clamp_score < s1->clamp_score) return(-1);
   else return(0);
}  /* end of oligocmp  */
