#include "tigrinc.hh"


FILE *  File_Open  (const char * Filename, const char * Mode)

/* Open  Filename  in  Mode  and return a pointer to its control
*  block.  If fail, print a message and exit. */

  {
   FILE  *  fp;

   fp = fopen (Filename, Mode);
   if  (fp == NULL)
       {
        fprintf (stderr, "ERROR:  Could not open file  %s \n", Filename);
        exit (EXIT_FAILURE);
       }

   return  fp;
  }



void *  Safe_calloc  (size_t N, size_t Len)

/* Allocate and return a pointer to an array of  N  elements of
*   Len  bytes each.  All set to 0.  Exit if fail. */

  {
   void  * P;

   P = calloc (N, Len);
   if  (P == NULL)
       {
        fprintf (stderr,
		 "ERROR:  calloc failed, there is not enough memory\n");
        exit (EXIT_FAILURE);
       }

   return  P;
  }



void *  Safe_malloc  (size_t Len)

/* Allocate and return a pointer to  Len  bytes of memory.
*  Exit if fail. */

  {
   void  * P;

   P = malloc (Len);
   if  (P == NULL)
       {
        fprintf (stderr,
		 "ERROR:  malloc failed, there is not enough memory\n");
        exit (EXIT_FAILURE);
       }

   return  P;
  }



void *  Safe_realloc  (void * Q, size_t Len)

/* Reallocate memory for  Q  to  Len  bytes and return a
*  pointer to the new memory.  Exit if fail. */

  {
   void  * P;

   P = realloc (Q, Len);
   if  (P == NULL)
       {
        fprintf (stderr,
		 "ERROR:  realloc failed, there is not enough memory\n");
        exit (EXIT_FAILURE);
       }

   return  P;
  }



char  Complement  (char Ch)

/* Returns the DNA complement of  Ch . */

  {
   switch  (tolower (Ch))
     {
      case  'a' :
        return  't';
      case  'c' :
        return  'g';
      case  'g' :
        return  'c';
      case  't' :
        return  'a';
      case  'r' :          // a or g
        return  'y';
      case  'y' :          // c or t
        return  'r';
      case  's' :          // c or g
        return  's';
      case  'w' :          // a or t
        return  'w';
      case  'm' :          // a or c
        return  'k';
      case  'k' :          // g or t
        return  'm';
      case  'b' :          // c, g or t
        return  'v';
      case  'd' :          // a, g or t
        return  'h';
      case  'h' :          // a, c or t
        return  'd';
      case  'v' :          // a, c or g
        return  'b';
      default :            // anything
        return  tolower (Ch);
     }
  }


bool CompareIUPAC (char x, char y)
{
  x = tolower(x);
  y = tolower(y);

  if ( x == 'n' || x == 'x' || y == 'n' || y == 'x' )
    return true;

  switch ( x )
    {
    case 'a' :
      switch ( y )
        {
        case 'a':
        case 'r':
        case 'w':
        case 'm':
        case 'd':
        case 'h':
        case 'v':
          return true;
        }
      return false;

    case 'c' :
      switch ( y )
        {
        case 'c':
        case 'y':
        case 's':
        case 'm':
        case 'b':
        case 'h':
        case 'v':
          return true;
        }
      return false;

    case 'g' :
      switch ( y )
        {
        case 'g':
        case 'r':
        case 's':
        case 'k':
        case 'b':
        case 'd':
        case 'v':
          return true;
        }
      return false;

    case 't' :
      switch ( y )
        {
        case 't':
        case 'y':
        case 'w':
        case 'k':
        case 'b':
        case 'd':
        case 'h':
          return true;
        }
      return false;

    case 'r' :          // a or g
      switch ( y )
        {
        case 'r':
        case 'a':
        case 'g':
        case 'd':
        case 'v':
          return true;
        }
      return false;

    case 'y' :          // c or t
      switch ( y )
        {
        case 'y':
        case 'c':
        case 't':
        case 'b':
        case 'h':
          return true;
        }
      return false;

    case 's' :          // c or g
      switch ( y )
        {
        case 's':
        case 'c':
        case 'g':
        case 'b':
        case 'v':
          return true;
        }
      return false;

    case 'w' :          // a or t
      switch ( y )
        {
        case 'w':
        case 'a':
        case 't':
        case 'd':
        case 'h':
          return true;
        }
      return false;

    case 'm' :          // a or c
      switch ( y )
        {
        case 'm':
        case 'a':
        case 'c':
        case 'h':
        case 'v':
          return true;
        }
      return false;

    case 'k' :          // g or t
      switch ( y )
        {
        case 'k':
        case 'g':
        case 't':
        case 'b':
        case 'd':
          return true;
        }
      return false;

    case 'b' :          // c, g or t
      switch ( y )
        {
        case 'b':
        case 'c':
        case 'g':
        case 't':
          return true;
        }
      return false;

    case 'd' :          // a, g or t
      switch ( y )
        {
        case 'd':
        case 'a':
        case 'g':
        case 't':
          return true;
        }
      return false;

    case 'h' :          // a, c or t
      switch ( y )
        {
        case 'h':
        case 'a':
        case 'c':
        case 't':
          return true;
        }
      return false;

    case 'v' :          // a, c or g
      switch ( y )
        {
        case 'v':
        case 'a':
        case 'c':
        case 'g':
          return true;
        }
      return false;
    }
  return false;
}


int Read_String  (FILE * fp, char * & T, long int & Size, char Name [],
		  int Partial)

/* Read next string from  fp  (assuming FASTA format) into  T [1 ..]
*  which has  Size  characters.  Allocate extra memory if needed
*  and adjust  Size  accordingly.  Return  TRUE  if successful,  FALSE
*  otherwise (e.g., EOF).  Partial indicates if first line has
*  numbers indicating a subrange of characters to read.
*/

  {
   char  * P, Line [MAX_LINE];
   long int  Len, Lo, Hi;
   int  Ch, Ct;

   while  ((Ch = fgetc (fp)) != EOF && Ch != '>')
     ;

   if  (Ch == EOF)
       return  FALSE;

   fgets (Line, MAX_LINE, fp);
   Len = strlen (Line);
   assert (Len > 0 && Line [Len - 1] == '\n');
   P = strtok (Line, " \t\n");
   if  (P != NULL)
       strcpy (Name, P);
     else
       Name [0] = '\0';
   Lo = 0;  Hi = LONG_MAX;
   if  (Partial)
       {
        P = strtok (NULL, " \t\n");
        if  (P != NULL)
            {
             Lo = strtol (P, NULL, 10);
             P = strtok (NULL, " \t\n");
             if  (P != NULL)
                 Hi = strtol (P, NULL, 10);
            }
        assert (Lo <= Hi);
       }

   Ct = 0;
   T [0] = '\0';
   Len = 1;
   while  ((Ch = fgetc (fp)) != EOF && Ch != '>')
     {
      if  (isspace (Ch))
          continue;

      Ct ++;
      if  (Ct < Lo || Ct > Hi)
          continue;

      if  (Len >= Size)
          {
           Size += INCR_SIZE;
           T = (char *) Safe_realloc (T, Size);
          }
      Ch = tolower (Ch);

      if  (! isalpha (Ch) && Ch != '*')
          {
           fprintf (stderr, "Unexpected character `%c\' in string %s\n",
                                 Ch, Name);
           Ch = 'x';
          }

      T [Len ++] = Ch;
     }

   T [Len] = '\0';
   if  (Ch == '>')
       ungetc (Ch, fp);

   return  TRUE;
  }



void  Reverse_Complement
    (char S [], long int Lo, long int Hi)

//  Convert substring  S [Lo .. Hi]  to its Watson-Crick reverse
//  complement

  {
   char  Ch;

   while  (Lo <= Hi)
     {
      Ch = S [Hi];
      S [Hi] = Complement (S [Lo]);
      S [Lo] = Complement (Ch);
      Lo ++;
      Hi --;
     }

   return;
  }

