/*
    Theseus - maximum likelihood superpositioning of macromolecular structures

    Copyright (C) 2004-2010 Douglas L. Theobald

    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the:

    Free Software Foundation, Inc.,
    59 Temple Place, Suite 330,
    Boston, MA  02111-1307  USA

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

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include "Error.h"
#include "lodmats.h"
#include "pdbMalloc.h"
#include "Cds.h"
#include "PDBCds.h"
#include "MultiPose2MSA.h"
#include "ProcGSLSVD.h"
#include "ProcGSLSVDOcc.h"
#include "msa.h"


static int
range_selxn(int index, int *lower, int *upper, int range_num);

static int
atom_selxn(char *name, int mode);

static const char atoms0[] = ":CA :C1*:";
static const char atoms1[] = ":N  :C  :O  :CA :";

static int
atom_selxn(char *name, int mode)
{
    switch(mode)
    {
        case 0: /* "CA :P  " */
            if (strstr(atoms0, name) != NULL)
                return (1);
            break;
        case 1: /* "N  :C  :O  :CA :" */
            if (strstr(atoms1, name) != NULL)
                return (1);
            break;
    }

    return (0);
}


static int
range_selxn(int index, int *lower, int *upper, int range_num)
{
    int             i;

    for (i = 0; i < range_num; ++i)
    {
        if (index >= lower[i] && index <= upper[i] )
            return (1);
    }

    return(0);
}


/*
char
aa3toaa1(char *aa3_s)
{
    int            index_i;

    index_i = (strstr(&aa3[0], aa3_s) - &aa3[0]) / 3;

    return (aa1[index_i]);
}


void
aa1toaa3(char aa1_c, char *aa3_s)
{
    int            i, index_i;

    index_i = (strchr(&aa1[0], aa1_c) - &aa1[0]) * 3;

    for (i = 0; i < 3; ++i)
        aa3_s[i] = aa3[index_i + i];

    aa3_s[3] = '\0';
}
*/


static void
VerifyAlignmentVsPDBs(PDBCdsArray *pdbA, CdsArray *baseA, MSA *msa,
                      const int atomsel, int *map)
{
    int             i, j, len, aalen;
    const int       cnum = pdbA->cnum;
    const int       seqlen = msa->seqlen;
    char           *seq = NULL;

    for (i = 0; i < cnum; ++i)
    {
        seq = msa->seq[map[i]];
        len = 0;
        for (j = 0; j < seqlen; ++j)
            if (seq[j] != '-')
                ++len;

        aalen = 0;
	    for (j = 0; j < pdbA->cds[i]->vlen; ++j)
			if (atom_selxn(pdbA->cds[i]->name[j], atomsel) == 1 &&
			    (pdbA->cds[i]->altLoc[j] == ' ' || pdbA->cds[i]->altLoc[j] == 'A'))
			    ++aalen;

        baseA->cds[i]->aalen = aalen;

        if (len != aalen)
        {
			fprintf(stderr, "\n\n  ERROR1122: PDB file '%s' and sequence '%s' in '%s'",
			        pdbA->cds[i]->filename, msa->name[map[i]], msa->filename);
			fprintf(stderr, "\n            have different lengths (%d vs %d)\n\n",
			       aalen, len);
            PrintTheseusTag();
			exit(EXIT_FAILURE);
        }
    }
}


/* Dumps the indices from a multiple sequence alignment into the segID field of pdb cds
   2010-10-12 */
void
Align2segID(PDBCdsArray *pdbA)
{
    int             i, j, k;
    const int       cnum = pdbA->cnum;
    int            *map = pdbA->seq2pdb->map;
    MSA            *msa = pdbA->seq2pdb->msa;
    char           *seq = NULL;
    int             resSeq = 0;
    char            alnindex[5];
    PDBCds      *cdsi = NULL;

    for (i = 0; i < cnum; ++i)
    {
        cdsi = pdbA->cds[i];
        k = 0; /* PDBCds index */
        j = 0; /* MSA index */
        seq = msa->seq[map[i]];
/* printf("\n\n%s\n%s", cdsi->filename, seq); */
        for (j = 0, k = 0; j < msa->seqlen && k < cdsi->vlen; ++j)
        {
            if (seq[j] != '-') /* not a gap */
            {
                resSeq = cdsi->resSeq[k];
                sprintf(alnindex, "%04d", j+1);

/* printf("\n%s %4d %4d %4d %4d %s", cdsi->segID[k], i, k, resSeq, cdsi->resSeq[k], alnindex); */
/* fflush(NULL); */

                while(cdsi->resSeq[k] == resSeq)
                {
                    strncpy(cdsi->segID[k], alnindex, 4);
                    //printf("\n%s %4d %4d %s %4d %c %5d", cdsi->segID[k], i, k, alnindex, resSeq, seq[j], cdsi->vlen);
                    //fflush(NULL);

                    ++k;
                    
                    if (k >= cdsi->vlen)
                        break;
                }
            }
        }
    }
}


/* Dumps the sqrt of the variance in the B-factor column of the pdb file, based on corresponding 
   residues in the sequence alignment.
   2010-10-12 */
void
Align2Vars(PDBCdsArray *pdbA, CdsArray *cdsA)
{
    int             i, j, k, m;
    const int       cnum = pdbA->cnum;
    int            *map = pdbA->seq2pdb->map;
    MSA            *msa = pdbA->seq2pdb->msa;
    char           *seq = NULL;
    int             resSeq = 0;
    PDBCds      *cdsi = NULL;
    int            *singletons = pdbA->seq2pdb->singletons;

    for (i = 0; i < cnum; ++i)
        for (j = 0; j < pdbA->cds[i]->vlen; ++j)
            pdbA->cds[i]->tempFactor[j] = 0.0;

    for (i = 0; i < cnum; ++i)
    {
        cdsi = pdbA->cds[i];
        k = 0; /* PDBCds index */
        j = 0; /* MSA index */
        m = 0; /* cds variances index */
        seq = msa->seq[map[i]];
/* printf("\n\n%s\n%s", cdsi->filename, seq); */
        /* if we put "m < cdsA->vlen" in here also, we miss hanging singletons (at the end of a
           MSA).  So we check that m is OK before accessing it. */
        while (j < msa->seqlen && k < cdsi->vlen)
        {
            if (singletons[j] == 1)
            {
                if (seq[j] == '-') /* IS a gap */
                {
                    ++j;
                }
                else
                {
                    resSeq = cdsi->resSeq[k];
    
    /* printf("\n%s %4d %4d %4d %4d %s", cdsi->segID[k], i, k, resSeq, cdsi->resSeq[k], alnindex); */
    /* fflush(NULL); */
    
                    while(cdsi->resSeq[k] == resSeq)
                    {
                        cdsi->tempFactor[k] = 0.0;
    
                        ++k;
                        
                        if (k >= cdsi->vlen)
                            break;
                    }

                    ++j;
                }
            }
            else
            {
                if (seq[j] != '-') /* NOT a gap */
                {
                    resSeq = cdsi->resSeq[k];
    
    /* printf("\n%s %4d %4d %4d %4d %s", cdsi->segID[k], i, k, resSeq, cdsi->resSeq[k], alnindex); */
    /* fflush(NULL); */
                    if (m < cdsA->vlen)
                    {    
                        while(cdsi->resSeq[k] == resSeq)
                        {
                            cdsi->tempFactor[k] = sqrt(cdsA->var[m]);
        
                            ++k;
                            
                            if (k >= cdsi->vlen)
                                break;
                        }
                    }

                    ++j;
                    ++m;
                }
                else
                {
                    ++j;
                    ++m;
                }
            }
        }
    }
}


static Seq2PDB
*GetMapFile(char *mapfile_name)
{
    Seq2PDB        *seq2pdb;
    FILE           *mapfile = NULL;
    int             i, numscanned, seqnum, maxseqnum;
    char            line[FILENAME_MAX + 256];
    int             ch;

    seq2pdb = Seq2pdbInit();

    mapfile = fopen(mapfile_name, "r");
    if (mapfile == NULL)
    {
        fprintf(stderr,
                "\n  ERROR691: cannot open alignment -> PDB mapping file \"%s\" \n",
                mapfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    maxseqnum = 0;
    while(1)
    {
        ch = getc(mapfile);
        
        if (ch == '#')
            while(ch != '\n')
                ch = getc(mapfile);

        if (ch == EOF || ch == '\n')
            ++maxseqnum;

        if (ch == EOF)
            break;
    }
//printf("\nmaxseqnum:%d", maxseqnum);
    Seq2pdbAlloc(seq2pdb, maxseqnum);

    rewind(mapfile);

    for(i = 0, seqnum = 0; i < maxseqnum; ++i)
    {
        if (fgets(line, FILENAME_MAX + 256, mapfile) == NULL)
            break;

        if (line[0] == '#')
            continue;

        if (strlen(line) > 1)
        {
            numscanned = sscanf(line, "%s %s", seq2pdb->pdbfile_name[i], seq2pdb->seqname[i]);

            if (numscanned == 2)
            {
                ++seqnum;
                /* printf("\nline %d (len %d): %s", i, (int) strlen(line), line); */
            }
        }
    }

    seq2pdb->seqnum = seqnum;

    fclose(mapfile);

    return(seq2pdb);
}


/* Constructs an integer vector that maps a Cds structure to a sequence name in the alignment */
/* First matches PDB filename to the PDB filename given in the mapfile.
   Every filename in the mapfile is associated with a sequence name of the same index.
   Then the fxn matches the sequence name in the mapfile with a sequence name in the alignment.
   Given these two matchings and the mapfile, the map[] vector can be constructed.
   Finally, given Cds i, then map[i] = k, where k is the index of the corresponding
   sequence in the MSA.
   NB: filename extensions are ignored. */
static void
GetSeq2PDBMap(PDBCdsArray *pdbA, Seq2PDB *seq2pdb, MSA *msa)
{
    int             i, j, k, str1len, str2len, str3len, str4len;
    const int       cnum = pdbA->cnum;
    char           *filename_root = NULL;
    char           *msaname_root = NULL;
    char           *mapseqname_root = NULL;
    char           *mappdbname_root = NULL;

    /* First set all flags in the MSA to 1, and later reset to 0 if the sequences have a 
       corresponding PDB file.
       This allows for sequences in the MSA that aren't being superpositioned. */
    for (i = 0; i < msa->seqnum; ++i)
        msa->flag[i] = 1;

    for (i = 0; i < cnum; ++i)
    {
        for (j = 0; j < seq2pdb->seqnum; ++j)
        {
            filename_root = getroot(pdbA->cds[i]->filename);
            mappdbname_root = getroot(seq2pdb->pdbfile_name[j]);
            mapseqname_root = getroot(seq2pdb->seqname[j]);

            str1len = strlen(filename_root);
            str2len = strlen(mappdbname_root);
            str3len = strlen(mapseqname_root);

            //printf("\ni:%3d j:%3d -- %s %s", i, j, filename_root, mappdbname_root);

            if ((strncmp(filename_root, mappdbname_root, str1len) == 0) &&
                (str1len == str2len))
            {
                for (k = 0; k < msa->seqnum; ++k)
                {
                    msaname_root = getroot(msa->name[k]);

                    str4len = strlen(msaname_root);

                    //printf("\ni:%3d j:%3d k:%3d -- %s %s %s", i, j, k, filename_root, mapseqname_root, msaname_root);

                    if ((strncmp(mapseqname_root, msaname_root, str3len) == 0) &&
                        (str3len == str4len)) /* now k (alignment) and i (pdb cds) match */
                    {
                        //printf("\nMATCH");
                        pdbA->seq2pdb->map[i] = k;
                        msa->flag[k] = 0; /* reset msa flag to 0 since this structure exists */

                        free(msaname_root);

                        break;
                    }
                    fflush(NULL);
                    
                    free(msaname_root);
                }

				if (k == msa->seqnum)
				{
					fprintf(stderr,
							"\n  ERROR_689: Sequence #%d (%s) in mapfile has no corresponding sequence in the alignment\n",
							j+1, seq2pdb->pdbfile_name[j]);
					PrintTheseusTag();
					exit(EXIT_FAILURE);
				}

                free(filename_root);
                free(mappdbname_root);
                free(mapseqname_root);

                break;
            }

            free(filename_root);
            free(mappdbname_root);
            free(mapseqname_root);
        }

        if (j == seq2pdb->seqnum)
        {
			fprintf(stderr,
					"\n  ERROR_690: PDB file #%d (%s) has no corresponding sequence in the alignment\n",
					i+1, pdbA->cds[i]->filename);
			PrintTheseusTag();
			exit(EXIT_FAILURE);
        }
    }
}


/* Same as above, but no mapfile needed as we assume that the sequence names and 
   PDB file names are identical */
static void
DefaultSeq2PDBMap(PDBCdsArray *pdbA, Seq2PDB *seq2pdb, MSA *msa)
{
    int             i, j;
    const int       cnum = pdbA->cnum;
    char            filename_root[FILENAME_MAX], msaname_root[FILENAME_MAX];

    /* First set all flags in the MSA to 1, and later reset to 0 if the sequences have a 
       corresponding PDB file.
       This allows for sequences in the MSA that aren't being superpositioned. */
    for (i = 0; i < msa->seqnum; ++i)
        msa->flag[i] = 1;

    for (i = 0; i < cnum; ++i)
    {
        for (j = 0; j < msa->seqnum; ++j)
        {
            strncpy(filename_root, getroot(pdbA->cds[i]->filename), strlen(pdbA->cds[i]->filename)+1);
            strncpy(msaname_root, getroot(msa->name[j]), strlen(msa->name[j])+1);

            if (strncmp(filename_root, msaname_root, FILENAME_MAX) == 0)
            {
                seq2pdb->map[i] = j; /* now j (alignment) and i (pdb cds) match */
                msa->flag[j] = 0; /* reset msa flag to 0 since this structure exists */
                break;
            }
        }

        if (j == msa->seqnum)
        {
			fprintf(stderr,
					"\n  ERROR690: PDB file #%d (%s) has no corresponding sequence in the alignment\n",
					i+1, pdbA->cds[i]->filename);
			PrintTheseusTag();
			exit(EXIT_FAILURE);
        }
    }
}


/* check for singletons, i.e. columns with only one non-gap residue */
static void
GetSingletons(int *singletons, MSA *msa)
{
    int             i, j, count;

    for (i = 0; i < msa->seqlen; ++i)
    {
        count = 0;
        for (j = 0; j < msa->seqnum; ++j)
        {
            /* The flag for each sequence was set/unset in GetSeq2PDBMap() or DefaultSeq2PDBMap()
               If flag is set to 1, we ignore it (because it has no PDB structural counterpart) */
            if (msa->seq[j][i] != '-' && msa->flag[j] == 0)
                ++count;
        }

        /* This flags all columns that have either only one or NO residues,
           and both need to be excluded from the superposition */
        if (count == 1 || count == 0)
            singletons[i] = 1;
        else
            singletons[i] = 0;
    }
}


/* check for ubiqs, i.e. columns with no gaps */
static void
GetUbiqs(int *ubiqs, MSA *msa)
{
    int             i, j, count;

    for (i = 0; i < msa->seqlen; ++i)
    {
        count = 0;
        for (j = 0; j < msa->seqnum; ++j)
        {
            /* The flag for each sequence was set/unset in GetSeq2PDBMap() or DefaultSeq2PDBMap()
               If flag is set to 1, we ignore it (because it has no PDB structural counterpart) */
            if (msa->seq[j][i] == '-' && msa->flag[j] == 0)
                ++count;
        }
//printf("\ncount[%d] = %d\n", i, count);
        /* if ANY gaps were counted, set flag. 
           NOTE that this does not do anything for the other
           flags -- they are the same as when ubiqs was passed */
        if (count > 0)
            ubiqs[i] = 1;
    }
}


static void
GetCdsFrAlignment(PDBCdsArray *pdbA, CdsArray *baseA,
                     Seq2PDB *seq2pdb, MSA *msa,
                     const int vlen, const int alignlen,
                     int *singletons)
{
    int             j, k, m, n, p, q;
    const int       cnum = pdbA->cnum;
    int            *map = seq2pdb->map;
    int            *upper = pdbA->upper; /* arrays of ints holding the upper and lower range bounds */
    int            *lower = pdbA->lower;
    int             range_num = pdbA->range_num;

	for (j = 0; j < cnum; ++j)
	{
	    k = map[j];
		m = n = p = 0;
		while(m < pdbA->cds[j]->vlen && n < vlen  && p < alignlen)
		{
			/* m = PDB length */
			/* n = baseA cds length */
			/* p = sequence alignment length */
			/* k = sequence index */
			/* j = pdb and baseA cds index */

/*                             printf("\n1 n:%d(%d) atomname:pdbA->cds[%d]->name[%d] %s", */
/*                                    n, alignlen, j, m, pdbA->cds[j]->name[m]); */
/*                             fflush(NULL); */
/* if (j == cnum - 1) */
/* printf("\naltLoc:%c", pdbA->cds[j]->altLoc[m]); */
			if (atom_selxn(pdbA->cds[j]->name[m], baseA->algo->atoms) == 1 &&
			    (pdbA->cds[j]->altLoc[m] == ' ' || pdbA->cds[j]->altLoc[m] == 'A'))
			{
/*                                 printf("\n1 m:%d(%d) msa->seq[%d(%d)][%d(%d)] = %c", */
/*                                        m, pdbA->cds[j]->vlen, k, cnum, n, alignlen, msa->seq[k][n]); */
/*                                 fflush(NULL); */
				if (range_selxn(p, lower, upper, range_num) == 1 - baseA->algo->revsel && /* in-range, or out-of-range if revsel == 1 */
				    singletons[p] == 0) /* not a singleton */
				{
					if (msa->seq[k][p] != '-') /* not a gap */
					{
						strncpy(baseA->cds[j]->resName[n], pdbA->cds[j]->resName[m], 3);
						baseA->cds[j]->chainID[n] = pdbA->cds[j]->chainID[m];
						baseA->cds[j]->resSeq[n]  = pdbA->cds[j]->resSeq[m];
						baseA->cds[j]->x[n]       = pdbA->cds[j]->x[m];
						baseA->cds[j]->y[n]       = pdbA->cds[j]->y[m];
						baseA->cds[j]->z[n]       = pdbA->cds[j]->z[m];
						baseA->cds[j]->o[n]       = 1.0;
						baseA->cds[j]->b[n]       = pdbA->cds[j]->tempFactor[m];

						++m;
						++n;
					}
					else /* is a gap */
					{
						strncpy(baseA->cds[j]->resName[n], "GAP", 3);
						baseA->cds[j]->chainID[n] = pdbA->cds[j]->chainID[m];
						baseA->cds[j]->resSeq[n]  = 0;
						baseA->cds[j]->x[n]       = 0.0;
						baseA->cds[j]->y[n]       = 0.0;
						baseA->cds[j]->z[n]       = 0.0;
						baseA->cds[j]->o[n]       = 0.0;
						baseA->cds[j]->b[n]       = 99.99;

						++n;
					}
				}
				else /* out of range */
				{
					if (msa->seq[k][p] != '-') /* not a gap */
					{
						++m;
					}

				}

				++p;
/* 				            if (j == cnum-1) */
/*                                 printf("\n2 m:%d(%d) msa->seq[%d(%d)][%d(%d)] = %c", */
/*                                        m, pdbA->cds[j]->vlen, k, cnum, n, alignlen, msa->seq[k][n]); */
/*                                 fflush(NULL); */
			}
			else /* not the proper atom slxn */
			{
				++m;
			}
/* 			            if (j == cnum-1) */
/*                             printf("\n2 n:%d(%d) atomname:pdbA->cds[%d]->name[%d] %s", */
/*                                    n, alignlen, j, m, pdbA->cds[j]->name[m]); */
/*                             fflush(NULL); */
		}
		/*******************************************************************************************/
		/* if the end of the PDB is before the end of the alignment */
		if (n < vlen && m != 0 && n != 0)
		{
/* 		    printf("\nHere: m:%4d n:%4d p:%4d j:%4d -- %4d %4d %4d %4d", */
/* 		            m, n, p, j, pdbA->cds[j]->vlen, vlen, alignlen, cnum); */

			for (q = n; q < vlen; ++q)
			{
				strncpy(baseA->cds[j]->resName[q], "GAP", 3);
				baseA->cds[j]->chainID[q] = pdbA->cds[j]->chainID[m-1];
				baseA->cds[j]->resSeq[q]  = 0;
				baseA->cds[j]->x[q]       = 0.0;
				baseA->cds[j]->y[q]       = 0.0;
				baseA->cds[j]->z[q]       = 0.0;
				baseA->cds[j]->o[q]       = 0.0;
				baseA->cds[j]->b[q]       = 66.66;
			}
		}
	}
}


static int
ParseSelxns(PDBCdsArray *pdbA, char *selection, const int alignlen, int *singletons, const int revsel)
{
    int             i, j, singleton_cnt;
    int             selxn_len, vlen;
    char          **endptr = NULL;
    char          **selections = NULL; /* an array of pdbA->range_num strings to hold each range selection */
    char            delims[] = ":";

    if (selection != NULL)
    {
        selxn_len = strlen(selection);

        pdbA->range_num = 1;
        for(i = 0; i < selxn_len; ++i)
        {
            if (selection[i] == ':')
                ++(pdbA->range_num);
        }

        selections = (char **) calloc(pdbA->range_num, sizeof(char *));
        pdbA->lower = (int *)  calloc(pdbA->range_num, sizeof(int));
        pdbA->upper = (int *)  calloc(pdbA->range_num, sizeof(int));
        if (selections == NULL || pdbA->lower == NULL || pdbA->upper == NULL)
        {
            perror("\n  ERROR");
            fprintf(stderr, " ERROR: could not allocate memory for selections in GetCdsSelection(). \n\n");
            PrintTheseusTag();
            exit(EXIT_FAILURE);
        }
        
        for (i = 0; i < pdbA->range_num; ++i)
        {
            selections[i] = (char *) calloc(128, sizeof(char));
            if (selections[i] == NULL)
            {
                perror("\n  ERROR");
                fprintf(stderr, " ERROR: could not allocate memory for selections[] in GetCdsSelection(). \n\n");
                PrintTheseusTag();
                exit(EXIT_FAILURE);
            }   
        }

        /* copy each range selection string into the 'selections[]' array */
        mystrncpy(selections[0], strtok(selection, delims), 127); 
        for (i = 1; i < pdbA->range_num; ++i)
            mystrncpy(selections[i], strtok(NULL, delims), 127);

/*         for (i = 0; i < pdbA->range_num; ++i) */
/*             printf"\n selections[%d] = %s", i, selections[i]); */

        for (j = 0; j < pdbA->range_num; ++j)
        {
            /* parse residue number range */
            selxn_len = strlen(selections[j]);

            i = 0;
            while(isspace(selections[j][i]) && i < selxn_len)
                ++i;

            if (isdigit(selections[j][i]))
            {
                pdbA->lower[j] = (int) strtol(&selections[j][i], endptr, 10) - 1;

                while(selections[j][i] != '-' && i < selxn_len)
                    ++i;

                ++i;
                while(isspace(selections[j][i]) && i < selxn_len)
                    ++i;

                if (isdigit(selections[j][i]))
                    pdbA->upper[j] = (int) strtol(&selections[j][i], endptr, 10) - 1;
                else
                {
                    fprintf(stderr, "\n\n  ERROR987: one of the column selections has no upper bound. \n\n");
                    fprintf(stderr, "  ERROR987: upper limit %d = %s \n\n", j, &selections[j][i]);
                    PrintTheseusTag();
                    exit(EXIT_FAILURE);
                }

                if (pdbA->upper[j] >= alignlen)
                {
                    fprintf(stderr, "\n\n  ERROR988: one of the column selections is out of bounds for the alignment. \n");
                    fprintf(stderr, "  ERROR988: upper limit %d = %d; alignment length = %d \n\n", j, pdbA->upper[j], alignlen);
                    PrintTheseusTag();
                    exit(EXIT_FAILURE);
                }
            }
            else
            {
                pdbA->lower[j] = 0;
                pdbA->upper[j] = alignlen - 1;
            }
        }
    }
    else
    {
        pdbA->range_num = 1;
        selections = (char **) calloc(1, sizeof(char *));
        pdbA->lower = (int *) calloc(1, sizeof(int));
        pdbA->upper = (int *) calloc(1, sizeof(int));
        selections[0] = (char *) calloc(128, sizeof(char));
        if (selections == NULL || pdbA->lower == NULL || pdbA->upper == NULL || selections[0] == NULL)
        {
            perror("\n  ERROR");
            fprintf(stderr, " ERROR989: could not allocate memory for selections in GetCdsSelection(). \n\n");
            PrintTheseusTag();
            exit(EXIT_FAILURE);
        }

        pdbA->lower[0] = 0;
        pdbA->upper[0] = alignlen - 1;
    }

    if (revsel == 0)
    {
		vlen = 0;
		for (j = 0; j < pdbA->range_num; ++j)
			vlen += (pdbA->upper[j] - pdbA->lower[j] + 1);

        /* don't count singletons that are in selected ranges */
        for (j = 0; j < pdbA->range_num; ++j)
            for (i = 0; i < alignlen; ++i)
                if (i >= pdbA->lower[j] && i <= pdbA->upper[j] && singletons[i] == 1)
                    vlen--;
    }
    else
    {
		vlen = alignlen;
		for (j = 0; j < pdbA->range_num; ++j)
			vlen -= (pdbA->upper[j] - pdbA->lower[j] + 1);

        singleton_cnt = 0;
        for (i = 0; i < alignlen; ++i)
            if (singletons[i] == 1)
                ++singleton_cnt;

        /* don't count singletons that are in (un)selected ranges */
        for (j = 0; j < pdbA->range_num; ++j)
            for (i = 0; i < alignlen; ++i)
                if (i >= pdbA->lower[j] && i <= pdbA->upper[j] && singletons[i] == 1)
                    singleton_cnt--;

        vlen -= singleton_cnt;
    }

    if (vlen > alignlen)
    {
        fprintf(stderr, "\n  ERROR663: the number of selected alignment columns (%d) exceeds the maximum length of the alignment (%d) \n\n", vlen, alignlen);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < pdbA->range_num; ++i)
        free(selections[i]);

    free(selections);

    return(vlen);
}


void
Align2MSA(PDBCdsArray *pdbA, CdsArray *baseA, char *msafile_name, char *mapfile_name)
{
    int             i, alnlen, vlen;
    const int       cnum = pdbA->cnum;
    MSA            *msa;
    //int            *singletons = NULL;
    Algorithm      *algo = baseA->algo;

    if (mapfile_name == NULL)
    {
        pdbA->seq2pdb = Seq2pdbInit();
        Seq2pdbAlloc(pdbA->seq2pdb, cnum);
    }
    else
    {
        pdbA->seq2pdb = GetMapFile(mapfile_name);
    }

    pdbA->seq2pdb->msa = msa = getmsa(msafile_name);

//    MSAprint(msa);
/*     printf("\n%d", msa->seqlen); */
/*     fflush(NULL); */

    if (msa->seqnum < pdbA->cnum)
    {
        fprintf(stderr,
                "\n  ERROR682: # alignment sequences (%d) < # cds (%d)\n",
                msa->seqnum, pdbA->cnum);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    alnlen = msa->seqlen;
    pdbA->seq2pdb->singletons = malloc(alnlen * sizeof(int));

    if (mapfile_name != NULL)
        GetSeq2PDBMap(pdbA, pdbA->seq2pdb, msa);
    else
        DefaultSeq2PDBMap(pdbA, pdbA->seq2pdb, msa);

    GetSingletons(pdbA->seq2pdb->singletons, msa);
    if (algo->missing == 1)
        GetUbiqs(pdbA->seq2pdb->singletons, msa);
//    for (i=0;i<alnlen;++i)
//        printf("singleton[%3d]:%d\n", i, singletons[i]);
    vlen = ParseSelxns(pdbA, algo->selection, alnlen, pdbA->seq2pdb->singletons, algo->revsel);

/*     printf("\n%d %d %d", msa->seqlen, alnlen, vlen); */

    /* check for singletons, i.e. columns with only one non-gap residue */
/*     for (i = 0; i < alnlen; ++i) */
/*     { */
/*         count = 0; */
/*         for (j = 0; j < cnum; ++j) */
/*         { */
/*             if (msa->seq[j][i] != '-') */
/*                 ++count; */
/*         } */
/* //printf("\ncol:%d count:%d", i, count); */
/*         if (count == 1) */
/*             --vlen; */
/*     } */
//    printf("\n%d %d %d\n", msa->seqlen, alnlen, vlen);

    /* allocate a CdsArray based on this alignment */
    if (algo->atoms == 0)
    {
        CdsArrayAlloc(baseA, cnum, vlen);
    }
    else
    {
        fprintf(stderr,
                "\n  ERROR683: atom selection must be CAs for superpositioning with a sequence alignment\n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < cnum; ++i)
        strncpy(baseA->cds[i]->filename, pdbA->cds[i]->filename, FILENAME_MAX - 1);

    VerifyAlignmentVsPDBs(pdbA, baseA, msa, algo->atoms, pdbA->seq2pdb->map);
    GetCdsFrAlignment(pdbA, baseA, pdbA->seq2pdb, msa, vlen, alnlen, pdbA->seq2pdb->singletons);

//    free(singletons);
}


double
CalcRotationsOcc(CdsArray *cdsA)
{
    Cds        **cds = cdsA->cds;
    Cds         *avecds = cdsA->avecds;
    const double   *wts = (const double *) cdsA->w;
    double          deviation = 0.0, deviation_sum = 0.0;
    int             i;

    for (i = 0; i < cdsA->cnum; ++i)
    {
        if(cdsA->algo->covweight != 0)
        {
                deviation = ProcGSLSVDCovOcc(cds[i], avecds, cds[i]->matrix,
                                                (const double **) cdsA->WtMat,
                                                cdsA->tmpmat3a,
                                                cdsA->tmpmat3b,
                                                cdsA->tmpmat3c,
                                                cdsA->tmpvec3a);
        }
        else if(cdsA->algo->varweight != 0 || cdsA->algo->leastsquares != 0)
        {
                deviation = ProcGSLSVDOcc(cds[i], avecds,
                                             cds[i]->matrix,
                                             wts,
                                             cdsA->tmpmat3a,
                                             cdsA->tmpmat3b,
                                             cdsA->tmpmat3c,
                                             cdsA->tmpvec3a);
        }

        cds[i]->wRMSD_from_mean = sqrt(deviation / cdsA->vlen);
        deviation_sum += deviation;

        if (cdsA->algo->verbose == 1)
        {
            /* rmsd from mean would usually need a 2 in denom,\
               but this is already deviation from mean,
               since structure #2 is the average structure */
            printf("CalcRotationsOcc:%5d %8.3f %13.3f \n",
                   i+1,
                   cds[i]->wRMSD_from_mean,
                   deviation);
        }
    }

    return(deviation_sum);
}


/* char aa1[] = "ARNDCQEGHILKMFPSTWYV"; */
/* char aa3[] = "ALAARGASNASPCYSGLNGLUGLYHISILELEULYSMETPHEPROSERTHRTRPTYRVAL"; */

void
pdb2fst(PDBCdsArray *pdbA)
{
    int             i, j, count, aaindex;
    char           *pindex = NULL;
    FILE           *fp;
    char            outfile[FILENAME_MAX];

    for (i = 0; i < pdbA->cnum; ++i)
    {
        strncpy(outfile, pdbA->cds[i]->filename, strlen(pdbA->cds[i]->filename)+1);
        strncat(outfile, ".fst", 4);

        fp = fopen(outfile, "w");

        //fprintf(fp, ">%-72s", pdbA->cds[i]->filename);
        fprintf(fp, ">%-s", pdbA->cds[i]->filename);

        count = 0;
        for (j = 0; j < pdbA->cds[i]->vlen; ++j)
        {
            //printf("ATOM %4d:%4d \'%s\'\n", i, j, pdbA->cds[i]->name[j]);

            if ( (strncmp(pdbA->cds[i]->record[j], "ATOM  ", 6) == 0 || strncmp(pdbA->cds[i]->record[j], "HETATM", 6) == 0)
                &&
                (strncmp(pdbA->cds[i]->name[j], "CA ", 3) == 0 || strncmp(pdbA->cds[i]->name[j], "C1*", 3) == 0) &&
                (pdbA->cds[i]->altLoc[j] == ' ' || pdbA->cds[i]->altLoc[j] == 'A'))
            {
				if (count % 72 == 0)
					fputc('\n', fp);

                pindex = strstr(aan3, pdbA->cds[i]->resName[j]);
                if (pindex == NULL)
                    fputc('X', fp);
                else
                {
					aaindex = (int) (pindex - &aan3[0])/3;
					fputc(aan1[aaindex], fp);
                }

                ++count;
            }
        }

        fputc('\n', fp);

        printf("    %4d %s  (%d aa)\n", i+1, outfile, count);

        fflush(NULL);
        fclose(fp);
    }
}
