/* COPYRIGHT 1997 Fred Hutchinson Cancer Research Center, Seattle, WA, USA
   oligo_melt.c:  Calculate various melting point temperatures for an oligo.
     oligo_melt <oligo_sequence> <DNA concentration in nM>
==========================================================================
12/30/97 1.
 1/13/98 1. Print reverse.
 3/28/98 1. Don't print reverse. References.
 11/20/99 1. Added SantaLucia
*/

#define EXTERN

#include <blocksprogs.h>

#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 suggs(a,g) (3.0 + 2.0 * a + 4.0 * g)

double freier();
double rychlik();
void compute_thermo();
void santalucia();
void print_reverse();

/*---------------------- Global variables ----------------------------*/
char Version[12] = "11/20/99.1";		/* Version date */

double Koncentration = 50;		/* salt in mM units */
double Concentration = 50;		/* DNA concentration in nM */
/* Primer3's Conc = 50nM while Nar's = 0.1nM */

/*  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 };
/*  SantaLucia, 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 SaLu_Enthalpy[16]  = {  7.9,  8.4,  7.8,  7.2,  8.5,  8.0, 10.6,  7.8,
                              8.2,  9.8,  8.0,  8.4,  7.2,  8.2,  8.5,  7.9 };
double SaLu_Entropy[16] = { 22.2, 22.4, 21.0, 20.4, 22.7, 19.9, 27.2, 21.0,
                             22.2, 24.4, 19.9, 22.4, 21.3, 22.2, 22.7, 22.2 };
/*=======================================================================*/
/*
 * main
 *   controls flow of program
 *   Parameters: argc, argv
 *   Error codes:
 */

int main(argc, argv)
     int argc;
     char *argv[];
{
   FILE  *inpf=stdin;
   char fname[80];
   Sequence *seq;
   int db_type, seq_type, num_at, num_gc;
   double entropy, enthalpy, salt, suggs_temp, nar_temp, primer3_temp;

   ErrorLevelReport = 2;		/* suppress BLIMPS errors */

   if (argc < 2)
   {
      printf("OLIGO_MELT %s\n", Version);
      printf("COPYRIGHT 1997, Fred Hutchinson Cancer Research");
      printf(" Center, Seattle, WA, USA\n");
      printf("\nUSAGE:  oligo_melt <oligo_sequence> [DNA conc in nM]\n");
   }

   /*--------------------------1st arg = sequence file ------------*/
   if (argc > 1) strcpy(fname, argv[1]);
   else
   {
      printf("Enter name of file containing oligos: ");
      gets(fname);
   }
   if (!strlen(fname) ) inpf = stdin;
   else if ((inpf=fopen(fname, "r")) == NULL)
   {
      printf("Cannot open %s\n", fname);
      exit(-1);
   }
   /*--------------------------2nd arg = concentration ------------*/
   if (argc > 2) Concentration = atof(argv[2]);

   /*  Assumes Koncentration is in mM = 10-3 */
   salt = 16.6 * ( log10(Koncentration) + log10(0.001) );

   db_type = type_dbs(inpf, DbInfo);
   seq_type = seq_type_dbs(inpf, DbInfo, db_type);
   if (seq_type != NA_SEQ)
   {
	seq_type = NA_SEQ;	/*  degen residues will be ignored */
   }

   printf("\nEstimated melting temperatures of non-degenerate clamp region (C=%.2fnM):\n",
        Concentration);
   while ((seq = read_a_sequence(inpf, db_type, seq_type)) != NULL)
   {
      printf("\n-----------------------------------------------------------\n");
      output_sequence(seq, stdout);

      /*  Using Breslauer's entropy & enthalpy  */
      compute_thermo(seq, &entropy, &enthalpy, &num_at, &num_gc);
      suggs_temp = suggs(num_at, num_gc);
      nar_temp =  freier(entropy, enthalpy);
      primer3_temp = nar_temp + salt;
      printf("Length=%d, clamp=%d\n", seq->length, seq->undefined);

      printf("\n---Using Breslauer's entropy=-%.1f & enthalpy=-%.1f\n",
		entropy, enthalpy);
      printf("AT/GC=%.1f, NAR=%.1f, PRIMER3=%.1f\n",
              suggs_temp, nar_temp, primer3_temp);

      /*  Using SantaLucia's entropy & enthalpy  */
      santalucia(seq, &entropy, &enthalpy, &num_at, &num_gc);
      suggs_temp = suggs(num_at, num_gc);
      nar_temp =  freier(entropy, enthalpy);
      primer3_temp = nar_temp + salt;

      printf("---Using SantaLucia's entropy=-%.1f & enthalpy=-%.1f\n",
		entropy, enthalpy);
      printf("AT/GC=%.1f, NAR=%.1f, PRIMER3=%.1f\n",
             suggs_temp, nar_temp, primer3_temp);


/*
      print_reverse(seq);
*/
      free_sequence(seq);
   }

   fclose(inpf);

   printf("\n-----------------------------------------------------------\n");
   printf("References:\n");
   printf("Entropy/Enthalpy: Table 2 from Breslauer, et al, (1986) PNAS 83:3746-3750\n");
   printf("Entropy/Enthalpy: Table 2 from SantaLucia, (1998) PNAS 95:1460-1465\n");
   printf("AT/GC: Suggs, et al, (1981) ICN-UCLA Symp.Dev.Biol. 23:683-693\n");
   printf("NAR: Eqn [2] from Freier, et al, (1986) PNAS 83:9373-9377\n");
   printf("PRIMER3: Eqn (ii) from Rychlik, et al, (1990) NAR 18:6409-6412\n");

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

/*=================================================================
    Equation [2] from Freier, et al, PNAS 83:9373-9377
=================================================================*/
double 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 freier */
/*=================================================================
    Equation [] from Rychlik, et al (1990), NAR 18:6409
	Ta = 0.3Tm_primer + 0.7Tm_product - 14.9
=================================================================*/
double rychlik(entropy, enthalpy)
double entropy, enthalpy;
{
   double tm_primer, tm_prod, salt, ta;

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

   return(tm_primer);
}  /* end of 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
   strand > 0: 5' clamp_last, clamp_first, core_last, core_first 3'
   strand < 0: 3' core_first, core_last, clamp_first, clamp_last 5'
=================================================================*/
void compute_thermo(seq, entropy, enthalpy, nat, ngc)
Sequence *seq;
double *entropy, *enthalpy;
int *nat, *ngc;
{
   int pos, prev_res, res, bi;

   *nat = *ngc = 0;
   *entropy = 10.8;  *enthalpy = 0.0;
   res = prev_res = -99;
   /*	Assumes seq is loaded 5' to 3' and stops at first degen. res */
   pos = 0;
   while (pos < seq->length &&
          seq->sequence[pos] >= 0 && seq->sequence[pos] < 4)
   {
      res = seq->sequence[pos];
      if (res == 0 || res == 3) *nat += 1;
      if (res == 1 || res == 2) *ngc += 1;
      if (prev_res >= 0 && prev_res < 4) 
      {
         bi = prev_res * 4 + res;
         *entropy += Bres_Entropy[bi]; 
         *enthalpy += Bres_Enthalpy[bi];
      }
      prev_res = res;
      pos++;
   }  
   seq->undefined = pos;		/* clamp+nondegen core */
}   /* end of compute_thermo */
/*=================================================================
   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
   strand > 0: 5' clamp_last, clamp_first, core_last, core_first 3'
   strand < 0: 3' core_first, core_last, clamp_first, clamp_last 5'
=================================================================*/
void santalucia(seq, entropy, enthalpy, nat, ngc)
Sequence *seq;
double *entropy, *enthalpy;
int *nat, *ngc;
{
   int pos, prev_res, res, bi;

   *nat = *ngc = 0;
   *entropy = 10.8;  *enthalpy = 0.0;
   res = prev_res = -99;
   /*	Assumes seq is loaded 5' to 3' and stops at first degen. res */
   pos = 0;
   while (pos < seq->length &&
          seq->sequence[pos] >= 0 && seq->sequence[pos] < 4)
   {
      res = seq->sequence[pos];
      if (res == 0 || res == 3) *nat += 1;
      if (res == 1 || res == 2) *ngc += 1;
      if (prev_res >= 0 && prev_res < 4) 
      {
         bi = prev_res * 4 + res;
         *entropy += SaLu_Entropy[bi]; 
         *enthalpy += SaLu_Enthalpy[bi];
      }
      prev_res = res;
      pos++;
   }  
   seq->undefined = pos;		/* clamp+nondegen core */
}   /* end of santalucia */
/*=====================================================================*/
/*=====================================================================*/
void print_reverse(seq)
Sequence *seq;
{
   int pos;

   printf("\nOligo in reverse order:\n");
   for (pos = seq->length-1; pos >= 0; pos--)
   {
      printf("%c", nt_btoa[ seq->sequence[pos] ]);
      if (pos%60 == 0) printf("\n");
   }
   printf("\n");
}  /* end of  print_reverse */
