/*
    MSA - multiple sequence alignment utils

    Copyright (C) 2004-2015 Douglas L. Theobald

    THESEUS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    THESEUS is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public
    License along with THESEUS in the file 'COPYING'. If not, see
    <http://www.gnu.org/licenses/>.

    -/_|:|_|_\-
*/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include "msa.h"
#include "DLTutils.h"

#ifdef DMALLOC
#include "dmalloc.h"
#endif

#define MINIMUM_MATCH_LENGTH 20

/* ********************************************************************** */
/* MSA prep and memory allocation/deallocation                            */
/* ********************************************************************** */

MSA
*getmsa(char *msafile_name)
{
    FILE           *msafile = NULL;
    char            line[256];
    int             taxan;
    MSA            *msa = MSAinit();

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR5: file \"%s\" not found. \n\n", msafile_name);
        exit(EXIT_FAILURE);
    }

    fgets(line, 255, msafile);
    fclose(msafile);

    if (strncmp(line, ">", 1) == 0)
    {
        reada2m(msafile_name, msa);
    }
    else if (strncmp(line, "CLUSTAL", 7) == 0)
    {
        readaln(msafile_name, msa);
    }
    else if (strncmp(line, "#NEXUS", 6) == 0)
    {
        if (isnexdist(msafile_name) != 0)
            readnexd(msafile_name, msa);
        else
            readnex(msafile_name, msa);
    }
    else if (sscanf(line, "%*d %d", &taxan) == 1)
    {
        readphylip(msafile_name, msa);
    }
    else if (sscanf(line, "%d", &taxan) == 1)
    {
        msa->distance = 1;
        readphylipd(msafile_name, msa);
    }
    else if (strncmp(line, "!!AA_MULTIPLE_ALIGNMENT", 23) == 0)
    {
        readmsf(msafile_name, msa);
    }
    else if (ismsf(msafile_name) != 0)
    {
        readmsf(msafile_name, msa);
    }
    else
    {
        fprintf(stderr,
                "\n ERROR6: file does not seem to be A2M, CLUSTAL, NEXUS, PHYLIP, or MSF format. \n\n");
        exit(EXIT_FAILURE);
    }

    return(msa);
}


/* determines if the NEXUS file has a distance matrix in it */
int
isnexdist(char *msafile_name)
{
    int             linenum;
    char           *line = NULL;
    FILE           *msafile = NULL;

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR5: file \"%s\" not found. \n\n", msafile_name);
        exit(EXIT_FAILURE);
    }

    line = (char *) malloc(256 * sizeof(char));

    rewind(msafile);
    for (linenum = 0; !feof(msafile); ++linenum)
    {
        fgets(line, 255, msafile);
        strtolower(line);
        if (strstr(line, "begin") && strstr(line, "distances"))
            return(linenum);
    }

    fclose(msafile);
    MyFree(line);
    line = NULL;

    return(0);
}


int
ismsf(char *msafile_name)
{
    int             linenum;
    char           *line = NULL;
    FILE           *msafile = NULL;

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR5: file \"%s\" not found. \n\n", msafile_name);
        exit(EXIT_FAILURE);
    }

    line = (char *) malloc(256 * sizeof(char));

    rewind(msafile);
    for (linenum = 0; !feof(msafile); ++linenum)
    {
        fgets(line, 255, msafile);
        if (strstr(line, "MSF:"))
            return(linenum);
    }

    fclose(msafile);
    MyFree(line);
    line = NULL;

    return(0);
}


MSA
*MSAinit(void)
{
    MSA        *msa = calloc(1, sizeof(MSA));

    msa->filename  = calloc(FILENAME_MAX, sizeof(char));
    msa->filename[0] = '\0';

    msa->seq      = NULL;
    msa->name     = NULL;
    msa->checksum = NULL;
    msa->flag     = NULL;
    msa->flag2    = NULL;
    msa->dist     = NULL;

    msa->distance = 0;
    msa->allocnum = 0;
    msa->alloclen = 0;
    msa->seqnum = 0;
    msa->seqlen = 0;
    msa->msachecksum = 0;

    return(msa);
}


void
MSAalloc(MSA *msa, int seqnum, int alignment_len, int name_size)
{
    int i;

    msa->seq       = (char **) malloc((seqnum+1) * sizeof(char *));
    msa->name      = (char **) malloc((seqnum+1) * sizeof(char *));
    msa->checksum  = (int *)   calloc((seqnum+1),  sizeof(int));
    msa->flag      = (int *)   calloc((seqnum+1),  sizeof(int));
    msa->flag2     = (int *)   calloc((seqnum+1),  sizeof(int));

    if (msa->seq      == NULL ||
        msa->name     == NULL ||
        msa->checksum == NULL ||
        msa->flag     == NULL ||
        msa->flag2    == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR59: could not allocate memory in function MSAalloc(). \n\n");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < seqnum; ++i)
    {
        msa->seq[i] = (char *) calloc((alignment_len+2), sizeof(char));
        msa->seq[i][0] = '\0';
        //msa->seq[i][alignment_len] = '\0';
        msa->name[i] = (char *) calloc((name_size+2), sizeof(char));
        msa->name[i][0] = '\0';
    }

    /* these should never be modified */
    msa->allocnum = seqnum;
    msa->alloclen = alignment_len;

    /* these will change */
    msa->seqnum = seqnum;
    msa->seqlen = alignment_len;
}


MSA
*DISTalloc(MSA *msa)
{
    int i;

    msa->distance = 1;
    msa->dist = (double **) malloc(msa->seqnum * sizeof(double *));

    for (i = 0; i < msa->seqnum; ++i)
        msa->dist[i] = (double *) calloc(msa->seqnum, sizeof(double));

    return(msa);
}


void
DISTfree(MSA *msa)
{
    int         i;

    if (msa->dist)
    {
        for (i = 0; i < msa->seqnum; ++i)
        {
            MyFree(msa->dist[i]);
            msa->dist[i] = NULL;
        }

        MyFree(msa->dist);
        msa->dist = NULL;
    }
}


MSAA
*MSAAalloc(int arraynum)
{
    MSAA       *array = calloc(1, sizeof(MSAA));
    int         i;

    array->array = calloc(arraynum, sizeof(MSA *));
    array->flag  = calloc(arraynum,  sizeof(int));

    if (array->array == NULL || array->flag  == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr,
                "\n ERROR79: could not allocate memory in function MSAalloc_array(). \n\n");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < arraynum; ++i)
        array->array[i] = MSAinit();

    array->arraynum = arraynum;

    return(array);
}


void
MSAdestroy(MSA **msa_ptr)
{
    if (msa_ptr)
    {
        MSA *msa = *msa_ptr;

        if (msa)
        {
            int         i;

            MyFree(msa->filename);
            msa->filename = NULL;

            if(msa->distance == 1)
                DISTfree(msa);

            if (msa->seq)
            {
                for (i = 0; i < msa->allocnum; ++i)
                {
                    if (msa->seq[i])
                    {
                        MyFree(msa->seq[i]);
                        msa->seq[i] = NULL;
                    }
                }

                MyFree(msa->seq);
                msa->seq = NULL;
            }

            if (msa->name)
            {
                for (i = 0; i < msa->seqnum; ++i)
                {
                    MyFree(msa->name[i]);
                    msa->name[i] = NULL;
                }

                MyFree(msa->name);
                msa->name = NULL;
            }

            MyFree(msa->checksum);
            msa->checksum = NULL;

            MyFree(msa->flag);
            msa->flag = NULL;

            MyFree(msa->flag2);
            msa->flag2 = NULL;

            MyFree(msa);
            msa = NULL;
        }
    }
}


/* free an array of MSAs */
void
MSAAdestroy(MSAA **msaa_ptr)
{
    MSAA *msaa = *msaa_ptr;
    int             i;

    if (msaa)
    {
        if (msaa->array)
        {
            for (i = 0; i < msaa->arraynum; ++i)
            {
                MSAdestroy(&(msaa->array[i]));
                msaa->array[i] = NULL;
            }

            MyFree(msaa->array);
            msaa->array = NULL;
        }

        MyFree(msaa->flag);
        msaa->flag = NULL;

        MyFree(msaa);
        *msaa_ptr = NULL;
    }
}


/* ********************************************************************** */
/* MSA operations                                                         */
/* ********************************************************************** */
void
MSAprint(MSA *msa)
{
    int             i;

    printf("\n msa->seqnum = %d \n", msa->seqnum);
    printf(  " msa->seqlen = %d \n\n", msa->seqlen);

    for(i = 0; i < msa->seqnum; ++i)
    {
        if(msa->flag[i] == 0)
            printf("%3d: %-16.16s %s\n", i, msa->name[i], msa->seq[i]);
    }

    printf("\n");
    fflush(NULL);
}


void
MSAmultiprint(MSAA *msas)
{
    int             i;

    for(i = 0; i < msas->arraynum; ++i)
    {
        fprintf(stderr, "\n msas[%d]:", i);
        MSAprint(msas->array[i]);
    }
}


/* copy one MSA into another -
   MSA#1 must be at least as big MSA#2 */
MSA
*MSAcpy(MSA *msa1, MSA *msa2)
{
    int             i;

    if((msa1->alloclen < msa2->seqlen) || (msa1->allocnum < msa2->seqnum))
    {
        fprintf(stderr, "\n ERROR43: MSA1 is smaller than MSA2  \n");
        fprintf(stderr,   " ERROR43: alignment cannot be copied \n\n");
        exit(EXIT_FAILURE);
    }

    mystrncpy(msa1->filename, msa2->filename, FILENAME_MAX);
    msa1->seqnum = msa2->seqnum;
    msa1->seqlen = msa2->seqlen;

    for (i = 0; i < msa1->seqnum; ++i)
    {
        msa1->flag[i]  = msa2->flag[i];
        msa1->flag2[i] = msa2->flag2[i];
        msa1->checksum[i] = msa2->checksum[i];
        mystrncpy(msa1->seq[i],  msa2->seq[i],  msa2->seqlen + 1);
        mystrncpy(msa1->name[i], msa2->name[i], SEQ_NAME_LENGTH);
    }

    return(msa1);
}


MSA
*MSAmult(MSA *msa, int multiply)
{
    MSA            *newmsa = MSAinit();
    int             i, j;

    MSAalloc(newmsa, msa->allocnum, msa->alloclen * multiply, SEQ_NAME_LENGTH);

    mystrncpy(newmsa->filename, msa->filename, FILENAME_MAX);
    newmsa->seqnum = msa->seqnum;
    newmsa->seqlen = msa->seqlen * multiply;

    for (i = 0; i < newmsa->seqnum; ++i)
    {
        newmsa->flag[i]  = msa->flag[i];
        newmsa->flag2[i] = msa->flag2[i];
        newmsa->checksum[i] = newmsa->checksum[i];
        mystrncpy(newmsa->name[i], msa->name[i], SEQ_NAME_LENGTH);
        for (j = 0; j < newmsa->seqlen; ++j)
            newmsa->seq[i][j] = msa->seq[i][(int)floor(j / multiply)];
    }

    MSAdestroy(&msa);
    msa = newmsa;
    return(newmsa);
}


/* duplicate everything */
MSA
*MSAduplicate(MSA *msa)
{
    MSA            *newmsa = MSAinit();
    int             i;

    MSAalloc(newmsa, msa->allocnum, msa->alloclen, SEQ_NAME_LENGTH);

    mystrncpy(newmsa->filename, msa->filename, FILENAME_MAX);
    newmsa->seqnum = msa->seqnum;
    newmsa->seqlen = msa->seqlen;

    for (i = 0; i < msa->seqnum; ++i)
    {
        newmsa->flag[i]  = msa->flag[i];
        newmsa->flag2[i] = msa->flag2[i];
        newmsa->checksum[i] = newmsa->checksum[i];
        mystrncpy(newmsa->seq[i],  msa->seq[i],  msa->seqlen + 1);
        mystrncpy(newmsa->name[i], msa->name[i], SEQ_NAME_LENGTH);
    }

    return(newmsa);
}


MSA
*MSAduplicate_noflags(MSA *msa)
{
    MSA            *newmsa = MSAinit();
    int             i, j, flags;

    flags = 0;
    for (i = 0; i < msa->seqnum; ++i)
        if (msa->flag[i] > 0 || msa->flag2[i] > 0)
            flags++;

    MSAalloc(newmsa, msa->seqnum - flags, msa->alloclen, SEQ_NAME_LENGTH);

    mystrncpy(newmsa->filename, msa->filename, FILENAME_MAX);
    newmsa->seqnum = msa->seqnum - flags;
    newmsa->seqlen = msa->seqlen;

    j = 0;
    for (i = 0; i < msa->seqnum; ++i)
    {
        if (msa->flag[i] == 0 && msa->flag2[i] == 0)
        {
            newmsa->flag[j]  = msa->flag[i];
            newmsa->flag2[j] = msa->flag2[i];
            newmsa->checksum[j] = newmsa->checksum[i];
            mystrncpy(newmsa->seq[j],  msa->seq[i],  msa->seqlen + 1);
            mystrncpy(newmsa->name[j], msa->name[i], SEQ_NAME_LENGTH);
            ++j;
        }
    }

    return(newmsa);
}


/* duplicate everything but the actual sequences */
MSA
*MSAduplicate_empty(MSA *msa)
{
    MSA            *newmsa = MSAinit();
    int             i;

    fflush(NULL);
    MSAalloc(newmsa, msa->allocnum, msa->alloclen, SEQ_NAME_LENGTH);

    mystrncpy(newmsa->filename, msa->filename, FILENAME_MAX);
    newmsa->seqnum = msa->seqnum;
    newmsa->seqlen = strlen(msa->seq[0]);

    for (i = 0; i < msa->seqnum; ++i)
    {
        newmsa->flag[i]  = msa->flag[i];
        newmsa->flag2[i] = msa->flag2[i];
        newmsa->checksum[i] = newmsa->checksum[i];
        mystrncpy(newmsa->name[i], msa->name[i], SEQ_NAME_LENGTH);
    }

    return(newmsa);
}


/* flag redundant sequences */
MSA
*MSAdeldups(MSA *msa)
{
    int             i, j, k;
    const int       seqnum = msa->seqnum;
    const int       seqlen = msa->seqlen;
    int            *flag = msa->flag;
    const char    **seq = (const char **) msa->seq;

    for(i = 0; i < seqnum; ++i)
    {
        if(flag[i] == 0) /* if already deleted, don't need to consider it */
        {
            for(j = 0; j < i; ++j)
            {
                if(flag[j] == 0) /* if already deleted, don't need to consider it */
                {
                    for(k = 0; k < seqlen; ++k)
                    {
                        if(seq[i][k] != seq[j][k])
                            break;
                    }

                    if(k == seqlen)
                        flag[j] = 1;
                }
            }
        }
    }

    return(msa);
}


/* Fix taxa names for PHYLIP, truncate at 10 chars, make unique brute force */
/* Also replace whitespace in name with underscores */
MSA
*MSAfixnamesphy(MSA *msa)
{
    int             i, j, cnt, dig;
    int             len = 10;
    char            digit[len];

    for(i = 0; i < msa->seqnum; ++i)
        msa->name[i][len] = '\0';

    for(i = 0; i < msa->seqnum; ++i)
        for(j = 0; j < len; ++j)
            if (isspace(msa->name[i][j]) || msa->name[i][j] == '|' || msa->name[i][j] == '*' || msa->name[i][j] == '-' || msa->name[i][j] == '.')
                msa->name[i][j] = '_';

    for(i = 0; i < msa->seqnum; ++i)
    {
        cnt = 0;
        for(j = i+1; j < msa->seqnum; ++j)
        {
            //printf("\n%d %s %s  (%d,%d)", cnt, msa->name[i], msa->name[j], i ,j);
            if (strncmp(msa->name[i], msa->name[j], len) == 0)
            {
                sprintf(digit, "%-d", cnt);
                dig = strlen(digit);

                if (strtol(&msa->name[j][len-dig], NULL, 10) == cnt)
                {
                    ++cnt;
                    sprintf(digit, "%-d", cnt);
                    dig = strlen(digit);
                }

                //printf("\n## %d %d %d %s %s  (%d,%d)", cnt, dig, len-dig, msa->name[i], msa->name[j], i ,j);
                strncpy(&msa->name[j][len-dig], digit, len);
                //printf("\n## %d %d %d %s %s\n", cnt, dig, len-dig, msa->name[i], msa->name[j]);
                ++cnt;
            }
        }
    }

    return(msa);
}


/* delete all gapped columns above a specified fraction cutoff, within a range of columns and sequences */
/* frac specifies the maximum fraction of gaps allowed per column */
MSA
*MSAdelallgaps(MSA *msa, int begcol, int endcol, int logap, int upgap, double frac)
{
    int             i, j, k, m;
    double          cnt;
    MSA            *tmpmsa = NULL;

    tmpmsa = MSAduplicate(msa);

    if (begcol < 1)
        begcol = 1;

    if (endcol > msa->seqlen)
        endcol = msa->seqlen;

    if (logap < 1)
        logap = 1;

    if (upgap > msa->seqnum)
        upgap = msa->seqnum;

    for (i = 0; i < begcol-1; ++i)
        for (k = 0; k < msa->seqnum; ++k)
            tmpmsa->seq[k][i] = msa->seq[k][i];

    m = begcol-1;
    //for (i = 0; i < tmpmsa->seqlen; ++i)
    for (i = begcol-1; i < endcol; ++i)
    {
        cnt = 0.0;
        //for (j = 0; j < msa->seqnum; ++j)
        for (j = logap-1; j < upgap; ++j)
        {
            if (msa->seq[j][i] == '-')
                cnt++;
        }

        //if (j == msa->seqnum)
        //if (j == upgap)
        //printf("\n%d %f", i+1, cnt/msa->seqnum);
        //if (cnt/msa->seqnum <= frac)
        /* if the fraction of gaps for this column is less than the specified cutoff, keep it */
        if (cnt / (upgap - logap + 1.0) <= frac)
        {
            for (k = 0; k < msa->seqnum; ++k)
            {
                tmpmsa->seq[k][m] = msa->seq[k][i];
                //printf("\n%d %c %d %c", m, tmpmsa->seq[k][m], i, msa->seq[k][i]);
            }
            ++m;
        }
    }

    for (i = endcol; i < msa->seqlen; ++i, ++m)
        for (k = 0; k < msa->seqnum; ++k)
            tmpmsa->seq[k][m] = msa->seq[k][i];

//printf("\n### endcol:%d m:%d", endcol, m);

    for (k = 0; k < msa->seqnum; ++k)
    {
        //printf("\n%d %c", m, tmpmsa->seq[k][m]);
        tmpmsa->seq[k][m] = '\0';
    }
    tmpmsa->seqlen = m;

    MSAcpy(msa, tmpmsa);
    MSAdestroy(&tmpmsa);

    return(msa);
}


/* collapse common gaps */
MSA
*MSAdelgap(MSA *msa)
{
    int             i, j, k, m;
    MSA            *tmpmsa = NULL;

    tmpmsa = MSAduplicate(msa);

    m = 0;
    for (i = 0; i < tmpmsa->seqlen; ++i)
    {
        for (j = 0; j < msa->seqnum; ++j)
        {
            if (msa->flag[j] == 0 && msa->seq[j][i] != '-')
                break;
        }

        if (j != msa->seqnum)
        {
            for (k = 0; k < tmpmsa->seqnum; ++k)
                tmpmsa->seq[k][m] = msa->seq[k][i];
            ++m;
        }
    }

    for (k = 0; k < tmpmsa->seqnum; ++k)
        tmpmsa->seq[k][m] = '\0';
    tmpmsa->seqlen = m;

    MSAcpy(msa, tmpmsa);
    MSAdestroy(&tmpmsa);

    return(msa);
}


/* remove big gaps, replace with a single gap character */
MSA
*MSAmingap(MSA *msa)
{
    int             i, j, k, m;
    MSA            *tmpmsa = NULL;

    tmpmsa = MSAduplicate(msa);

    m = 0;
    for (i = 0; i < tmpmsa->seqlen; ++i)
    {
        for (j = 0; j < msa->seqnum; ++j)
        {
            if (msa->seq[j][i] != '-')
                break;
        }

        if (j != msa->seqnum)
        {
            for (k = 0; k < tmpmsa->seqnum; ++k)
                tmpmsa->seq[k][m] = msa->seq[k][i];
            ++m;
        }
    }

    for (k = 0; k < tmpmsa->seqnum; ++k)
        tmpmsa->seq[k][m] = '\0';
    tmpmsa->seqlen = m - 1;

    MSAcpy(msa, tmpmsa);
    MSAdestroy(&tmpmsa);

    return(msa);
}


/* Merge an array of alignments into one -        */
/* toss duplicate sequences -                     */
/* all alignments must be of same length to merge */
/* Argument is a pointer to an array of pointers  */
/* to alignment structures                        */
MSA
*MSAmerge(MSAA *msas)
{
    int             i, j;
    int             total_seqnum = 0;
    int             last_seqlen;
    int             counter;
    MSA            *newmsa = MSAinit();

    last_seqlen = msas->array[0]->seqlen;
    for(i = 0; i < msas->arraynum; ++i)
    {
        total_seqnum += msas->array[i]->seqnum;

        if(msas->array[i]->seqlen != last_seqlen)
        {
            fprintf(stderr, "\n ERROR99: alignments are of different length \n");
            fprintf(stderr,   " ERROR99: alignments cannot be merged        \n\n");
            exit(EXIT_FAILURE);
        }

        last_seqlen = msas->array[i]->seqlen;
    }

    MSAalloc(newmsa, total_seqnum, (last_seqlen + 1), SEQ_NAME_LENGTH);

    for(i = 0, counter = 0; i < msas->arraynum; ++i)
    {
        for(j = 0; j < msas->array[i]->seqnum; ++j, ++counter)
        {
            mystrncpy(newmsa->name[counter], msas->array[i]->name[j], SEQ_NAME_LENGTH);
            mystrncpy(newmsa->seq[counter],  msas->array[i]->seq[j],  (msas->array[i]->seqlen + 1));
        }
    }

    newmsa->seqlen = strlen(newmsa->seq[0]);
    newmsa->seqnum = total_seqnum;
    MSAmingap(newmsa);
    MSAdeldups(newmsa);

    return(newmsa);
}


/* This function finds the sequence in msa1 that has the largest
   consequetive match to any sequence in msa2.
   It returns the length of the match.
   Indexes of the matching sequences are in seq1 and seq2.
   The starting and ending indexes of the matches are in
   begin1, end1, begin2, end2.

   NOTE:  end1 and end2 are inclusive. */
int
MSAseqeq(MSA *msa1,
         MSA *msa2,
         int *seq1,
         int *begin1,
         int *end1, /* end is inclusive */
         int *seq2,
         int *begin2,
         int *end2)
{
    int             i, j, k, l, m, n;
    int             /* equivalent,  */match_length, best_match_length;

    i = j = k = l = m = n = 0;
    /* equivalent = 0; */
    *begin1 = *begin2 = -1;
    *end1 = *end2 = -1;
    best_match_length = 0;

    for(i = 0; i < msa1->seqnum; ++i) /* scan all sequences in msa1 */
    {
        for(j = 0; j < msa2->seqnum; ++j) /* scan all sequences in msa2 */
        {
            for(k = 0; k < msa1->seqlen; ++k) /* scan all starting positions in msa1->seq[i] */
            {
                for(l = 0; l < msa2->seqlen; ++l) /* scan all starting positions in msa2->seq[j] */
                {
                    m = k;
                    n = l;
                    match_length = 0;
                    /* this will end on either the last character _or_ the null terminator */
                    while(m < msa1->seqlen && n < msa2->seqlen)
                    {
                        if(msa1->seq[i][m] == msa2->seq[j][n] &&
                           (msa1->seq[i][m] != '-'))
                            ++m, ++n, ++match_length;
                        else if(msa1->seq[i][m] == '-' && m != k)
                            ++m;
                        else if(msa2->seq[j][n] == '-' && n != l)
                            ++n;
                        else
                            break;
                    }

                    if(match_length > best_match_length)
                    {
                        best_match_length = match_length;
                        *seq1 = i;
                        *seq2 = j;
                        *begin1 = k;
                        *begin2 = l;
                        while(msa1->seq[i][m] == '-' || msa1->seq[i][m] == '\0') /* don't want the end to be a gap */
                            --m;
                        *end1 = m; /* inclusive */
                        while(msa2->seq[j][n] == '-' || msa2->seq[j][n] == '\0') /* don't want the end to be a gap */
                            --n;
                        *end2 = n;
                    }
                } /* end of msa2->seq[j] scan loop */
            } /* end of msa1->seq[i] scan loop */
        } /* end of msa2 scan loop */
    } /* end of msa1 scan loop */

    return(best_match_length);
}


void
MSAcombine2(MSAA *msaa, int msa1_i, int msa2_i, int seq1_i, int seq2_i, int msao_i)
{
    int             i, j, k, m;
    int             maxlen, seqnum, num1, num2, len1, len2; /* max possible length of the new combined alignment */
    MSA            *msa1 = NULL;
    MSA            *msa2 = NULL;
    MSA            *msao = NULL;
    char           *seq1 = NULL;
    char           *seq2 = NULL;

    msa1 = msaa->array[msa1_i];
    msa2 = msaa->array[msa2_i];
    msao = msaa->array[msao_i];
    seq1 = msa1->seq[seq1_i];
    seq2 = msa2->seq[seq2_i];
    num1 = msa1->seqnum;
    num2 = msa2->seqnum;
    len1 = msa1->seqlen;
    len2 = msa2->seqlen;

    maxlen = len1 + len2;
    seqnum = num1 + num2;

    if (msao)
        MSAdestroy(&msao);

    msao = MSAinit();
    MSAalloc(msao, seqnum, maxlen, SEQ_NAME_LENGTH);

    /* copy the names over */
    for (m = 0; m < num1; ++m)
        strncpy(msao->name[m], msa1->name[m], strlen(msa1->name[m])+1);

    for (m = 0; m < num2; ++m)
        strncpy(msao->name[m + num1], msa2->name[m], strlen(msa2->name[m])+1);

    i = j = k = 0;

    while((i < len1 || j < len2) && k < maxlen)
    {
        if(seq1[i] == seq2[j])
        {
            if (seq1[i] == '-')
            {
                for (m = 0; m < num1; ++m)
                    msao->seq[m][k] = msa1->seq[m][i];

                for (m = 0; m < num2; ++m)
                    msao->seq[m + num1][k] = '-';

                ++k;

                for (m = 0; m < num1; ++m)
                    msao->seq[m][k] = '-';

                for (m = 0; m < num2; ++m)
                    msao->seq[m + num1][k] = msa2->seq[m][j];

                ++k;
            }
            else
            {
                for (m = 0; m < num1; ++m)
                    msao->seq[m][k] = msa1->seq[m][i];

                for (m = 0; m < num2; ++m)
                    msao->seq[m + num1][k] = msa2->seq[m][j];

                ++k;
            }

            ++i;
            ++j;
        }
        else if (seq1[i] != seq2[j])
        {
            if (seq1[i] == '-')
            {
                for (m = 0; m < num1; ++m)
                    msao->seq[m][k] = msa1->seq[m][i];

                for (m = 0; m < num2; ++m)
                    msao->seq[m + num1][k] = '-';

                ++i;
            }
            else if (seq2[j] == '-')
            {
                for (m = 0; m < num1; ++m)
                    msao->seq[m][k] = '-';

                for (m = 0; m < num2; ++m)
                    msao->seq[m + num1][k] = msa2->seq[m][j];

                ++j;
            }

            ++k;
        }
    }

    msao->seqlen = k;

    for (m = 0; m < seqnum; ++m)
        msao->seq[m][k] = '\0';

    msao->flag[num1 + seq2_i] = 1;
}


/* ********************************************************************** */
/* reading alignment files                                                */
/* ********************************************************************** */
/* NB -- I use the underbar '_' as a special character in the             */
/* MarkovPolo mod_perl handler as a sub for weird tainted metacharacters  */

/* Function: GCGchecksum()
 * Date:     DLT, Wed Sep 8 10:16 2003 [Boulder]
 *
 * Purpose:  Calculate a GCG checksum for an MSA (or sequence).
 *           Method from sqio.c by Sean Eddy in hmmer
 *           source.
 *           Originally provided by Steve Smith of Genetics
 *           Computer Group.
 *
 * Returns:  GCG checksum.
 */
int
GCGchecksum(MSA *msa, int seq)
{
    int             checksum;
    int             i;

    checksum = 0;
    for (i = 0; i < msa->seqlen; ++i)
        checksum += ((i % 57 + 1) * (int) toupper(msa->seq[seq][i]));

    msa->checksum[seq] = checksum % 10000;

    return (msa->checksum[seq]);
}


int
GCGmultichecksum(MSA *msa)
{
    int             checksum;
    int             i;

    checksum = 0;
    for (i = 0; i < msa->seqnum; ++i)
    {
        if (msa->flag[i] == 0)
        {
            if (msa->checksum[i] != 0)
            {
                checksum += msa->checksum[i];
            }
            else
            {
                checksum += GCGchecksum(msa, i);
            }
        }
    }

    msa->msachecksum = checksum % 10000;
    return (msa->msachecksum);
}


void
reada2m(char *msafile_name, MSA *msa)
{
    int             i, j;
    int             ch;
    FILE           *msafile = NULL;

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR69: cannot open file \"%s\" \n", msafile_name);
        exit(EXIT_FAILURE);
    }

    strncpy(msa->filename, msafile_name, strlen(msafile_name));

    /* get the number of sequences in the a2m file  */
    msa->allocnum = 0;
    do
    {
        ch = getc(msafile);
        if (ch == '>')
            ++msa->allocnum;
    }
    while (ch != EOF);

    /* get the length of the alignment (per sequence) */
    rewind(msafile);
    msa->alloclen = 0;
    do
    {
        ch = getc(msafile);
    }
    while (ch != '\n' && ch != EOF);

    while (1)
    {
        ch = getc(msafile);
        if (ch == '>' || ch == EOF)
            break;
        if (!isspace(ch))
            ++(msa->alloclen);
    }

    /* now allocate space for the sequences  */
    MSAalloc(msa, msa->allocnum, msa->alloclen, 8192); /* max name size is 8192 characters */
    msa->seqlen = msa->alloclen;
    msa->seqnum = msa->allocnum;

    rewind(msafile);
    ch = getc(msafile); /* should be the first ">" - so skip it  */

    for (i = 0; i < msa->seqnum; ++i)
    {
        /* read the name, id, etc. first */
        for (j = 0; j < 8192; ++j)
        {
            ch = getc(msafile);
            if (ch == '\n' || ch == EOF)
                break;
            msa->name[i][j] = ch;
        }

        msa->name[i][j] = '\0';
        if (strlen(msa->name[i]) < 1)
        {
            fprintf(stderr, "\n ERROR7: sequence %d in a2m file has no name. \n\n", i+1);
            exit(EXIT_FAILURE);
        }

        for (j=0; j < msa->seqlen; ++j)
        {
            ch = getc(msafile);
            while (isspace(ch))
                ch = getc(msafile);
            //msa->seq[i][j] = toupper(ch);
            msa->seq[i][j] = ch;
        }
        msa->seq[i][j] = '\0'; /* for good measure */
        ch = skipspace(msafile);
        /* at this point last character should be the first ">" of the next sequence */
    }

    fclose(msafile);
}


void
reada2m2(char *msafile_name, MSA *msa)
{
    int             i, j, k, filesize;
    FILE           *msafile = NULL;
    char           *filestr = NULL;

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR69: cannot open file \"%s\" \n", msafile_name);
        exit(EXIT_FAILURE);
    }

    filestr = slurpfile(msafile);
    fclose(msafile);
    filesize = strlen(filestr);

    /* get the number of sequences in the a2m file  */
    msa->allocnum = 0;
    for (i = 0; i < filesize; ++i)
    {
        if (filestr[i] == '>')
            ++msa->allocnum;
    }

    /* get the length of the alignment (per sequence) */
    msa->alloclen = 0;
    i=0;
    while (filestr[i] != '\n' && i < filesize)
        ++i;

    while (i < filesize)
    {
        if (filestr[i] == '>' || i >= filesize)
            break;
        if (!isspace(filestr[i]))
            ++(msa->alloclen);
        ++i;
    }

    /* now allocate space for the sequences  */
    MSAalloc(msa, msa->allocnum, msa->alloclen, 8192); /* max name size is 8192 characters */
    msa->seqlen = msa->alloclen;
    msa->seqnum = msa->allocnum;

    k = 1; /* skip first '>' */
    for (i = 0; i < msa->seqnum; ++i)
    {
        if (k >= filesize)
            break;
        /* read the name, id, etc. first */
        for (j = 0; j < 8192; ++j)
        {
            if (filestr[k] == '\n' || k >= filesize)
                break;
            msa->name[i][j] = filestr[k];
            ++k;
        }

        msa->name[i][j] = '\0';
        if (strlen(msa->name[i]) < 1)
        {
            fprintf(stderr, "\n ERROR7: sequence %d in a2m file has no name. \n\n", i+1);
            exit(EXIT_FAILURE);
        }

        for (j=0; j < msa->seqlen; ++j)
        {
            while (isspace(filestr[k]) && k < filesize)
                ++k;
            msa->seq[i][j] = toupper(filestr[k]);
            if (k >= filesize)
                break;
            ++k;
        }
        msa->seq[i][j] = '\0'; /* for good measure */
        while (isspace(filestr[k]) && k < filesize)
            ++k;
        ++k;
        /* at this point last character should be just past the first ">" of the next sequence */
    }

    MyFree(filestr);
}


/* readaln() -
   Read clustalw 1.83 format alignment files */
void
readaln(char *msafile_name, MSA *msa)
{
    int             i, j;
    int             whitespace;
    int             name_len, block_count, block_seq_len, alignment_len, line_len;
    int             ch;
    char           *line = NULL;
    char           *buff = NULL;
    char            name[64], newname[64];   /* clustalw 1.83 format allows 30 character names maximum */
    long            seq_start;
    FILE           *msafile = NULL;

    line = (char *) malloc(512 * sizeof(char));

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR15: file \"%s\" not found. \n\n", msafile_name);
        exit(EXIT_FAILURE);
    }
    rewind(msafile);

    strncpy(msa->filename, msafile_name, strlen(msafile_name));

    /* get the number of sequences in the aln file */
    /* first skip "CLUSTAL" header line */
    fgets(line, 512, msafile);
    skipspace(msafile);
    seq_start = ftell(msafile) - 1; /* mark the beginning of seq1 name */

    /* next count number of sequences in first block in file */
    fseek(msafile, seq_start, SEEK_SET); /* move to beginning of name0, block0 */
    if (fgets(line, 512, msafile) == NULL)
    {
        fprintf(stderr,
                "\n  ERROR010: CLUSTAL file \"%s\" appears to have no data \n\n",
                msafile_name);
        exit(EXIT_FAILURE);
    }

    /* get the name of sequence #0 */
    if (sscanf(line, "%30s", name) == EOF)
    {
        fprintf(stderr,
                "\n  ERROR011: CLUSTAL file \"%s\" appears to have no data \n\n",
                msafile_name);
        exit(EXIT_FAILURE);
    }

    msa->allocnum = 1;
    while(!feof(msafile))
    {
        if (fgets(line, 512, msafile) == NULL)
            break;

        if (isspace(line[0]) || sscanf(line, "%30s", newname) == EOF)
            break;
        else
        {
            if (strncmp(name, newname, 64) == 0)
                break;
            else
                ++msa->allocnum;
        }
    }
//printf("\nmsa->allocnum = %d", msa->allocnum); fflush(NULL);
    /* get the (maximum) length of the alignment (per sequence) */
    /* first find the length of one line in a block */
    fseek(msafile, seq_start, SEEK_SET); /* move to beginning of name0, block0 */
    line_len   = 0;                      /* count sequence length in first block */
    whitespace = 0;
    do
    {
        ch = getc(msafile);
        if (ch == EOF)
            break;
        if (isspace(ch))
            ++whitespace; /* count spaces in line */
        ++line_len;
    }
    while (ch != '\n');
//printf("\nline_len = %d", line_len); fflush(NULL);
    name_len = strlen(name);
    block_seq_len = line_len - whitespace - name_len; /* seq length per block */
//printf("\nblock_seq_len = %d", block_seq_len); fflush(NULL);
    /* count remaining blocks */
    block_count = 1;
    while (!feof(msafile))
    {
        fgets(line, line_len + 64, msafile); /* must do at least +2 to get endline character */
        if (strncmp(line, name, name_len) == 0)
            ++block_count;
    }

    /* allocate space for the sequences */
    alignment_len = block_count * block_seq_len; /* includes any whitespace at end of last block, so we over-allocate */
    MSAalloc(msa, msa->allocnum, alignment_len, 32);

    /* allocate space for a line from the alignment (if alignment is not interleaved, this could be very large) */
    MyFree(line);
    line = (char *) malloc((line_len + 3) * sizeof(char));
    buff = (char *) malloc((block_seq_len + 3) * sizeof(char));
    if (line == NULL || buff == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR74: could not allocate memory for line or buff in function readaln(). \n\n");
        exit(EXIT_FAILURE);
    }

    /* now read sequences from file into MSA object */
    fseek(msafile, seq_start, SEEK_SET); /* back to beginning of first block */

    for(i=0; i < block_count; ++i)
    {
        for(j=0; j < msa->allocnum; ++j)
        {
            fgets(line, line_len + 64, msafile);       /* read line */
            sscanf(line, "%s%s%*d", msa->name[j], buff); /* read seq into buff */

            strtoupper(buff);
            strcat(msa->seq[j], buff);
        }

        do
        {
            ch = getc(msafile);
        }
        while(isspace(ch) || ch == '!' || ch == '.' || ch == ':' || ch == '*');

        ungetc(ch, msafile);
    }

    msa->seqlen = strlen(msa->seq[0]);
    msa->seqnum = msa->allocnum;

//    MSAprint(msa);
//    exit(1);

    fclose(msafile);
    MyFree(line);
    MyFree(buff);
}


/* readnex() -
   This monster parses NEXUS files! Woohoo!!
   It seems to work, and somewhat generally.
   It currently cannot handle nested comments. */
void
readnex(char *msafile_name, MSA *msa)
{
    int             i, j, k, l;
    int             filesize;
    int             ntax, nchar;
    int             block;
    char            ch;
    char            gap;
    char            *filestr;
    char           *begin_data, *end_data, *dimensions, *format, *matrix;
    char          **endptr = NULL;
    char            tmp_name[128];
    FILE           *msafile = NULL;

    begin_data = end_data = dimensions = format = matrix = NULL;

    /* get the size of the NEXUS file and allocate space to slurp it in           */
    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR15: file \"%s\" not found. \n\n", msafile_name);
        exit(EXIT_FAILURE);
    }
    rewind(msafile);

    filesize = getfilesize(msafile);

    filestr = (char *) malloc((filesize + 10) * sizeof(char));
    if (filestr == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR74: could not allocate memory in for filestr in function readnex(). \n\n");
        exit(EXIT_FAILURE);
    }

    /* slurp it in to 'filestr', sans comments                                   */
    rewind(msafile);

    i = j = 0;
    while(j < filesize)
    {
        ch = getc(msafile);
        ++j;
        if (ch == (char) EOF)
            break;
        if(ch == '[') /* 91 = '[' */
        {
            while(ch != ']') /* this bonks on nested comments */
            {
                ch = getc(msafile);/* 93 = ']' */
                ++j;
            }
            ch = getc(msafile);
            ++j;
        }
        filestr[i] = ch;
        /*fputc(ch, stderr);*/
        ++i;
    }

    if (i == 0)
    {
        fprintf(stderr, "\n ERROR332: NEXUS file does not contain data \n\n");
        exit(EXIT_FAILURE);
    }

    strtolower(filestr);

    /* find the beginning and end of the DATA block (or the preferred CHARACTERS  */
    /* block according to the docs)                                               */

    begin_data = strstr(filestr, "begin data");
    if(begin_data == NULL)
    {
        begin_data = strstr(filestr, "characters");
        if(begin_data)
            begin_data = strchr(begin_data, ';') + 1;

        if(begin_data == NULL)
        {
            fprintf(stderr, "\n ERROR333: NEXUS file does not contain a valid data or characters block: ");
            fprintf(stderr, "\n           no 'begin data;' or 'begin characters;' \n\n");
            exit(EXIT_FAILURE);
        }
    }

    format = strstr(begin_data, "format") + strlen("format");
    if(format == (char *) strlen("format"))
    {
        fprintf(stderr, "\n ERROR334: NEXUS file does not contain a valid 'format' declaration \n\n");
        exit(EXIT_FAILURE);
    }

    dimensions = strstr(begin_data, "dimensions") + strlen("dimensions");
    if(dimensions == (char *) strlen("dimensions"))
    {
        fprintf(stderr, "\n ERROR335: NEXUS file does not contain a valid 'dimensions' declaration \n\n");
        exit(EXIT_FAILURE);
    }

    end_data = strstr(dimensions, "end;");
    if(end_data == NULL)
    {
        fprintf(stderr, "\n ERROR336: NEXUS file does not contain a valid data block: no 'end;' \n\n");
        exit(EXIT_FAILURE);
    }

    /* get gap character if it exists                                             */

    gap =  getvalc(format, "gap", '=', endptr);

    /* Read NTAX and NCHAR dimensions data                                        */
    ntax =  getvali(dimensions, "ntax", '=', endptr);
    if(endptr == &dimensions)
    {
        fprintf(stderr, "\n ERROR55: NEXUS file does not contain NTAX dimensions \n\n");
        exit(EXIT_FAILURE);
    }

    nchar = getvali(dimensions, "nchar", '=', endptr);
    if(endptr == &dimensions)
    {
        fprintf(stderr, "\n ERROR57: NEXUS file does not contain NCHAR dimensions \n\n");
        exit(EXIT_FAILURE);
    }

    /* now allocate space for the sequences                                       */

    msa->allocnum = ntax;
    msa->alloclen = nchar;

    MSAalloc(msa, msa->allocnum, msa->alloclen, 128);

    /* find the MATRIX declaration (beginning of sequence blocks)                 */

    matrix = strstr(dimensions, "matrix") + strlen("matrix");
    if(matrix == (char *) strlen("matrix"))
    {
        fprintf(stderr, "\n ERROR337: NEXUS file has no \"matrix\" data \n\n");
        exit(EXIT_FAILURE);
    }

    /* HERE'S THE REAL DEAL! -- slurp in the matrix data                          */

    /* read in multiple blocks of data, if they exist --
       check if the taxa names held in msa->name are the same as
       from first block */

    i = 0;     /* matrix index (raw data string we are reading) */
    j = 0;     /* present taxa index                            */
    k = 0;     /* msa->name string index                        */
    l = 0;     /* msa->seq string position                      */
    block = 0; /* current block index                           */

    while((int)strlen(msa->seq[ntax-1]) < nchar) /* loop over all blocks, resetting j each time (j = taxa index) */
    {
        j = 0;
        while(j < ntax) /* loop over all taxa in a block */
        {
            l = strlen(msa->seq[j]); /* current l is the length of the sequence so far */

            /* skip over any initial space */
            while(isspace(matrix[i]))
                ++i;

            /* if this is the first pass, read the name into the MSA -
               if this is a subsequent pass (other interleaved blocks),
               check to see if the names match as they should */
            if(block == 0)
            {
                k = 0;
                /* read in the name of taxa j */
                while(!isspace(matrix[i]))
                {
                    if(matrix[i] == ';')
                        goto Hell;
                    msa->name[j][k] = matrix[i];
                    ++i;
                    ++k;
                }
                msa->name[j][k] = '\0';
            }
            else
            {
                /* check the name of taxa j */
                k = 0;
                while(!isspace(matrix[i]))
                {
                    if(matrix[i] == ';')
                        goto Hell;
                    tmp_name[k] = matrix[i];
                    ++i;
                    ++k;
                }
                tmp_name[k] = '\0';

                if(strcmp(tmp_name, msa->name[j]))
                {
                    fprintf(stderr, "\n ERROR336: NEXUS file has a taxon name problem: \n");
                    fprintf(stderr, "\n           '%s' from first block vs. '%s' from block %d \n\n",
                            msa->name[j], tmp_name, block);
                    exit(EXIT_FAILURE);
                }
            }

            /* skip over space between name and characters */
            while(isspace(matrix[i]))
                ++i;

            /* read in the character data up until the newline */
            while((matrix[i] != '\r') &&
                  (matrix[i] != '\n') &&
                  (matrix[i] != '\f') &&
                  (l < nchar))
            {
                if(matrix[i] == ';')
                    goto Hell;
                else if(isspace(matrix[i])) /* if space in the sequence, skip over it */
                {
                    ++i;
                    continue;
                }
                else if(matrix[i] == gap || /* replace NEXUS specified gap characters with a dash '-' */
                        matrix[i] == '_')   /* The underbar '_' is my own metacharacter, not clustals' -- I use it in the */
                    msa->seq[j][l] = '-';   /* MarkovPolo mod_perl module as a sub for weird tainted metacharacters       */
                else
                    msa->seq[j][l] = matrix[i];
                ++i;
                ++l;
            }
            msa->seq[j][l] = '\0'; /* null terminate msa->seq so we can get its length with strlen */
            ++j; /* increment taxa index */
        }
        ++block;
    }

    msa->seqnum = msa->allocnum;
    msa->seqlen = msa->alloclen;

    fclose(msafile);
    MyFree(filestr);

    /* I've always wanted to do this */
    Hell:
    {
        fprintf(stderr, "\n ERROR666: NEXUS file has an unexpected terminating ';' ");
        fprintf(stderr, "\n           (perhaps incorrect NTAX or NCHAR?)         \n");
        exit(EXIT_FAILURE);
    }
}


void
readnexd(char *msafile_name, MSA *msa)
{
    int             i, j;
    int             filesize;
    int             ntax;
    int             diagonal = 1, format_diag = 0;
    char            ch;
    char            *filestr;
    char           *begin_dist, /* *end_dist, */ *format, *triangle, *matrix;
    char          **endptr = NULL;
    FILE           *msafile = NULL;

    begin_dist = /* end_dist = */ format = triangle = matrix = NULL;

    /* get the size of the NEXUS file and allocate space to slurp it in           */
    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR15: file \"%s\" not found. \n\n", msafile_name);
        exit(EXIT_FAILURE);
    }

    filesize = getfilesize(msafile);

    filestr = (char *) calloc((filesize + 10), sizeof(char));
    if (filestr == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR74: could not allocate memory in for filestr in function readnex(). \n\n");
        exit(EXIT_FAILURE);
    }

    /* slurp it in to 'filestr', sans comments                                   */
    rewind(msafile);

    i = j = 0;
    while(j < filesize)
    {
        ch = getc(msafile);
        ++j;
        if (ch == (char) EOF)
            break;
        if(ch == '[') /* 91 = '[' */
        {
            while(ch != ']') /* this bonks on nested comments */
            {
                ch = getc(msafile);/* 93 = ']' */
                ++j;
            }
            ch = getc(msafile);
            ++j;
        }
        filestr[i] = ch;
        /*fputc(ch, stderr);*/
        ++i;
    }

    if (i == 0)
    {
        fprintf(stderr, "\n ERROR332: NEXUS file does not contain data \n\n");
        exit(EXIT_FAILURE);
    }

    strtoupper(filestr);

    /* Read NTAX                                                                  */
    ntax =  getvali(filestr, "ntax", '=', endptr);
    if(endptr == &filestr)
    {
        fprintf(stderr, "\n ERROR55: NEXUS file does not contain NTAX dimensions \n\n");
        exit(EXIT_FAILURE);
    }

    /* find the beginning and end of the DATA block (or the preferred CHARACTERS  */
    /* block according to the docs)                                               */
    begin_dist = strstr(filestr, "begin distances");
    if(begin_dist == NULL)
    {
        fprintf(stderr, "\n ERROR333: NEXUS file does not contain a valid distances block: ");
        fprintf(stderr, "\n           no 'begin distances;'' \n\n");
        exit(EXIT_FAILURE);
    }

    format = strstr(begin_dist, "format") + strlen("format");
    if(begin_dist == (char *) strlen("format"))
    {
        fprintf(stderr, "\n ERROR334: NEXUS file does not contain a valid 'format' declaration \n\n");
        exit(EXIT_FAILURE);
    }

    triangle = strstr(format, "triangle");
    if(triangle == NULL)
    {
        fprintf(stderr, "\n ERROR336: NEXUS file does not specify format of distance matrix: no 'triangle' declaration \n\n");
        exit(EXIT_FAILURE);
    }

    if (strstr(format, "nodiagonal"))
        diagonal = 0;
    else
        diagonal = 1;

    /* now allocate space for the data                                            */
    msa->allocnum = ntax;
    msa->alloclen = 0;
    msa->distance = 1;

    MSAalloc(msa, msa->allocnum, msa->alloclen, 128);
    DISTalloc(msa);

    /* find the MATRIX declaration (beginning of distance blocks)                 */
    matrix = strstr(begin_dist, "matrix") + strlen("matrix");
    if(matrix == (char *) strlen("matrix"))
    {
        fprintf(stderr, "\n ERROR337: NEXUS file has no \"matrix\" data \n\n");
        exit(EXIT_FAILURE);
    }

    /* HERE'S THE REAL DEAL! -- slurp in the matrix data                          */
    triangle = getvals(triangle, "triangle", '=', endptr);

    if (strncmp(triangle, "both", 4) == 0)
        format_diag = 1; /* full */
    else if (strncmp(triangle, "upper", 5) == 0 && diagonal == 1)
        format_diag = 2; /* upper diagonal */
    else if (strncmp(triangle, "upper", 5) == 0 && diagonal == 0)
        format_diag = 3; /* upper */
    else if (strncmp(triangle, "lower", 5) == 0 && diagonal == 1)
        format_diag = 4; /* lower diagonal */
    else if (strncmp(triangle, "lower", 5) == 0 && diagonal == 0)
        format_diag = 5; /* lower */

    nexus_dist(msa, filestr, filesize, format_diag);

    msa->seqnum = msa->allocnum;
    msa->seqlen = msa->alloclen;

    fclose(msafile);
    MyFree(filestr);
    MyFree(triangle);
}


void
nexus_dist(MSA *msa, char *filestr, int filesize, int format)
{
    int            i, j, k;

    k = 0; /* move through the first line with the digit */
    while(isspace(filestr[k]) && k < filesize)
        ++k;
    for (i = 0; i < msa->seqnum; ++i)
    {
        /* read the name, id, etc. first */
        for (j = 0; j < 10 && k < filesize; ++j, ++k)
            msa->name[i][j] = filestr[k];

        j = 0;
        while (!isspace(filestr[k]) && isprint(filestr[k]) && k < filesize)
        {
            msa->name[i][j] = filestr[k];
            ++k;
            if (j == 15)
            {
                while(!isspace(filestr[k]) && isprint(filestr[k]) && k < filesize)
                    ++k;
                break;
            }
            ++j;
        }

        msa->name[i][j+1] = '\0';

        if (strlen(msa->name[i]) < 1)
        {
            fprintf(stderr, "\n ERROR7: sequence %d in a2m file has no name. \n\n", i+1);
            exit(EXIT_FAILURE);
        }

        switch(format)
        {
            case 1: /* full */
                for (j = 0; j < msa->seqnum && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                break;
            case 2: /* lower  */
                for (j = i+1; j < msa->seqnum && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                msa->dist[i][i] = 0.0;
                break;
            case 3: /* lower diagonal */
                for (j = i; j < msa->seqnum && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                break;
            case 4: /* upper */
                for (j = 0; j < i && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                msa->dist[i][i] = 0.0;
                break;
            case 5: /* upper diagonal */
                for (j = 0; j <= i && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                break;
        }
        while(isspace(filestr[k]) && k < filesize)
            ++k;
    }
}



/* ********************************************************************** */
/* NEXUS-style Key:Value reading functions                                */
/* ********************************************************************** */
/* These four functions get key:value pairs of the form
   'key=value' or 'key = value', etc. from a string --
   whitespace between key and value is ignored.
   These are used in 'readnex()' for NEXUS format stuff.
   Note the return types.

   The 'endptr' argument can be NULL, but if the value
   is not found endptr = &string, so you can check.
   getvalc() only returns isgraph() characters
   (no whitespace or control characters) */

int
getvali(char *string, char *key, char delimiter, char **endptr)
{
    char           *key_p = NULL;
    int             val = 0;
    long int        i, j;
    char           *key_len = NULL;

    key_p = strstr(string, key) + strlen(key);
    key_len = (char *) strlen(string) - key_p + string;

    i = j = 0;
    while((isspace(key_p[i]) || (key_p[i] == delimiter)) &&
          i < (long int)key_len)
        ++i;

    val = (int) strtol(&key_p[i], endptr, 10);
    if(*endptr == &key_p[i])
        endptr = &string;

    return (val);
}


double
getvald(char *string, char *key, char delimiter, char **endptr)
{
    char           *key_p = NULL;
    double          val = 0.0f;
    long int        i, j;
    char           *key_len = NULL;

    i = j = 0;

    key_p = strstr(string, key) + strlen(key);
    key_len = (char *)strlen(string) - key_p + string;

    while((isspace(key_p[i]) || (key_p[i] == delimiter)) &&
          i < (long int) key_len)
        ++i;

    val = strtod(&key_p[i], endptr);
    if(*endptr == &key_p[i])
        endptr = &string;

    return (val);
}


/* if you're good you'll free the returned string */
char
*getvals(char *string, char *key, char delimiter, char **endptr)
{
    char           *key_p = NULL;
    char           *val = NULL;
    long int        i, j;
    char           *key_len = NULL;

    i = j = 0;

    val = malloc(strlen(string) * sizeof(char));

    key_p = strstr(string, key) + strlen(key);
    key_len = (char *) strlen(string) - key_p + string;

    while((isspace(key_p[i]) || (key_p[i] == delimiter)) &&
          i < (long int) key_len)
        ++i;

    while(!isspace(key_p[i]))
    {
        val[j] = key_p[i];
        ++i;
        ++j;
    }
    val[j] = '\0';

    if(strlen(val) == 0)
    {
        endptr = &string;
        return(string);
    }
    else
        return(val);
}


char
getvalc(char *string, char *key, char delimiter, char **endptr)
{
    char           *key_p = NULL;
    long int        i, j;
    char           *key_len = NULL;

    i = j = 0;

    key_p = strstr(string, key) + strlen(key);
    key_len = (char *)strlen(string) - key_p + string;

    while((isspace(key_p[i]) || (key_p[i] == delimiter)) &&
          i < (long int) key_len)
        ++i;

    if(isgraph(key_p[i]))
    {
        return(key_p[i]);
    }
    else
    {
        endptr = &string;
        return('\0');
    }
}


void
readphylip(char *msafile_name, MSA *msa)
{
    int             i, j, k, start, seqpos, scannum;
    int             bufflen = 4096;
    char            name[1024];
    char           *line = NULL;
    char           *buff = NULL;
    FILE           *msafile = NULL;

    line = (char *) malloc(bufflen * sizeof(char));
    buff = (char *) malloc(bufflen * sizeof(char));
    if (line == NULL || buff == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR74: could not allocate memory in function readphylip(). \n\n");
        exit(EXIT_FAILURE);
    }

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR15: file \"%s\" not found. \n\n", msafile_name);
        exit(EXIT_FAILURE);
    }

    /* get the number of sequences in the phylip file */
    fgets(line, bufflen, msafile);
    sscanf(line, "%d %d", &msa->seqnum, &msa->seqlen);
    printf("\nseqnum:%d seqlen:%d", msa->seqnum, msa->seqlen);

    msa->allocnum = msa->seqnum;

    if (msa->allocnum == 0)
    {
        fprintf(stderr,
                "\n ERROR: Phylip file \"%s\" appears to have no data \n\n",
                msafile_name);
        exit(EXIT_FAILURE);
    }

    /* now allocate space for the sequences */
    MSAalloc(msa, msa->allocnum, msa->seqlen, 128);

    seqpos = k = 0;
    for(j = 0; j < msa->allocnum; ++j)
    {
        fgets(line, bufflen, msafile);    /* read line */
        sscanf(line, "%s", msa->name[j]); /* read name */
        //printf("\n%s", msa->name[j]);

        k = 0;
        for (i = strlen(msa->name[j]); i < strlen(line); ++i)
        {
            if (isalpha(line[i]) || line[i] == '-' || line[i] == '?' || line[i] == '.')
            {
                msa->seq[j][k] = line[i];
                ++k;
            }
        }
    }

    /* now read sequences from file into MSA object */
    while(!feof(msafile))
    {
        seqpos = k;
        //printf("\nseqlen:%d seqpos:%d", msa->seqlen, seqpos);
        if (seqpos >= msa->seqlen)
            break;

        //printf("\nseqpos:%d", seqpos);
        for(j = 0; j < msa->allocnum; ++j)
        {
            fgets(line, bufflen, msafile); /* read line */
            scannum = sscanf(line, "%s", &name[0]); /* read name? */

            if (scannum <= 0)
            {
                --j;
                continue;
            }

            if (strcmp(name, msa->name[j]) == 0)
                start = strlen(msa->name[j]);
            else
                start = 0;

            //printf("\n%d %s", seqpos, &line[start]);

            k = seqpos;
            for (i = start; i < strlen(line); ++i)
            {
                if (isalpha(line[i]) || line[i] == '-' || line[i] == '?' || line[i] == '.')
                {
                    msa->seq[j][k] = line[i];
                    ++k;
                }
            }
        }
    }

    msa->seqlen = strlen(msa->seq[0]);
    msa->seqnum = msa->allocnum;

    fclose(msafile);
    MyFree(line);
    MyFree(buff);
}


void
readphylipd(char *msafile_name, MSA *msa)
{
    int             filesize, format;
    FILE           *msafile = NULL;
    char           *line = NULL;
    char           *filestr = NULL;

    line = (char *) calloc(2048, sizeof(char));

    msafile = fopen(msafile_name, "r");
    if (msafile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR69: cannot open file \"%s\" \n", msafile_name);
        exit(EXIT_FAILURE);
    }

    /* get the number of taxa in the phylip dist file  */
    rewind(msafile);
    fgets(line, 255, msafile);
    sscanf(line, "%d", &msa->seqnum);
    msa->allocnum = msa->seqnum;
    msa->alloclen = 0;
    msa->distance = 1;

    filestr = slurpfile(msafile);
    filesize = strlen(filestr);
    /*printfile(msafile, stdout);*/

    /* now allocate space for the taxa names and the dist matrix */
    MSAalloc(msa, msa->seqnum, msa->alloclen, 8192); /* max name size is 8192 characters */
    msa->seqlen = msa->alloclen;
    DISTalloc(msa);

    /* figure out if the matrix is upper, lower, both, and/or diagonal */
    format = phylip_dist_format(msa, filestr, filesize);

    printf("\n*** format = %d\n", format);
    fflush(NULL);

    if (format == 0)
    {
        fprintf(stderr,
                "\n ERROR72: phylip distance file \"%s\" is in unknown format \n",
                msafile_name);
        exit(EXIT_FAILURE);
    }

    phylip_dist(msa, filestr, filesize, format);

    MyFree(filestr);
    MyFree(line);
}


/* determines the format of a PHYLIP distance file, upper, lower, diagonal, and/or full */
int
phylip_dist_format(MSA *msa, char *filestr, int filesize)
{
    int             i, distances1, distances2;

    i = 0; /* move through the first line with the digit */
    while((isspace(filestr[i]) || isdigit(filestr[i])) && i < filesize)
        ++i;
    /* move through the first taxon name */
    while(!isspace(filestr[i]) && i < filesize)
        ++i;
    /* move through the spaces after the taxon name */
    while(isspace(filestr[i]) && i < filesize)
        ++i;

    /* count the number of distance entries for the first taxon */
    distances1 = 0;
    while(distances1 < msa->seqnum)
    {
        if(isalpha(filestr[i]) && filestr[i] != 'e')
            break;
        while((isdigit(filestr[i]) ||
               filestr[i] == '-' ||
               filestr[i] == '.' ||
               filestr[i] == 'e') &&
              i < filesize)
            ++i;
        while(isspace(filestr[i]) && i < filesize)
            ++i;
        ++distances1;
    }

    /* move through the second taxon name */
    while(!isspace(filestr[i]) && i < filesize)
        ++i;
    /* move through the spaces after the second taxon name */
    while(isspace(filestr[i]) && i < filesize)
        ++i;

    distances2 = 0;
    while(distances2 < msa->seqnum)
    {
        if(isalpha(filestr[i]) && filestr[i] != 'e')
            break;
        while((isdigit(filestr[i]) ||
               filestr[i] == '-' ||
               filestr[i] == '.' ||
               filestr[i] == 'e') &&
              i < filesize)
            ++i;
        while(isspace(filestr[i]) && i < filesize)
            ++i;
        ++distances2;
    }

    if (distances1 == distances2 && distances1 == msa->seqnum)
        /* full */
        return (1);
    else if (distances1 == msa->seqnum -1 && distances2 == msa->seqnum - 2)
        /* upper diagonal */
        return (2);
    else if (distances1 == msa->seqnum && distances2 == msa->seqnum - 1)
        /* upper */
        return (3);
    else if (distances1 == 0 && distances2 == 1)
        /* lower diagonal */
        return (4);
    else if (distances1 == 1 && distances2 == 2)
        /* lower */
        return (5);
    else
        return (0);
}


/* reads a phylip full format distance file from a filestring */
void
phylip_dist(MSA *msa, char *filestr, int filesize, int format)
{
    int            i, j, k, m;

    k = 0; /* move through the first line with the digit */
    while((filestr[k] != '\n') && k < filesize)
    {
        //printf("\n%d %c", k, filestr[k]);
        ++k;
    }
    ++k;

    //printf("\nlast %d %c bbb", k, filestr[k]);
    //printf("\n\n");
    //fflush(NULL);

    for (i = 0; i < msa->seqnum; ++i)
    {
        //printf("\nHERE\n");
        /* read the name, id, etc. first */
        for (j = 0; j < 10 && k < filesize; ++j, ++k)
            msa->name[i][j] = filestr[k];

        j=9; /* get rid of trailing spaces */
        while(j >= 0 && isspace(msa->name[i][j]))
            --j;
        msa->name[i][j+1] = '\0';
        for(m = 0; m < j; ++m) /* replace internal spaces and dashes with a '_' */
            if(isspace(msa->name[i][m]) || msa->name[i][m] == '-')
                msa->name[i][m] = '_';

        if (strlen(msa->name[i]) < 1)
        {
            fprintf(stderr, "\n ERROR7: sequence %d in a2m file has no name. \n\n", i+1);
            exit(EXIT_FAILURE);
        }

        switch(format)
        {
            case 1: /* full */
                for (j = 0; j < msa->seqnum && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                break;
            case 2: /* lower  */
                for (j = i+1; j < msa->seqnum && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                msa->dist[i][i] = 0.0;
                break;
            case 3: /* lower diagonal */
                for (j = i; j < msa->seqnum && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                break;
            case 4: /* upper */
                for (j = 0; j < i && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                msa->dist[i][i] = 0.0;
                break;
            case 5: /* upper diagonal */
                for (j = 0; j <= i && k < filesize; ++j)
                {
                    while(isspace(filestr[k]) && k < filesize)
                        ++k;
                    /*sscanf(&filestr[k], "%lf", &msa->dist[i][j]);*/
                    msa->dist[i][j] = msa->dist[j][i] = (double) strtod(&filestr[k], NULL);
                    while(!isspace(filestr[k]) && k < filesize)
                        ++k;
                }
                break;
        }
        while(isspace(filestr[k]) && k < filesize)
            ++k;
    }

/*     printf("\n\n"); */
/*     for (i = 0; i < msa->seqnum; ++i) */
/*     { */
/*         printf("\n%d %s", i, msa->name[i]); */
/*     } */
/*     fflush(NULL); */

}


void
readmsf(char *msafile_name, MSA *msa)
{

}


/* ********************************************************************** */
/* writing alignment files                                                */
/* ********************************************************************** */

void
writea2m_seq(MSA *msa, int seqID, char *outfile_root)
{
    int i;

    for (i=0; i < msa->seqnum; ++i)
        msa->flag[i] = 1;
    msa->flag[seqID] = 0;

    writea2m(msa, 0, msa->seqlen, outfile_root);

    for (i=0; i < msa->seqnum; ++i)
        msa->flag[i] = 0;
}


void
writealn_seq(MSA *msa, int seqID, char *outfile_root)
{
    int i;

    for (i=0; i < msa->seqnum; ++i)
        msa->flag[i] = 1;
    msa->flag[seqID] = 0;
    writealn(msa, 0, msa->seqlen, outfile_root);
    for (i=0; i < msa->seqnum; ++i)
        msa->flag[i] = 0;
}


void
writenex_seq(MSA *msa, int seqID, char *outfile_root)
{
    int i;

    for (i=0; i < msa->seqnum; ++i)
        msa->flag[i] = 1;
    msa->flag[seqID] = 0;
    writenex(msa, 0, msa->seqlen, outfile_root);
    for (i=0; i < msa->seqnum; ++i)
        msa->flag[i] = 0;
}


void
writea2m(MSA *msa, int begin, int window, char *outfile_root)
{
    FILE          *outfile = NULL;
    char           outfile_name[256];
    int            linelen; /* linelen is current line length in an interleaved block */
    int            i, j, k;

    mystrncpy(outfile_name, outfile_root, FILENAME_MAX);
    strcat(outfile_name, ".a2m");

    outfile = fopen(outfile_name, "w");
    if (outfile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR19: file \"%s\" not found. \n\n", outfile_name);
        exit(EXIT_FAILURE);
    }

    /* see if the current sequence window is empty (matches "-" or "?" or ".")
       if so, flag it so it won't be printed out to the alignment file        */
    for (i = 0; i < msa->seqnum; ++i)
    {
        msa->flag2[i] = 0; /* gotta make sure to reset it to 0 */
        j = begin;
        while((msa->seq[i][j] == '-' ||
               msa->seq[i][j] == '?' ||
               msa->seq[i][j] == '.') &&
              j < begin + window)
            ++j;

        if (j >= begin + window)
            msa->flag2[i] = 1;
    }

    for (k = 0; k < msa->seqnum; ++k)
    {
        if ((msa->flag2[k] == 1) || (msa->flag[k] == 1))
                continue;

        fprintf(outfile, ">%s\n", msa->name[k]);
        linelen = 0;
        for (j = begin;
             j < begin + window && j < msa->seqlen;
             ++j)
        {
            fputc(msa->seq[k][j], outfile);
            ++linelen;

            if (linelen == MAXLINE-1)
            {
                linelen = 0;
                fputc('\n', outfile);
            }
        }
        fputc('\n', outfile);
    }

    fputc('\n', outfile);
    fclose(outfile);
}


void
writealn(MSA *msa, int begin, int window, char *outfile_root)
{
    FILE          *outfile = NULL;
    int            i, j, k, blocks;
    char          *outfile_name = NULL;

    outfile_name = (char *) malloc(256 * sizeof(char));
    if (outfile_name == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR24: Could not allocate memory for outfile_name in function writealn(). \n\n");
        exit(EXIT_FAILURE);
    }

    mystrncpy(outfile_name, outfile_root, FILENAME_MAX);
    strcat(outfile_name, ".aln");
    outfile = fopen(outfile_name, "w");
    if (outfile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR25: file \"%s\" not found. \n\n", outfile_name);
        exit(EXIT_FAILURE);
    }

    fprintf(outfile, "CLUSTAL W (1.83) multiple sequence alignment\n\n\n");

    if (window > (msa->seqlen - begin))
        window = (msa->seqlen - begin);

    for (i = 0; i < msa->seqnum; ++i)
    {
        msa->flag2[i] = 0;

        j = begin;
        while((msa->seq[i][j] == '-' ||
               msa->seq[i][j] == '?' ||
               msa->seq[i][j] == '.') &&
              j < begin + window)
            ++j;

//printf("flag2[i]:%d\n", msa->flag2[i] );

        if (j >= begin + window)
            msa->flag2[i] = 1;
    }

    blocks = (int) (window / MAXLINE + 1);
    //printf("blocks:%d\n", blocks);

    for(i = 0; i < blocks; ++i)
    {
        for(j = 0; j < msa->seqnum; ++j) /* for every sequence */
        {
            //printf("flag2[j]:%d flag[j]:%d\n", msa->flag2[j], msa->flag[j] );
            if ((msa->flag2[j] == 1) || (msa->flag[j] == 1))
                continue;

            if (msa->seq[j][begin + i*MAXLINE] == '\0')
            {
                fputc('\n', outfile);
                break;
            }

            /* fprintf(outfile, "%-30.30s      ", msa->name[j]); */
            fprintf(outfile, "%-16.16s      ", msa->name[j]);
            //printf("\n%d\n", j);fflush(NULL);

            for (k = begin + i*MAXLINE;
                 k < begin + window &&
                 k < msa->seqlen &&
                 k < (begin + i*MAXLINE + MAXLINE);
                 ++k)
            {
                if (msa->seq[j][k] == '\0')
                {
                    fputc('\n', outfile);
                    break;
                }
                fputc(msa->seq[j][k], outfile);
            }
            fputc('\n', outfile);
        }
        fputc('\n', outfile);
    }

    fputc('\n', outfile);
    fclose(outfile);
    MyFree(outfile_name);
}


void
writephylip(MSA *msa, int begin, int window, char *outfile_root)
{
    FILE          *outfile = NULL;
    int            i, j, k, blocks;
    char          *outfile_name = NULL;

    outfile_name = (char *) malloc(256 * sizeof(char));
    if (outfile_name == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR24: Could not allocate memory for outfile_name in function writealn(). \n\n");
        exit(EXIT_FAILURE);
    }

    mystrncpy(outfile_name, outfile_root, FILENAME_MAX);
    strcat(outfile_name, ".phy");
    outfile = fopen(outfile_name, "w");
    if (outfile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR25: file \"%s\" not found. \n\n", outfile_name);
        exit(EXIT_FAILURE);
    }

    fprintf(outfile, "%d %d\n", msa->seqnum, msa->seqlen);

    if (window > (msa->seqlen - begin))
        window = (msa->seqlen - begin);

    for (i = 0; i < msa->seqnum; ++i)
    {
        msa->flag2[i] = 0;

        j = begin;
        while((msa->seq[i][j] == '-' ||
               msa->seq[i][j] == '?' ||
               msa->seq[i][j] == '.') &&
              j < begin + window)
            ++j;

//printf("flag2[i]:%d\n", msa->flag2[i] );

        if (j >= begin + window)
            msa->flag2[i] = 1;
    }

    blocks = (int) (window / MAXLINE + 1);
    //printf("blocks:%d\n", blocks);

    for(i = 0; i < blocks; ++i)
    {
        for(j = 0; j < msa->seqnum; ++j) /* for every sequence */
        {
            //printf("flag2[j]:%d flag[j]:%d\n", msa->flag2[j], msa->flag[j] );
            if ((msa->flag2[j] == 1) || (msa->flag[j] == 1))
                continue;

            /* fprintf(outfile, "%-30.30s      ", msa->name[j]); */
            if (i == 0)
                fprintf(outfile, "%-20.20s ", msa->name[j]);

            for (k = begin + i*MAXLINE;
                 k < begin + window &&
                 k < msa->seqlen &&
                 k < (begin + i*MAXLINE + MAXLINE);
                 ++k)
            {
                fputc(msa->seq[j][k], outfile);
            }
            fputc('\n', outfile);
        }
        fputc('\n', outfile);
    }

    fputc('\n', outfile);
    fclose(outfile);
    MyFree(outfile_name);
}


void
writenex(MSA *msa, int begin, int window, char *outfile_root)
{
    FILE          *outfile = NULL;
    int            i, j, k, unflagged_seqnum, blocks, dnacount, rescount, protein = 0, morph = 0;
    char          *outfile_name = NULL;
    char           seqtype[16];

    outfile_name = (char *) malloc(256 * sizeof(char));
    if (outfile_name == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR20: Could not allocate memory for outfile_name in function writenex(). \n\n");
        exit(EXIT_FAILURE);
    }

    mystrncpy(outfile_name, outfile_root, FILENAME_MAX);
    strcat(outfile_name, ".nex");
    outfile = fopen(outfile_name, "w");
    if (outfile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR21: file \"%s\" not found. \n\n", outfile_name);
        exit(EXIT_FAILURE);
    }

    if (window > (msa->seqlen - begin))
        window = (msa->seqlen - begin);

    /* don't print out an empty window with all gaps */
    for (i = 0; i < msa->seqnum; ++i)
    {
        msa->flag2[i] = 0;
        j = begin;
        while((msa->seq[i][j] == '-' ||
               msa->seq[i][j] == '?' ||
               msa->seq[i][j] == '.') &&
              j < begin + window)
            ++j;

        if (j >= begin + window)
            msa->flag2[i] = 1;
    }

    for(i = 0, unflagged_seqnum = 0; i < msa->seqnum; ++i)
    {
        if((msa->flag[i] == 0) && msa->flag2[i] == 0)
            ++unflagged_seqnum;
    }

    /* try to figure out if this is DNA or protein or morphology */
    dnacount = rescount = 0;
    for(i = 0; i < msa->seqnum; ++i)
    {
        for(j = 0; j < msa->seqlen; ++j)
        {
            if(msa->seq[i][j] == 'e' ||
               msa->seq[i][j] == 'f' ||
               msa->seq[i][j] == 'i' ||
               msa->seq[i][j] == 'l' ||
               msa->seq[i][j] == 'p' ||
               msa->seq[i][j] == 'q' ||
               msa->seq[i][j] == 'z')
            {
                protein = 1;
                break;
            }

            if(isdigit(msa->seq[i][j]))
            {
                morph = 1;
                break;
            }

            if(msa->seq[i][j] == 'a' ||
               msa->seq[i][j] == 'c' ||
               msa->seq[i][j] == 't' ||
               msa->seq[i][j] == 'g')
            ++dnacount;

            if (msa->seq[i][j] != '-' &&
                msa->seq[i][j] != '?' &&
                msa->seq[i][j] != '.')
            ++rescount;
        }
        if(protein == 1 || morph == 1)
            break;
    }

    if(protein == 1)
        strcpy(seqtype, "protein");
    else if(morph == 1)
        strcpy(seqtype, "standard");
    else if ((double)dnacount/(double)rescount > 0.8)
        strcpy(seqtype, "dna");
    else
        strcpy(seqtype, "protein");

    fprintf(outfile, "#NEXUS\n\n");
    fprintf(outfile, "Begin data;\n");
    fprintf(outfile, "    Dimensions ntax=%d nchar=%d;\n", unflagged_seqnum, window);
    fprintf(outfile, "    Format datatype=%s interleave=yes gap=-;\n", seqtype);
    fprintf(outfile, "    Matrix");

    blocks = (int) (window / MAXLINE + 1);

    for(i = 0; i < blocks; ++i)
    {
        for(j=0; j < msa->seqnum; ++j) /* for every sequence */
        {
            if ((msa->flag[j] == 1) || (msa->flag2[j] == 1))
                continue;

            fprintf(outfile, "\n%16.16s  ", msa->name[j]);

            for (k = begin + i*MAXLINE;
                 k < begin + window &&
                 k < msa->seqlen &&
                 k < (begin + i*MAXLINE + MAXLINE);
                 ++k)
            {
                fputc(msa->seq[j][k], outfile);
                if ((k - begin + 1) % 10 == 0) /* every 10th character spit out a space */
                    fputc(' ', outfile);
            }
        }
        fputc('\n', outfile);
    }

    fprintf(outfile, "    ;\n");
    fprintf(outfile, "End;\n\n");

    fclose(outfile);
    MyFree(outfile_name);
}


void
writenexd(MSA *msa, char *outfile_root)
{
    FILE          *outfile = NULL;
    int            i, j;
    char           outfile_name[256];

    mystrncpy(outfile_name, outfile_root, FILENAME_MAX);
    strcat(outfile_name, ".nex");
    outfile = fopen(outfile_name, "w");
    if (outfile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR21: file \"%s\" not found. \n\n", outfile_name);
        exit(EXIT_FAILURE);
    }

    fprintf(outfile, "#NEXUS\n\n");
    fprintf(outfile, "begin taxa;\n");
    fprintf(outfile, "    dimensions ntax=%d;\n", msa->seqnum);
    fprintf(outfile, "    taxlabels\n");
    for (i = 0; i < msa->seqnum; ++i)
        fprintf(outfile, "        %-s\n", msa->name[i]);
    fprintf(outfile, "    ;\nend;\n\n");
    fprintf(outfile, "begin distances;\n");
    fprintf(outfile, "    format\n");
    fprintf(outfile, "        triangle=lower;\n");
    /*fprintf(outfile, "        missing=?;\n");*/
    fprintf(outfile, "    matrix\n");
    for (i = 0; i < msa->seqnum; ++i) /* for all taxa (down the row) */
    {
        fprintf(outfile, "        %-16.16s", msa->name[i]);
        for (j = 0; j <= i; ++j) /* for each column from 0 up to the diagonal */
        {
            fprintf(outfile, " %10.5f", msa->dist[i][j]);
        }

        fprintf(outfile, "\n"); /* end the row */
    }
    fprintf(outfile, "    ;\nend;\n\n");
    printf("\n NEXUS file \'%s\' written out. \n", outfile_name);

    fclose(outfile);
}


void
writepsib(MSA *msa, int begin, int window, char *outfile_root)
{
    FILE          *outfile = NULL;
    int            i, j, k, blocks;
    char          *outfile_name = NULL;

    outfile_name = (char *) malloc(256 * sizeof(char));
    if (outfile_name == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR23: Could not allocate memory for outfile_name in function writepsib(). \n\n");
        exit(EXIT_FAILURE);
    }

    mystrncpy(outfile_name, outfile_root, FILENAME_MAX);
    strcat(outfile_name, ".psib");
    outfile = fopen(outfile_name, "w");
    if (outfile == NULL)
    {
        perror("\n ERROR");
        fprintf(stderr, "\n ERROR25: file \"%s\" not found. \n\n", outfile_name);
        exit(EXIT_FAILURE);
    }

    if (window > (msa->seqlen - begin))
        window = (msa->seqlen - begin);

    for (i = 0; i < msa->seqnum; ++i)
    {
        msa->flag[i] = 0;
        j = begin;
        while((msa->seq[i][j] == '-' ||
               msa->seq[i][j] == '?' ||
               msa->seq[i][j] == '.') &&
              j < begin + window)
            ++j;

        if (j >= begin + window)
            msa->flag[i] = 1;
    }

    blocks = (int) (window / MAXLINE + 1);

    for(i = 0; i < blocks; ++i)
    {
        for(j=0; j < msa->seqnum; ++j) /* for every sequence */
        {
            if (msa->flag[j] == 1)
                continue;

            fprintf(outfile, "%-30.30s      ", msa->name[j]);

            for (k = begin + i*MAXLINE;
                 k < begin + window &&
                 k < msa->seqlen &&
                 k < (begin + i*MAXLINE + MAXLINE);
                 ++k)
            {
                fputc(msa->seq[j][k], outfile);
            }
            fputc('\n', outfile);
        }
        fputc('\n', outfile);
    }
    fclose(outfile);
    MyFree(outfile_name);
}


#define FSTMAXLINE 70


/* subname[] should be at least 64 in length */
int
scan_fasta_name(const char *fstname, char **subname, int nfield)
{
    //const char line[] = "2004/12/03 12:01:59;info1;;info2;info3";
    const char *ptr = fstname;
    char field[64];
    int n, items_read, cnt;

    cnt = 0;
    while (*ptr != '\0')
    {
        ++cnt;

        items_read = sscanf(ptr, "%63[^|]%n", field, &n);

        printf("field[%d] = \"%s\"\n", cnt, field);

        if (cnt == nfield)
        {
            printf("** field[%d] = \"%s\"\n", cnt, field);
            strncpy(*subname, field, 64);
            return(n);
        }

        if (items_read == 1)
            ptr += n; /* advance the pointer by the number of characters read */

        if ( *ptr != '|' )
            break; /* didn't find an expected delimiter, done? */

        ++ptr; /* skip the delimiter */

        //printf("field = \"%s\"\n", field);
        field[0]='\0';
    }

    strncpy(*subname, "", 64);

    return(0);
}


void
writefasta_explode(MSA *msa, int nfield)
{
    FILE           *outfile;
    int             linelen; /* linelen is current line length in an interleaved block */
    unsigned long   i, j;
    char            ch;
    char           *outfile_name = malloc(256 * sizeof(char));

    for (i = 0; i < (unsigned long) msa->seqnum; ++i)
    {
        /* see if the current sequence window is empty (matches "-" or "?" or ".") */
        j = 0;
        while(msa->seq[i][j] == '-' || msa->seq[i][j] == '?' || msa->seq[i][j] == '.')
        {
            ++j;
        }

        /* if so, don't write this file */
        if (j > strlen(msa->seq[i]))
            return;

        //sscanf(&msa->name[i][0], "%s", outfile_name); /* "1" to get rid of initial > */
        scan_fasta_name(&msa->name[i][0], &outfile_name, nfield);
        strcat(outfile_name, ".fst");
        outfile = fopen(outfile_name, "w");
        fprintf(outfile, ">%s\n", msa->name[i]);

        linelen = 0;
        for (j = 0; j < strlen(msa->seq[i]); ++j)
        {
            ch = msa->seq[i][j];
            if (isalpha(ch))
            {
                putc(ch, outfile);
                ++linelen;
                if (linelen == FSTMAXLINE)
                {
                    linelen = 0;
                    putc('\n', outfile);
                }
            }
        }

        putc('\n', outfile);
        fclose(outfile);
    }

    MyFree(outfile_name);
}

