/*  blk2pssm.c	Convert a block to PSSM format
      Requires blimps 3.2.6+ routines for proper calculation of BZX

	blk2pssm <in_block_file> <out_pssm_file> <pssm_format> [<pssm_type>]
		pssm_format = B for blimps, M for mast, G for Gribskov,
			P for psi-blast
		pssm_type = block_to_matrix() conversion type:
		2	odds ratios, sum to 100 in each position
		3-6 are log odds with position-based pseudo counts:
		3	pseudo-counts, log-odds, nats, positive (blimps default)
		4	pseudo-counts, log-odds, bits
		5	pseudo-counts, log-odds, 1/2 bits
		6	pseudo-counts, log-odds, 1/3 bits (cobbler default)
		9	pseudo-counts nearly zero, log-odds, nats, positive
		10	3 but just odds ratios
		20	3 but just (counts+pseudos)/(totcount+totpseudo)
		21	just counts/totcount
		30	average score
		40	SIFT; Pauline's pseudos, (counts+pseudos)/(totc+totp)
		For backward compatibility:
		<pssm_format> without <pssm_type> gives:
		B => 6
		M => 6
		G => 30
		P => 20
           
--------------------------------------------------------------------
 10/ 5/96 J. Henikoff
 10/14/96 Leave out stop codon column for mast.
  2/22/99 Updated for blimps 3.2.6
  5/25/99 Call frq_qij() to load Qij & frequency.
  7/ 3/99 output_gribskov(), output_psi_blast()
 12/29/00 Separate pssm_format from pssm_type
  9/ 9/02 Don't treat null type as invalid
  8/27/03 Fix types to match block_to_matrix() types
>>>types 10,20,21,30 aren't working<<<
====================================================================*/

#define BLK2PSSM_C_
#define EXTERN
#define MAXNAME 80	/* Maximum file name length */

#include <blocksprogs.h>

/*
extern struct float_qij *Qij;
extern double frequency[MATRIX_AA_WIDTH];
*/

void output_mast_matrix();
void output_gribskov();
void output_psi_blast();

/*=======================================================================*/
/*
 * main
 *   controls flow of program
 *   Parameters: argc, argv
 *   Error codes:
 */

int main(argc, argv)
     int argc;
     char *argv[];
{
  FILE *bfp, *ofp, *fp;
  Block *block;
  Matrix *matrix, *logodds;
  char bdbname[MAXNAME], conname[MAXNAME];
  char ctemp[10];
  int format, type, itemp;

  ErrorLevelReport = 5;			/* No blimps errors reported */

   if (argc < 4)
   {
      printf("BLK2PSSM: Copyright 1996-2003 Fred Hutchinson Cancer Research");
      printf(" Center\n");
      printf("USAGE:  blk2pssm <block_file> <pssm_file> <pssm_format> [<pssm_type>]\n");
      printf("        <pssm_format> = B BLIMPS|M MAST|G Gribskov|P Psi-Blast\n");
      printf("        <pssm_type> = 3,6,20,21\n");
   }

/* ------------1st arg = blocks database -------------------------------*/
   if (argc > 1)
      strcpy(bdbname, argv[1]);
   else
   {
      printf("\nEnter name of BLOCKS input file: ");
      gets(bdbname);
   }
   if ( (bfp=fopen(bdbname, "r")) == NULL)
   {
      printf("\nCannot open file %s\n", bdbname);
      exit(-1);
   }
/* ------------2nd arg = PSSM file -----------------------*/
   if (argc > 2)
      strcpy(conname, argv[2]);
   else
   {
      printf("\nEnter name of PSSM output file: ");
      gets(conname);
   }
   if ( (ofp=fopen(conname, "w")) == NULL)
   {
      printf("\nCannot open file %s\n", conname);
      exit(-1);
   }

/*--------------3rd arg = PSSM format-------------------------*/
/* blimps=0, mast=1, gribs=2, psi=3 */
   format = 0; type = 6;
   if (argc > 3) {  strcpy(ctemp, argv[3]);  }
   else
   {
      printf("\nEnter PSSM format type (B=blimps, M=mast, G=Gribskov, P=Psi-Blast) [B]:  ");
      gets(ctemp);
   }
   if (strlen(ctemp) && (ctemp[0] == 'm' || ctemp[0] == 'M'))
   {       format = 1; type = 6;  }
   else 
   {
      if (strlen(ctemp) && (ctemp[0] == 'g' || ctemp[0] == 'G'))
      {  format = 2; type = 10;  }
      else  
      {
         if (strlen(ctemp) && (ctemp[0] == 'p' || ctemp[0] == 'P'))
         {  format = 3; type = 20;   }
      }
    }

/*--------------4th arg = PSSM type -----------------------------*/
   if (argc > 4)
   {
      itemp = atoi(argv[4]);
      if ((itemp > 2 && itemp < 7) || itemp == 9 || itemp == 10 ||
	   itemp == 20 || itemp == 21 || itemp == 30 || itemp == 40) 
      {    
         type = itemp;  
         printf("Using type %d\n", type); 
      }
      else
      { printf("%s Invalid type %d, using type %d\n", argv[4], itemp, type); }
   }
   
/*-----------------------------------------------------------------*/
   /* Sets global variables Qij, frequency[], RTot  */
   /* Always reads default.amino.frq & default.qij & sets RTot = 5 */
   frq_qij();
 
   if (type == 9) RTot = 1.0;		/* see narrow.c */

/*-----------------------------------------------------------------*/
     /*   should write the alphabet once per family */
  if (format == 1) fprintf(ofp, "ALPHABET= ARNDCQEGHILKMFPSTWYVBZX\n");
  while ((block = read_a_block(bfp)) != NULL)
  {
      matrix = block_to_matrix(block, type);
      switch (format)
      {
         case 0:    /* Blocks format */
            output_matrix_s(matrix, ofp, FLOAT_OUTPUT); 
            break;
         case 1:    /* Mast format  */
            output_mast_matrix(matrix, ofp);
            break;
         case 2:
            output_gribskov(matrix, ofp);
            break;
         case 3:
            /* to match cobbler seq */
            logodds = block_to_matrix(block, 6);
            output_psi_blast(matrix, logodds, ofp);
	    free_matrix(logodds);
            break;
         default:
            break;
      }
      free_matrix(matrix);
      free_block(block);
  }
   
  fclose(bfp); fclose(ofp);
  exit(0);

}  /* end of main */
/*=======================================================================
    0- 1A 2R 3N 4D 5C 6Q 7E 8G 9H 10I 11L 12K 13M 14F 15P 16S 17T 18W 19Y
   20V 21B 22Z 23X 24* 25J,O,U 
========================================================================*/
void output_mast_matrix(matrix, ofp)
Matrix *matrix;
FILE *ofp;
{
   int pos, aa;

   fprintf(ofp, "log-odds matrix: alength= 23 w= %d\n", matrix->width);
   for (pos=0; pos < matrix->width; pos++)
   {
      /*  Force U == X */
      matrix->weights[25][pos] = matrix->weights[23][pos];
      for (aa=1; aa <= 23; aa++)
      {
         fprintf(ofp, "% 6.3f ", matrix->weights[aa][pos]);
      }
      fprintf(ofp, "\n");
   }

}  /* end of output_mast_matrix */
/*=======================================================================
	Compute X for each position as average score over entire matrix
	(instead of just over the position). Compute -* as min over
	entire matrix.
>>>>> Have to read in the aa frequencies first <<<<<<<
========================================================================*/
void compute_X(matrix)
Matrix *matrix;
{
   int pos, aa;
   double dmean, dmin;

   dmean = 0.0;
   dmin = 9999.99;

   for (pos=0; pos < matrix->width; pos++)
   {
      for (aa=1; aa <=20; aa++)
      {
/*         dmean += freqs[aa] * matrix->weights[aa][pos];    */
         if (matrix->weights[aa][pos] < dmin) dmin = matrix->weights[aa][pos];
      }
   }
   dmean /= (double) matrix->width;
   for (pos=0; pos < matrix->width; pos++)
   {
      matrix->weights[23][pos] = dmean;			/*   X   */
      matrix->weights[25][pos] = dmean;			/* JOU   */
      matrix->weights[0][pos] = dmin;			/*   -   */
      matrix->weights[24][pos] = dmin;			/*   *   */
   }
} /* end of compute_X */
/*=======================================================================
    0- 1A 2R 3N 4D 5C 6Q 7E 8G 9H 10I 11L 12K 13M 14F 15P 16S 17T 18W 19Y
   20V 21B 22Z 23X 24* 25J,O,U 

PSI-BLAST checkpoint file is supposed to contain the target frequencies
so the values for each column should sum to 1:
	(counts + pseudo-counts) / (total counts)
But the sequence values are taken from a logodds matrix so they should
match the cobbler sequence.
========================================================================*/
void output_psi_blast(matrix, logodds, ofp)
Matrix *matrix, *logodds;
FILE *ofp;
{
   int pos, aa, maxaa;
   long ltemp;
   double dtemp, maxscore;
   char c;

   ltemp = (long) matrix->width;
   fwrite(&ltemp, sizeof(long), 1, ofp);

   /*  Need to write out the sequence next; pick highest scoring
	amino acid in each column of the logodds matrix */
   for (pos=0; pos < logodds->width; pos++)
   {
      maxaa = -9999; maxscore = -9999.;
      for (aa=1; aa <= 20; aa++)
      {
         if (logodds->weights[aa][pos] > maxscore)
         {   maxaa = aa; maxscore = logodds->weights[aa][pos];  }
      }
      c = aa_btoa[maxaa];
      fwrite(&c, sizeof(char), 1, ofp);
   }

   for (pos=0; pos < logodds->width; pos++)
   {
/*		dsum is always 1.0
      dsum = 0.0;
      for (aa=1; aa <= 20; aa++)
      {
         dsum += matrix->weights[aa][pos];
      }
*/

      for (aa=1; aa <= 20; aa++)
      {
/*
         dtemp = matrix->weights[aa][pos]/dsum;
printf("%f ", dtemp);
*/
         dtemp = matrix->weights[aa][pos];
         fwrite(&dtemp, sizeof(double), 1, ofp);
      }
/*
printf("\n");
*/
   }

}  /* end of output_psi_blast */
/*=======================================================================
    0- 1A 2R 3N 4D 5C 6Q 7E 8G 9H 10I 11L 12K 13M 14F 15P 16S 17T 18W 19Y
   20V 21B 22Z 23X 24* 25J,O,U 

   Do all the entries in a Gribskov profile have to be positive integers?
   They are here; odds ratios normalized to add to 100 in each position.
========================================================================*/
void output_gribskov(matrix, ofp)
Matrix *matrix;
FILE *ofp;
{
   int pos, aa, maxaa, itemp;
   double dmin, dsum, maxscore;

   fprintf(ofp, "(Peptide) Length: %d\n", matrix->width);
   fprintf(ofp, "COBBLER Profile for %s %s\n", matrix->number, matrix->de);
   fprintf(ofp, "Cons");
   for (aa=1; aa <= 20; aa++) fprintf(ofp, "      %c", aa_btoa[aa]);
   fprintf(ofp, "\n");

   dmin = 9999.;
   for (pos=0; pos < matrix->width; pos++)
   {
      for (aa=1; aa <= 20; aa++)
      {
         if (matrix->weights[aa][pos] < dmin)
         {  dmin = matrix->weights[aa][pos];  }
      }
   }
printf("dmin=%f\n", dmin);

   /*  Need to write out the sequence next; pick highest scoring
	amino acid in each column */
   for (pos=0; pos < matrix->width; pos++)
   {
      dsum = 0.0;
      maxaa = -9999; maxscore = -9999.;
      for (aa=1; aa <= 20; aa++)
      {
         dsum += matrix->weights[aa][pos];
         if (matrix->weights[aa][pos] > maxscore)
         {   maxaa = aa; maxscore = matrix->weights[aa][pos];  }
      }
      fprintf(ofp, "%c    ", aa_btoa[maxaa]);

      for (aa=1; aa <= 20; aa++)
      {
/*
         itemp = round( 10. * (matrix->weights[aa][pos] - dmin) );
*/
         itemp = round( 100. * matrix->weights[aa][pos] / dsum );
         fprintf(ofp, "%6d ", itemp);
      }
      fprintf(ofp, "\n");
   }

}  /* end of output_gribskov */
