/*
    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 <time.h>
#include <limits.h>
#include "Error.h"
#include "pdbMalloc.h"
#include "Cds.h"
#include "PDBCds.h"
#include "pdbStats.h"
#include "pdbUtils.h"
#include "pdbIO.h"
#include "distfit.h"
#include "DLTmath.h"


static const char atoms0[] = "CA :C1*";
static const char atoms1[] = "N  :C  :O  :CA :P  :C1*:O3*:O5*:C3*:C4*:C5*:";
static const char atoms3[] = "CA :CB :P  :C1*";


static int
atom_selxn(char *name, int mode, char *useratoms)
{
    const char     *atoms = NULL;

    if (useratoms != NULL)
        mode = 10;

    if (mode == 4)
    {
        if (strchr(name, 'H') == NULL)
            return (1);
        else
            return (0);
    }

    switch(mode)
    {
        case 0: /* "CA :P  " */
            atoms = atoms0;
            break;
        case 1: /* "N  :C  :O  :CA :P  :O3*:O5*:C3*:C4*:C5*:" */
            atoms = atoms1;
            break;
        case 2: /* all atoms */
            return (1);
            break;
        case 3: /* "CA :CB :P  " */
            atoms = atoms3;
            break;
        case 10: /* user defined atom strings */
            atoms = useratoms;
            break;
    }

    if (strstr(atoms, name) != NULL)
        return (1);
    else
        return (0);
}


static int
range_selxn(int chainID, char *chains, int resSeq, int *lower, int *upper, int range_num)
{
    int             i;

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

    return(0);
}


/* A bunch of XPLOR-correcting silliness, mostly hydrogen format stuff */
static void
XPLORcorrections(PDBCds *pdbcds, int record)
{
    char              tmpname[5];

    /* assigne chainID based upon segID */
    if (!isalpha(pdbcds->chainID[record]) && isalpha(pdbcds->segID[record][0]))
        pdbcds->chainID[record] = pdbcds->segID[record][0];

    /* fix hydrogens and primes for nucleic acids */
    if (strncmp(pdbcds->name[record], "H5'", 3) == 0)
    {
        memcpy(pdbcds->name[record], "H5*", 3);
        pdbcds->Hnum[record] = '1';
    }
    else if (strncmp(pdbcds->name[record], "5''", 3) == 0)
    {
        memcpy(pdbcds->name[record], "H5*", 3);
        pdbcds->Hnum[record] = '2';
    }
    else if (strncmp(pdbcds->name[record], "H2'", 3) == 0)
    {
        memcpy(pdbcds->name[record], "H2*", 3);
        pdbcds->Hnum[record] = '1';
    }
    else if (strncmp(pdbcds->name[record], "2''", 3) == 0)
    {
        memcpy(pdbcds->name[record], "H2*", 3);
        pdbcds->Hnum[record] = '2';
    }

    /* fix all remaining primes */
    if (pdbcds->name[record][2] == '\'')
        pdbcds->name[record][2] = '*';

    if (pdbcds->Hnum[record] == 'H' && pdbcds->name[record][2] != '*')
    {
        /*printf("\n    before: Hnum = %c, name = %3s  --  serial = %d ",
               pdbcds->Hnum[record], pdbcds->name[record], pdbcds->serial[record]);*/
        pdbcds->Hnum[record] = pdbcds->name[record][2];
        tmpname[0] = 'H';
        tmpname[1] = '\0';
        strcat(tmpname, pdbcds->name[record]);
        memcpy(pdbcds->name[record], tmpname, 3);
        /*printf("\n    after:  Hnum = %c, name = %3s",
               pdbcds->Hnum[record], pdbcds->name[record]);*/
    }
    else if (pdbcds->name[record][0] == 'H' && isdigit(pdbcds->name[record][2]))
    {
        /*printf("\n    before: Hnum = %c, name = %3s  --  serial = %d ",
               pdbcds->Hnum[record], pdbcds->name[record], pdbcds->serial[record]);*/
        pdbcds->Hnum[record] = pdbcds->name[record][2];
        pdbcds->name[record][2] = ' ';
        /*printf("\n    after:  Hnum = %c, name = %3s",
               pdbcds->Hnum[record], pdbcds->name[record]);*/
    }

    /* assign atomic element field */
    pdbcds->element[record][1] = pdbcds->name[record][0];

    /* change nucleic acid base names from three-letter to one-letter */
    if (     strncmp(pdbcds->resName[record], "ADE", 3) == 0)
        memcpy(pdbcds->resName[record], "  A", 3);
    else if (strncmp(pdbcds->resName[record], "CYT", 3) == 0)
        memcpy(pdbcds->resName[record], "  C", 3);
    else if (strncmp(pdbcds->resName[record], "GUA", 3) == 0)
        memcpy(pdbcds->resName[record], "  G", 3);
    else if (strncmp(pdbcds->resName[record], "THY", 3) == 0)
        memcpy(pdbcds->resName[record], "  T", 3);
    else if (strncmp(pdbcds->resName[record], "URA", 3) == 0)
        memcpy(pdbcds->resName[record], "  U", 3);
    else if (strncmp(pdbcds->resName[record], "URI", 3) == 0)
        memcpy(pdbcds->resName[record], "  U", 3);
}


static void
ScanPDBLine(char *buff, PDBCds *cp, int j)
{
    char           *endline = NULL;
    char            amber_atom_name = buff[12];

    endline = strpbrk(&buff[54], "\r\n\f");
    if (endline != NULL) /* kill the newline, if there */
        *endline = '\0';

    if (!isdigit(amber_atom_name) && !isspace(amber_atom_name)) /* for nonstandard PDB files, with atom name in wrong column (shifted one to the left, such as with AMBER8) */
    {
        /*         1         2         3         4         5         6         7         8
        12345678901234567890123456789012345678901234567890123456789012345678901234567890
        ATOM    145  N   VAL A  25      32.433  16.336  57.540  1.00 11.92      A1   N
        ATOM    146  CA  VAL A  25      31.132  16.439  58.160  1.00 11.85      A1   C  
        ATOM  15088 1HG1 ILE    28      -3.430   4.303  -6.057  1.00  0.00           H  
        ATOM   7580 HD23 LEU  1724     111.285  90.889 -61.535                      
        */
                                                      /* #  COLs  LEN  DATA TYPE    FIELD      DEFINITION */
        sscanf(&buff[0],  "%6c",   cp->record[j]);    /* 1  0-5   6    Record name  "ATOM  " */
        sscanf(&buff[6],  "%5u",  &cp->serial[j]);    /* 2  6-10  5    Integer      serial     Atom serial number. */
        sscanf(&buff[12], "%3c",   cp->name[j]);      /* 4  12-14 4(3) Atom         name       Atom name. -- unofficial, nonstandard PDB (AMBER8 format, hosers) */
        sscanf(&buff[15], "%1c",  &cp->Hnum[j]);      /* 3  15    1      hydrogen number, (!official). */
        sscanf(&buff[16], "%1c",  &cp->altLoc[j]);    /* 5  16    1    Character    altLoc     Alternate location indicator. */
        sscanf(&buff[17], "%3c",   cp->resName[j]);   /* 6  17-19 3    Residue name resName    Residue name. */
        sscanf(&buff[20], "%1c",  &cp->xchainID[j]);  /* 7  20    1    Character    xchainID   Chain identifier (!official). */
        sscanf(&buff[21], "%1c",  &cp->chainID[j]);   /* 8  21    1    Character    chainID    Chain identifier. */
        sscanf(&buff[22], "%4d",  &cp->resSeq[j]);    /* 9  22-25 4    Integer      resSeq     Residue sequence number. */
        sscanf(&buff[26], "%1c",  &cp->iCode[j]);     /* 10 26    1    AChar        iCode      Code for insertion of residues. */
        sscanf(&buff[30], "%8lf", &cp->x[j]);         /* 11 30-37 8    Real(8.3)    x          Orthogonal coordinates for X */
        sscanf(&buff[38], "%8lf", &cp->y[j]);         /* 12 30-37 8    Real(8.3)    y          Orthogonal coordinates for Y */
        sscanf(&buff[46], "%8lf", &cp->z[j]);         /* 13 30-37 8    Real(8.3)    z          Orthogonal coordinates for Z */
        sscanf(&buff[54], "%6lf", &cp->occupancy[j]); /* 14 54-59 6    Real(6.2)    occupancy  Occupancy. */
        sscanf(&buff[60], "%6lf", &cp->tempFactor[j]);/* 15 60-65 6    Real(6.2)    tempFactor Temperature factor. */
        sscanf(&buff[72], "%4c",   cp->segID[j]);     /* 16 72-75 4    LString(4)   segID      Segment identifier, left-just. */
        sscanf(&buff[76], "%2c",   cp->element[j]);   /* 17 76-77 2    LString(2)   element    Element symbol, right-just. */
        sscanf(&buff[78], "%2c",   cp->charge[j]);    /* 18 78-79 2    LString(2)   charge     Charge on the atom. */
    }
    else /* standard PDB format http://www.rcsb.org/pdb/file_formats/pdb/pdbguide2.2/guide2.2_frame.html */
    {
        /*          1         2         3         4         5         6         7         8
        012345678901234567890123456789012345678901234567890123456789012345678901234567890
        ATOM    145  N   VAL A  25      32.433  16.336  57.540  1.00 11.92      A1   N
        ATOM    146  CA  VAL A  25      31.132  16.439  58.160  1.00 11.85      A1   C  
        ATOM  15088 1HG1 ILE    28      -3.430   4.303  -6.057  1.00  0.00           H  
        ATOM   7580 HD23 LEU  1724     111.285  90.889 -61.535                      
        */
                                                      /* #  COLs  LEN  DATA TYPE    FIELD      DEFINITION */
        sscanf(&buff[0],  "%6c",   cp->record[j]);    /* 1  0-5   6    Record name  "ATOM  " */
        sscanf(&buff[6],  "%5u",  &cp->serial[j]);    /* 2  6-10  5    Integer      serial     Atom serial number. */
        sscanf(&buff[12], "%1c",  &cp->Hnum[j]);      /* 3  12    1      hydrogen number, usu (!official). */
        sscanf(&buff[13], "%3c",   cp->name[j]);      /* 4  13-15 4(3) Atom         name       Atom name. */
        sscanf(&buff[16], "%1c",  &cp->altLoc[j]);    /* 5  16    1    Character    altLoc     Alternate location indicator. */
        sscanf(&buff[17], "%3c",   cp->resName[j]);   /* 6  17-19 3    Residue name resName    Residue name. */
        sscanf(&buff[20], "%1c",  &cp->xchainID[j]);  /* 7  20    1    Character    xchainID   Chain identifier (!official). */
        sscanf(&buff[21], "%1c",  &cp->chainID[j]);   /* 8  21    1    Character    chainID    Chain identifier. */
        sscanf(&buff[22], "%4d",  &cp->resSeq[j]);    /* 9  22-25 4    Integer      resSeq     Residue sequence number. */
        sscanf(&buff[26], "%1c",  &cp->iCode[j]);     /* 10 26    1    AChar        iCode      Code for insertion of residues. */
        sscanf(&buff[30], "%8lf", &cp->x[j]);         /* 11 30-37 8    Real(8.3)    x          Orthogonal coordinates for X */
        sscanf(&buff[38], "%8lf", &cp->y[j]);         /* 12 30-37 8    Real(8.3)    y          Orthogonal coordinates for Y */
        sscanf(&buff[46], "%8lf", &cp->z[j]);         /* 13 30-37 8    Real(8.3)    z          Orthogonal coordinates for Z */
        sscanf(&buff[54], "%6lf", &cp->occupancy[j]); /* 14 54-59 6    Real(6.2)    occupancy  Occupancy. */
        sscanf(&buff[60], "%6lf", &cp->tempFactor[j]);/* 15 60-65 6    Real(6.2)    tempFactor Temperature factor. */
        sscanf(&buff[72], "%4c",   cp->segID[j]);     /* 16 72-75 4    LString(4)   segID      Segment identifier, left-just. */
        sscanf(&buff[76], "%2c",   cp->element[j]);   /* 17 76-77 2    LString(2)   element    Element symbol, right-just. */
        sscanf(&buff[78], "%2c",   cp->charge[j]);    /* 18 78-79 2    LString(2)   charge     Charge on the atom. */
        
        //printf("%4d:%4d %d %d \'%s\'\n", i, j, cp->serial[j], cp->Hnum[j], cp->name[j]);
    }
}


/* reads all coordinate fields for each model in a pdb file */
int
ReadPDBCds(char *pdbfile_name, PDBCdsArray *pdbA, int cds_i, int modelnum, int fix_atom_names)
{
    //char            buff[99];
    int             bufflen = 256;
    char           *buff = NULL;
    int             i, j, pos;
    FILE           *pdbfile = NULL;
    char            pdbdir[FILENAME_MAX], dirpdbfile_name[FILENAME_MAX];
    PDBCds     **cds = pdbA->cds;
    PDBCds      *cp = NULL;
    
    buff = malloc(bufflen* sizeof(char));
    if (buff == NULL)
    {
        perror("\n\n  ERROR");
        fprintf(stderr, "\n\n  ERROR_071: could not allocate memory for buff. \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }
    
    buff[bufflen-1] = '\0';

    pdbfile = fopen(pdbfile_name, "r");
    if (pdbfile == NULL)
    {
        if (getenv("PDBDIR") != NULL)
        {
            strncpy(pdbdir, getenv("PDBDIR"), FILENAME_MAX);
            strncpy(pdbdir, getenv("PDBDIR"), FILENAME_MAX);
            strncpy(dirpdbfile_name, pdbdir, FILENAME_MAX - 1);
            strncat(dirpdbfile_name, pdbfile_name, FILENAME_MAX - strlen(pdbfile_name) - 1);
            
            pdbfile = fopen(dirpdbfile_name, "r");
        }

        if (pdbfile == NULL)
        {
            perror("\n\n  ERROR");
            fprintf(stderr, "\n\n  ERROR71: file \"%s\" not found. \n", pdbfile_name);
            PrintTheseusTag();
            exit(EXIT_FAILURE);
        }
    }

    for (i = 0; i < modelnum; ++i)
    {
        pos = cds_i + i;
        cp = cds[pos];

        /* move to next model */
        if (modelnum > 1)
        {
            while (fgets(buff, bufflen, pdbfile) != NULL)
            {
                if (strncmp(buff, "MODEL", 5) == 0 || 
                    strncmp(buff, "REMARK FRAME:", 13) == 0 ||
                    strncmp(buff, "REMARK  OPLS-AA", 15) == 0) // DLTPU
                {
                    cp->model = i;
                    break;
                }
            }
        }

        mystrncpy(cp->filename, pdbfile_name, FILENAME_MAX - 1);

        /* read in the model's cds */
        j = 0;
        while (fgets(buff, bufflen, pdbfile))
        {
            if (j >= cp->vlen ||
                strncmp(buff, "ENDMDL", 6) == 0 ||
                strncmp(buff, "END", 3) == 0)
            {
                break;
            }

            /* if the line is an ATOM or HETATM record */
            if (strncmp(buff, "ATOM  ", 6) == 0 || 
                strncmp(buff, "HETATM", 6) == 0)
            {
                ScanPDBLine(buff, cp, j);
                if (fix_atom_names == 0)
                    XPLORcorrections(cp, j);

                if (isfinite(cp->occupancy[j]) == 0)
                    cp->occupancy[j] = 1.0;

                ++j;
            }
        }
    }

    //PrintPDBCds(stdout, pdbA->cds[0]);
    free(buff);
    fclose(pdbfile);
    return(EXIT_SUCCESS);
}


// CdsArray
// *GetDefaultCdsSel(CdsArray *baseA, PDBCdsArray *pdbA)
// {
//     int             vlen, slxnlen = 0;
//     int            *selection_index = NULL; /* array of ints corresponding to selected atoms */
//     int             i, lower, upper, pdbi;
// 
//     if (pdbA->cnum < 2)
//     {
//         fprintf(stderr, "\n  ERROR23: Number of cds in pdbfiles is less than 2. \n");
//         PrintTheseusTag();
//         exit(EXIT_FAILURE);
//     }
// 
//     CdsArrayAllocNum(baseA, pdbA->cnum);
// 
//     for (pdbi = 0; pdbi < pdbA->cnum; ++pdbi)
//     {
//         lower = 0;
//         upper = vlen = pdbA->cds[pdbi]->vlen;
//     
//         if (upper - 4 < lower)
//         {
//             fprintf(stderr, "\n  ERROR: upper residue bound must be at least 3 greater than the lower bound. \n");
//             PrintTheseusTag();
//             exit(EXIT_FAILURE);
//         }
//     
//         selection_index = (int *) malloc (vlen * sizeof(int));
//         if (selection_index == NULL)
//         {
//             perror("\n\n  ERROR");
//             fprintf(stderr, "\n  ERROR: could not allocate memory for selection_index in GetCdsSelection(). \n");
//             PrintTheseusTag();
//             exit(EXIT_FAILURE);
//         }
// 
//         slxnlen = 0;
//         for (i = 0; i < vlen; ++i)
//         {
//             if (   strncmp(pdbA->cds[pdbi]->record[i], "ATOM  ", 6) == 0
//                 && (   pdbA->cds[pdbi]->altLoc[i] == ' '
//                     || pdbA->cds[pdbi]->altLoc[i] == 'A'
//                     || pdbA->cds[pdbi]->altLoc[i] == '1')
//                 /* && strncmp(pdbA->cds[pdbi]->resName[i], "GLY", 3) != 0 */ /* DLT - skip glycines for now */
//                 && atom_selxn(pdbA->cds[pdbi]->name[i], baseA->algo->atoms, baseA->algo->atomslxn /* DLT - use '3' to get CBs */) == 1)
//             {
//                 selection_index[slxnlen] = i;
//                 ++slxnlen;
//             }
//         }
// 
//         baseA->cds[pdbi] = CdsInit();
//         CdsAlloc(baseA->cds[pdbi], slxnlen);
// 
//         baseA->cds[pdbi]->model = pdbA->cds[pdbi]->model;
//         strcpy(baseA->cds[pdbi]->filename, pdbA->cds[pdbi]->filename);
//     
//         for (i = 0; i < slxnlen; ++i)
//         {
//             strncpy(baseA->cds[pdbi]->resName[i], pdbA->cds[pdbi]->resName[selection_index[i]], 4);
//             baseA->cds[pdbi]->resSeq[i] = pdbA->cds[pdbi]->resSeq[selection_index[i]];
//             baseA->cds[pdbi]->x[i]      = pdbA->cds[pdbi]->x[selection_index[i]];
//             baseA->cds[pdbi]->y[i]      = pdbA->cds[pdbi]->y[selection_index[i]];
//             baseA->cds[pdbi]->z[i]      = pdbA->cds[pdbi]->z[selection_index[i]];
//             baseA->cds[pdbi]->o[i]      = pdbA->cds[pdbi]->occupancy[selection_index[i]];
//             baseA->cds[pdbi]->b[i]      = pdbA->cds[pdbi]->tempFactor[selection_index[i]];
//         }
//         
//         free(selection_index);
//     }
// 
//     baseA->vlen = slxnlen;
//     baseA->avecds = CdsInit();
//     CdsAlloc(baseA->avecds, slxnlen);
//     /*PrintCds(baseA->cds[0]);
//     PrintCds(baseA->cds[1]);*/
// 
//     return (baseA);
// }


/* static int */
/* isdelim(char character, char *delims) */
/* { */
/*     int            i, len; */
/*  */
/*     len = strlen(delims); */
/*  */
/*     for (i = 0; i < len; ++i) */
/*     { */
/*         if (character == delims[i]) */
/*             return(1); */
/*     } */
/*     return (0); */
/* } */


static int
GetSlxnLen(PDBCdsArray *pdbA, const int crds, Algorithm *algo, char *chains,
           int *lower, int *upper, const int range_num, int *selection_index, int rev)
{
    int            i, vlen;

    vlen = 0;
	for (i = 0; i < pdbA->cds[crds]->vlen; ++i)
	{
/*         printf("\n%s, %s, %c, %c, %d", */
/*                pdbA->cds[crds]->record[i], */
/*                pdbA->cds[crds]->name[i], */
/*                pdbA->cds[crds]->altLoc[i], */
/*                pdbA->cds[crds]->chainID[i], */
/*                pdbA->cds[crds]->resSeq[i]); */
/*         fflush(NULL); */
		if (   (   strncmp(pdbA->cds[crds]->record[i], "ATOM  ", 6) == 0
				|| strncmp(pdbA->cds[crds]->record[i], "HETATM", 6) == 0)
			&& atom_selxn(pdbA->cds[crds]->name[i], algo->atoms, algo->atomslxn) == 1
			&& (pdbA->cds[crds]->altLoc[i] == ' ' || pdbA->cds[crds]->altLoc[i] == 'A')
			&& range_selxn(pdbA->cds[crds]->chainID[i], chains, pdbA->cds[crds]->resSeq[i], lower, upper, range_num) == rev) /* DLT debug FIX THIS!!!! */
		{
		    if (selection_index != NULL)
    			selection_index[vlen] = i;
			++vlen;
		}
	}

    return(vlen);
}


void
GetCdsSelection(CdsArray *baseA, PDBCdsArray *pdbA)
{
    int             vlen, last_len;
    int            *selection_index = NULL; /* array of ints corresponding to selected atoms */
    int             i, j;
    char           *chains = NULL; /* array of chars holding chain IDs of ranges in the selection criteria */
    int            *upper = NULL, *lower = NULL; /* an array of ints holding the upper and lower range bounds */
    int             selection_len;
    char          **endptr = NULL;
    int             range_num = 1; /* number of residue ranges specified in selection */
    char          **selections = NULL; /* an array of range_num strings to hold each range selection */
    char            delims[] = ":";

    if (baseA->algo->selection != NULL)
    {
        selection_len = strlen(baseA->algo->selection);

        for(i = 0; i < selection_len; ++i)
        {
            if (baseA->algo->selection[i] == ':')
                ++range_num;
        }

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

        /* if the user specified XPLOR/CNS-style atoms, translate them to standard PDB format (DNA primes should be "*", not "'") */
        for (i = 0; i < strlen(baseA->algo->selection); ++i)
            if (baseA->algo->selection[i] == '\'')
                baseA->algo->selection[i] = '*';

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

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

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

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

            if (isalpha(selections[j][i]))
            {
                chains[j] = toupper(selections[j][i]);
                ++i;
            }
            else
                chains[j] = 0;

            if (isalpha(selections[j][i]))
            {
                fprintf(stderr, "\n  ERROR1002: incorrect format for chainID selection (too many characters). \n");
                Usage(0);
                exit(EXIT_FAILURE);
            }

            if (isdigit(selections[j][i]))
            {
                lower[j] = (int) strtol(&selections[j][i], endptr, 10);
        
                while(selections[j][i] != '-' && i < selection_len)
                    ++i;
    
                ++i;
                while(isspace(selections[j][i]) && i < selection_len)
                    ++i;
    
                upper[j] = (int) strtol(&selections[j][i], endptr, 10);
            }
            else
            {
                lower[j] = 0;
                upper[j] = pdbA->vlen - 1;
            }
        }
    }
    else
    {
        range_num = 1;
        selections = (char **) calloc(1, sizeof(char *));
        lower      = (int *)   calloc(1, sizeof(int));
        upper      = (int *)   calloc(1, sizeof(int));
        chains     = (char *)  calloc(1, sizeof(char));
        selections[0] = (char *) calloc(128, sizeof(char));
        if (selections == NULL || lower == NULL || upper == NULL || chains == NULL || selections[0] == NULL)
        {
            perror("\n  ERROR");
            fprintf(stderr, "\n  ERROR1003: could not allocate memory for selections in GetCdsSelection(). \n");
            PrintTheseusTag();
            exit(EXIT_FAILURE);
        }

        lower[0] = -INT_MAX;
        upper[0] = INT_MAX;
    }

/*     if (upper[0] - 4 < lower[0]) */
/*     { */
/*         fprintf(stderr, "\n  ERROR876: upper residue bound must be at least 3 greater than the lower bound. %d %d \n", */
/*                          upper[0], lower[0]); */
/*         PrintTheseusTag(); */
/*         exit(EXIT_FAILURE); */
/*     } */

    selection_index = (int *) calloc (pdbA->vlen, sizeof(int));
    if (selection_index == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR1004: could not allocate memory for selection_index in GetCdsSelection(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    /* First count the number of selected atoms for each structure, and make sure they are all the same */
    last_len = vlen = 0;
    for (i = 0; i < pdbA->cnum; ++i)
    {
//         printf("\n%s, %s, %c, %c, %d",
//                pdbA->cds[i]->record[i],
//                pdbA->cds[i]->name[i],
//                pdbA->cds[i]->altLoc[i],
//                pdbA->cds[i]->chainID[i],
//                pdbA->cds[i]->resSeq[i]);
//         fflush(NULL);
        last_len = vlen;
		if (baseA->algo->revsel == 0)
			vlen = GetSlxnLen(pdbA, i, baseA->algo, chains, lower, upper, range_num, NULL, 1);
		else
			vlen = GetSlxnLen(pdbA, i, baseA->algo, chains, lower, upper, range_num, NULL, 0);
	
	    if (i > 0 && vlen != last_len)
		{
			fprintf(stderr,
					"\n  ERROR1: length of selection is (%d) different from preceding cds (%d) in GetCdsSelection() \n", vlen, i+1);
			PrintTheseusTag();
			exit(EXIT_FAILURE);
		}

		if (vlen < 3)
		{
			fprintf(stderr,
					"\n  ERROR3: 'vlen' is too small (%d) in GetCdsSelection(); not enough atoms selected in cds %d. \n", vlen, i+1);
			PrintTheseusTag();
			exit(EXIT_FAILURE);
		}
    }

    /* Now allocate based on this length */
    /* baseA = CdsArrayInit(); */ /* NB!!: we don't need to initialize, since the pointer was passed above */
    CdsArrayAlloc(baseA, pdbA->cnum, vlen);
    CdsArraySetup(baseA);

    /* Now get the selections again and read them in this time */
    for (j = 0; j < pdbA->cnum; ++j)
    {
		if (baseA->algo->revsel == 0)
			vlen = GetSlxnLen(pdbA, j, baseA->algo, chains, lower, upper, range_num, selection_index, 1);
		else
			vlen = GetSlxnLen(pdbA, j, baseA->algo, chains, lower, upper, range_num, selection_index, 0);

        baseA->cds[j]->model = pdbA->cds[j]->model;
        strncpy(baseA->cds[j]->filename, pdbA->cds[j]->filename, FILENAME_MAX - 1);

        for (i = 0; i < baseA->vlen; ++i)
        {
            strncpy(baseA->cds[j]->resName[i], pdbA->cds[j]->resName[selection_index[i]], 3);
            baseA->cds[j]->chainID[i] = pdbA->cds[j]->chainID[selection_index[i]];
            baseA->cds[j]->resSeq[i] = pdbA->cds[j]->resSeq[selection_index[i]];
            baseA->cds[j]->x[i]      = pdbA->cds[j]->x[selection_index[i]];
            baseA->cds[j]->y[i]      = pdbA->cds[j]->y[selection_index[i]];
            baseA->cds[j]->z[i]      = pdbA->cds[j]->z[selection_index[i]];
            baseA->cds[j]->o[i]      = 1.0;/* pdbA->cds[j]->occupancy[selection_index[i]]; */ /* DLT debug - shouldn't need to do this, I think prob is in SuperPose() */
            baseA->cds[j]->b[i]      = pdbA->cds[j]->tempFactor[selection_index[i]];
        }
    }
    
/* PrintCds(baseA->cds[0]); */
/* PrintCds(baseA->cds[1]); */

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

    free(selections);
    free(selection_index);
    free(upper);
    free(lower);
    free(chains);
}


void
GetCdsSelection_old(CdsArray *baseA, PDBCdsArray *pdbA)
{
    int             vlen;
    int            *selection_index = NULL; /* array of ints corresponding to selected atoms */
    int             i, j;
    char           *chains = NULL; /* array of chars holding chain IDs of ranges in the selection criteria */
    int            *upper = NULL, *lower = NULL; /* an array of ints holding the upper and lower range bounds */
    int             selection_len;
    char          **endptr = NULL;
    int             range_num = 1; /* number of residue ranges specified in selection */
    char          **selections = NULL; /* an array of range_num strings to hold each range selection */
    char            delims[] = ":";

    if (baseA->algo->selection != NULL)
    {
        selection_len = strlen(baseA->algo->selection);

        for(i = 0; i < selection_len; ++i)
        {
            if (baseA->algo->selection[i] == ':')
                ++range_num;
        }

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

        /* if the user specified XPLOR/CNS-style atoms, translate them to standard PDB format (DNA primes should be "*", not "'") */
        for (i = 0; i < strlen(baseA->algo->selection); ++i)
            if (baseA->algo->selection[i] == '\'')
                baseA->algo->selection[i] = '*';

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

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

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

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

            if (isalpha(selections[j][i]))
            {
                chains[j] = toupper(selections[j][i]);
                ++i;
            }
            else
                chains[j] = 0;

            if (isalpha(selections[j][i]))
            {
                fprintf(stderr, "\n  ERROR1002: incorrect format for chainID selection (too many characters). \n");
                Usage(0);
                exit(EXIT_FAILURE);
            }

            if (isdigit(selections[j][i]))
            {
                lower[j] = (int) strtol(&selections[j][i], endptr, 10);
        
                while(selections[j][i] != '-' && i < selection_len)
                    ++i;
    
                ++i;
                while(isspace(selections[j][i]) && i < selection_len)
                    ++i;
    
                upper[j] = (int) strtol(&selections[j][i], endptr, 10);
            }
            else
            {
                lower[j] = 0;
                upper[j] = pdbA->vlen - 1;
            }
        }
    }
    else
    {
        range_num = 1;
        selections = (char **) calloc(1, sizeof(char *));
        lower      = (int *)   calloc(1, sizeof(int));
        upper      = (int *)   calloc(1, sizeof(int));
        chains     = (char *)  calloc(1, sizeof(char));
        selections[0] = (char *) calloc(128, sizeof(char));
        if (selections == NULL || lower == NULL || upper == NULL || chains == NULL || selections[0] == NULL)
        {
            perror("\n  ERROR");
            fprintf(stderr, "\n  ERROR1003: could not allocate memory for selections in GetCdsSelection(). \n");
            PrintTheseusTag();
            exit(EXIT_FAILURE);
        }

        lower[0] = -INT_MAX;
        upper[0] = INT_MAX;
    }

/*     if (upper[0] - 4 < lower[0]) */
/*     { */
/*         fprintf(stderr, "\n  ERROR876: upper residue bound must be at least 3 greater than the lower bound. %d %d \n", */
/*                          upper[0], lower[0]); */
/*         PrintTheseusTag(); */
/*         exit(EXIT_FAILURE); */
/*     } */

    selection_index = (int *) calloc (pdbA->vlen, sizeof(int));
    if (selection_index == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR1004: could not allocate memory for selection_index in GetCdsSelection(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    vlen = 0;

    if (baseA->algo->revsel == 0)
    {
        for (i = 0; i < pdbA->vlen; ++i)
        {
/*         printf("\n%s, %s, %c, %c, %d", */
/*                pdbA->cds[0]->record[i], */
/*                pdbA->cds[0]->name[i], */
/*                pdbA->cds[0]->altLoc[i], */
/*                pdbA->cds[0]->chainID[i], */
/*                pdbA->cds[0]->resSeq[i]); */
            if (   (   strncmp(pdbA->cds[0]->record[i], "ATOM  ", 6) == 0
                    || strncmp(pdbA->cds[0]->record[i], "HETATM", 6) == 0)
                && atom_selxn(pdbA->cds[0]->name[i], baseA->algo->atoms, baseA->algo->atomslxn) == 1
                && (pdbA->cds[0]->altLoc[i] == ' ' || pdbA->cds[0]->altLoc[i] == 'A')
                && range_selxn(pdbA->cds[0]->chainID[i], chains, pdbA->cds[0]->resSeq[i], lower, upper, range_num) == 1) /* DLT debug FIX THIS!!!! */
            {
                selection_index[vlen] = i;
                ++vlen;
            }
        }
    }
    else
    {
        for (i = 0; i < pdbA->vlen; ++i)
        {
/*         printf("\n\n%s, %s, %c, %c, %d\n", */
/*                pdbA->cds[0]->record[i], */
/*                pdbA->cds[0]->name[i], */
/*                pdbA->cds[0]->altLoc[i], */
/*                pdbA->cds[0]->chainID[i], */
/*                pdbA->cds[0]->resSeq[i]); */
            if (   (   strncmp(pdbA->cds[0]->record[i], "ATOM  ", 6) == 0
                    || strncmp(pdbA->cds[0]->record[i], "HETATM", 6) == 0)
                && atom_selxn(pdbA->cds[0]->name[i], baseA->algo->atoms, baseA->algo->atomslxn) == 1
                && (pdbA->cds[0]->altLoc[i] == ' ' || pdbA->cds[0]->altLoc[i] == 'A')
                && range_selxn(pdbA->cds[0]->chainID[i], chains, pdbA->cds[0]->resSeq[i], lower, upper, range_num) == 0) /* DLT debug FIX THIS!!!! */
            {
                selection_index[vlen] = i;
                ++vlen;
            }
        }
    }

    if (vlen < 3)
    {
        fprintf(stderr,
                "\n  ERROR3: 'vlen' is too small (%d) in GetCdsSelection(); not enough atoms selected. \n", vlen);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    /* baseA = CdsArrayInit(); */ /* NB!!: we don't need to initialize, since the pointer was passed above */
    CdsArrayAlloc(baseA, pdbA->cnum, vlen);
    CdsArraySetup(baseA);

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

    for (j = 0; j < pdbA->cnum; ++j)
    {
        for (i = 0; i < baseA->vlen; ++i)
        {
            strncpy(baseA->cds[j]->resName[i], pdbA->cds[j]->resName[selection_index[i]], 3);
            baseA->cds[j]->chainID[i] = pdbA->cds[j]->chainID[selection_index[i]];
            baseA->cds[j]->resSeq[i] = pdbA->cds[j]->resSeq[selection_index[i]];
            baseA->cds[j]->x[i]      = pdbA->cds[j]->x[selection_index[i]];
            baseA->cds[j]->y[i]      = pdbA->cds[j]->y[selection_index[i]];
            baseA->cds[j]->z[i]      = pdbA->cds[j]->z[selection_index[i]];
            baseA->cds[j]->o[i]      = 1.0;/* pdbA->cds[j]->occupancy[selection_index[i]]; */ /* DLT debug - shouldn't need to do this, I think prob is in SuperPose() */
            baseA->cds[j]->b[i]      = pdbA->cds[j]->tempFactor[selection_index[i]];
        }
    }
    
/* PrintCds(baseA->cds[0]); */
/* PrintCds(baseA->cds[1]); */

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

    free(selections);
    free(selection_index);
    free(upper);
    free(lower);
    free(chains);
}




PDBCdsArray
*GetPDBCds(char **argv_array, int narguments, int fmodel, int fix_atom_names)
{
    int                  i, j, k;
    int                 *models_per_pdb = NULL;
    int                 *len_array = NULL;
    int                  cnum, models, vlen, last_vlen;
    char                 pdbfile_name[FILENAME_MAX], pdbdir[FILENAME_MAX],
                         dirpdbfile_name[FILENAME_MAX];
    char                 buff[99];
    FILE                *pdbfile = NULL;
    PDBCdsArray      *pdbA = NULL;

    if (getenv("PDBDIR") != NULL)
        strncpy(pdbdir, getenv("PDBDIR"), FILENAME_MAX);

    vlen = last_vlen = 0;

    models_per_pdb = (int *) calloc(narguments, sizeof(int));
    len_array = (int *) calloc(narguments, sizeof(int));
    if (models_per_pdb == NULL || len_array == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR1: could not allocate memory for 'models_per_pdb' or 'len_array' in GetPDBCds(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    /* count models and atoms, verify consistency and allocate */
    cnum = 0;
    for (i = 0; i < narguments; ++i)
    {
        strncpy(pdbfile_name, argv_array[i], 255);

        pdbfile = fopen(pdbfile_name, "r");
        if (pdbfile == NULL)
        {
            strncpy(dirpdbfile_name, pdbdir, FILENAME_MAX - 1);
            strncat(dirpdbfile_name, pdbfile_name, FILENAME_MAX - strlen(pdbfile_name) - 1);
            
            pdbfile = fopen(dirpdbfile_name, "r");

            if (pdbfile == NULL)
            {
                perror("\n  ERROR");
                fprintf(stderr, "\n  ERROR73: file \"%s\" not found. \n", pdbfile_name);
                PrintTheseusTag();
                exit(EXIT_FAILURE);
            }
        }

        models = 0;
        last_vlen = 0;
        while (fgets(buff, (int) sizeof(buff), pdbfile) != NULL)
        {
            if (strncmp(buff, "MODEL", 5) == 0 || 
                strncmp(buff, "REMARK FRAME:", 13) == 0 ||
                strncmp(buff, "REMARK  OPLS-AA", 15) == 0) // DLTPU
            {
                vlen = 0;
                while (fgets(buff, sizeof(buff), pdbfile))
                {
                    if ((strncmp(buff, "END", 3) == 0) || (strncmp(buff, "ENDMDL", 6) == 0)) // DLTPU
                        break;
                    else if (strncmp(buff, "ATOM  ", 6) == 0 || strncmp(buff, "HETATM", 6) == 0)
                        ++vlen;
                }

                /* printf("\n vlen1 = %d", vlen); */
                if (fmodel == 1)
                    break;
                else if ((models > 0) && (vlen != last_vlen))
                {
                    fprintf(stderr,
                            "\n  WARNING1005: cds #%d in \"%s\" has a different atom number (%d) than the preceding cds (%d). \n",
                            models + 1, pdbfile_name, vlen, last_vlen);
/*                     PrintTheseusTag(); */
/*                     exit(EXIT_FAILURE); */
                }
                else if (vlen < 3)
                {
                    fprintf(stderr,
                            "\n  WARNING1006: cds #%d in \"%s\" has no selected atoms. \n",
                            models + 1, pdbfile_name);
                    PrintTheseusTag();
                    exit(EXIT_FAILURE);
                }
                else
                {
                    last_vlen = vlen;
                }

                ++models;
            }
        }

        if (models != 0 && fmodel == 0)
        {
            models_per_pdb[i] = models;
            cnum += models;
        }
        else if (models != 0 && fmodel == 1) /* read only the first model */
        {
            models_per_pdb[i] = 1;
            cnum += 1;
        }
        else
        {
            models_per_pdb[i] = 1;
            ++cnum;
            vlen = 0;
            rewind(pdbfile);

            while (fgets(buff, sizeof(buff), pdbfile) != NULL)
            {
                if (strncmp(buff, "END", 3) == 0 || (strncmp(buff, "ENDMDL", 6) == 0))
                    break;
                else if (strncmp(buff, "ATOM  ", 6) == 0 || strncmp(buff, "HETATM", 6) == 0)
                    ++vlen;
            }
        }

        fclose(pdbfile);

        if (vlen < 3)
        {
            fprintf(stderr, "\n  ERROR_002: 'vlen' is too small (%d) in GetPDBCds()", vlen); 
            fprintf(stderr, "\n  Too few atoms read. \n");
            PrintTheseusTag();
            exit(EXIT_FAILURE);
        }

        len_array[i] = vlen;
        //printf("\n vlen[%d] = %d", i, vlen);
    }

    pdbA = PDBCdsArrayInit();
    PDBCdsArrayAllocNum(pdbA, cnum);

    k = 0;
    for (i = 0; i < narguments; ++i)
    {
        for (j = 0; j < models_per_pdb[i]; ++j)
        {
            /* pdbA->cds[k] = PDBCdsInit(); */ /* already initialized and partially allocated above */
            PDBCdsAlloc(pdbA->cds[k], len_array[i]);
            ++k;
        }
    }

    /* read in all cds for all pdb files */
    for (i = 0, j = 0; i < narguments && j < cnum; ++i)
    {
        strncpy(pdbfile_name, argv_array[i], FILENAME_MAX - 1);
        ReadPDBCds(pdbfile_name, pdbA, j, models_per_pdb[i], fix_atom_names);
        j += models_per_pdb[i];
    }

    free(models_per_pdb);
    free(len_array);
    return(pdbA);
}


void
PrintCds(Cds *cds)
{
    int             i;

    fprintf(stdout, "\n veclen = %d \n", cds->vlen);
    fprintf(stdout, " filename = %s \n", cds->filename);
    fprintf(stdout, " model = %d \n",    cds->model);

    for (i = 0; i < cds->vlen; ++i)
    {
        fprintf(stdout,
                "%4d %3s %4d %8.3f %8.3f %8.3f  %8.3f\n",
                i+1,
                cds->resName[i],
                cds->resSeq[i],
                cds->x[i],
                cds->y[i],
                cds->z[i],
                cds->o[i]);
    }

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


/* Prints out a set of PDB coordinates (one model)
   adding TER cards when appropriate
   and renumbering the 'serial' field from 1
   it can handle two character chain IDs (xchainID & chainID) */
void
PrintPDBCds(FILE *pdbfile, PDBCds *pdbcds)
{
    int             i;
    unsigned int    serial = 1;

    for (i = 0; i < pdbcds->vlen; ++i)
    {
                /*  r     s     Hn  ar  xc r    i  x      y       z        o      tF     sI      e c */
                /*  ATOM   1949 1HB  ARG A 255      19.326  -3.835  -3.438  1.00  1.31           H   */

        fprintf(pdbfile,
               /*     r  s   H    n  aL   rN  x  c  rSiC       x    y    z    o   tF          sI    e    c  */
                "%-6.6s%5u %1c%3.3s%1c%-3.3s%1c%1c%4d%1c   %8.3f%8.3f%8.3f%6.2f%6.2f      %-4.4s%2.2s%2.2s\n",
                pdbcds->record[i],   serial,                  pdbcds->Hnum[i],
                pdbcds->name[i],     pdbcds->altLoc[i],    pdbcds->resName[i],
                pdbcds->xchainID[i], pdbcds->chainID[i],   pdbcds->resSeq[i],
                pdbcds->iCode[i],    pdbcds->x[i],         pdbcds->y[i],
                pdbcds->z[i],        pdbcds->occupancy[i], pdbcds->tempFactor[i],
                pdbcds->segID[i],    pdbcds->element[i],   pdbcds->charge[i]);
        /* fflush(NULL); */
        ++serial;

        /* add TER cards at end of ATOM chains */
        /* first expression evaluates whether at end of internal ATOM chain */
        /* second expression evaluates if at end of cds and ATOM chain */
        if (
            ((i + 1) <  pdbcds->vlen && 
              ( strncmp(pdbcds->record[i], "ATOM  ", 6) == 0 || strncmp(pdbcds->record[i], "HETATM", 6) == 0 )
            &&
             (pdbcds->chainID[i] != pdbcds->chainID[i+1] || pdbcds->xchainID[i] != pdbcds->xchainID[i+1]))
           ||
            ((i + 1) == pdbcds->vlen && 
             ( strncmp(pdbcds->record[i], "ATOM  ", 6) == 0 || strncmp(pdbcds->record[i], "HETATM", 6) == 0 ))
           ||
            (i < pdbcds->vlen - 1 &&
            IsNameCAorP(pdbcds->name[i]) &&
            IsNameCAorP(pdbcds->name[i+1]) &&
            strncmp(pdbcds->name[i], pdbcds->name[i+1], 3) != 0)
             /* (SqrPDBCdsDist(pdbcds, i, pdbcds, i + 1) > 64) */ /* DLT debug -- this should just check backbone atoms */
           )
        {
            fprintf(pdbfile,
                    "%-6.6s%5u      %-3.3s%1c%1c%4d%1c\n",
                    "TER",                  serial              ,    pdbcds->resName[i],
                    pdbcds->xchainID[i], pdbcds->chainID[i],   pdbcds->resSeq[i],
                    pdbcds->iCode[i]);
            ++serial;
        }
    }

    fflush(NULL);
}


void
PrintCds2File(FILE *pdbfile, Cds *cds)
{
    int             i;
    unsigned int    serial = 1;

    for (i = 0; i < cds->vlen; ++i)
    {
                /*  r     s     Hn  ar  xc r    i  x      y       z        o      tF     sI      e c */
                /*  ATOM   1949 1HB  ARG A 255      19.326  -3.835  -3.438  1.00  1.31           H   */

        fprintf(pdbfile,
               /*     r  s   H    n  aL   rN  x  c  rSiC       x    y    z    o   tF          sI    e    c  */
                "%-6.6s%5u  %3.3s %-3.3s %1c%4d    %8.3f%8.3f%8.3f%6.2f%6.2f\n",
                "ATOM",   serial,
                "CA ", cds->resName[i],
                cds->chainID[i],   cds->resSeq[i],
                cds->x[i],         cds->y[i],     cds->z[i],
                cds->o[i], cds->b[i]);
        /* fflush(NULL); */
        ++serial;

        /* add TER cards at end of ATOM chains */
        /* first expression evaluates whether at end of internal ATOM chain */
        /* second expression evaluates if at end of cds and ATOM chain */
/*         if ( */
/*             ((i + 1) <  cds->vlen &&  */
/*               ( strncmp(cds->record[i], "ATOM  ", 6) == 0 || strncmp(cds->record[i], "HETATM", 6) == 0 ) */
/*             && */
/*              (cds->chainID[i] != cds->chainID[i+1] || cds->xchainID[i] != cds->xchainID[i+1])) */
/*            || */
/*             ((i + 1) == cds->vlen &&  */
/*              ( strncmp(cds->record[i], "ATOM  ", 6) == 0 || strncmp(cds->record[i], "HETATM", 6) == 0 )) */
/*            || */
/*             (i < cds->vlen - 1 && */
/*             IsNameCAorP(cds->name[i]) && */
/*             IsNameCAorP(cds->name[i+1]) && */
/*             strncmp(cds->name[i], cds->name[i+1], 3) != 0) */
/*            ) */
/*         { */
/*             fprintf(pdbfile, */
/*                     "%-6.6s%5u      %-3.3s%1c%1c%4d%1c\n", */
/*                     "TER",                  serial              ,    cds->resName[i], */
/*                     cds->xchainID[i], cds->chainID[i],   cds->resSeq[i], */
/*                     cds->iCode[i]); */
/*             ++serial; */
/*         } */
    }

    fflush(NULL);
}


void
PrintOccPDBCds(FILE *pdbfile, PDBCds *pdbcds)
{
    int             i;
    unsigned int    serial = 1;

    for (i = 0; i < pdbcds->vlen; ++i)
    {
        if (pdbcds->occupancy[i] == 1.0)
        {
                    /*  r     s     Hn  ar  xc r    i  x      y       z        o      tF     sI      e c */
                    /*  ATOM   1949 1HB  ARG A 255      19.326  -3.835  -3.438  1.00  1.31           H   */
            fprintf(pdbfile,
                   /*     r  s   H    n  aL   rN  x  c  rSiC       x    y    z    o   tF          sI    e    c  */
                    "%-6.6s%5u %1c%3.3s%1c%-3.3s%1c%1c%4d%1c   %8.3f%8.3f%8.3f%6.2f%6.2f      %-4.4s%2.2s%2.2s\n",
                    pdbcds->record[i],   serial,                  pdbcds->Hnum[i],
                    pdbcds->name[i],     pdbcds->altLoc[i],    pdbcds->resName[i],
                    pdbcds->xchainID[i], pdbcds->chainID[i],   pdbcds->resSeq[i],
                    pdbcds->iCode[i],    pdbcds->x[i],         pdbcds->y[i],
                    pdbcds->z[i],        pdbcds->occupancy[i], pdbcds->tempFactor[i],
                    pdbcds->segID[i],    pdbcds->element[i],   pdbcds->charge[i]);
            /* fflush(NULL); */
            ++serial;
    
            /* add TER cards at end of ATOM chains */
            /* first expression evaluates whether at end of internal ATOM chain */
            /* second expression evaluates if at end of cds and ATOM chain */
            if (
                ((i + 1) <  pdbcds->vlen && 
                 ( strncmp(pdbcds->record[i], "ATOM  ", 6) == 0 || strncmp(pdbcds->record[i], "HETATM", 6) == 0 )
                &&
                 (pdbcds->chainID[i] != pdbcds->chainID[i+1] || pdbcds->xchainID[i] != pdbcds->xchainID[i+1]))
               ||
                ((i + 1) == pdbcds->vlen 
                && 
                ( strncmp(pdbcds->record[i], "ATOM  ", 6) == 0 || strncmp(pdbcds->record[i], "HETATM", 6) == 0))
               ||
                (i < pdbcds->vlen - 1 &&
                IsNameCAorP(pdbcds->name[i]) &&
                IsNameCAorP(pdbcds->name[i+1]) &&
                strncmp(pdbcds->name[i], pdbcds->name[i+1], 3) != 0)
                 /* (SqrPDBCdsDist(pdbcds, i, pdbcds, i + 1) > 64) */ /* DLT debug -- this should just check backbone atoms */
               )
            {
                fprintf(pdbfile,
                        "%-6.6s%5u      %-3.3s%1c%1c%4d%1c\n",
                        "TER",                  serial              ,    pdbcds->resName[i],
                        pdbcds->xchainID[i], pdbcds->chainID[i],   pdbcds->resSeq[i],
                        pdbcds->iCode[i]);
                ++serial;
            }
        }
    }

    fflush(NULL);
}


int
IsNameCAorP(char *name)
{
    if (strlen(name) < 3)
        return(0);
    else if (strncmp(name, "CA ", 3) == 0 || strncmp(name, "P  ", 3) == 0)
        return(1);
    else
        return(0);
}


// int
// ReadCds(char *pdbfile_name, CdsArray *cdsA, int cds_index, int modelnum)
// {
//     char            buff[99], alt;
//     int             i = 0;
//     int             ncoord;
//     FILE           *pdbfile = NULL;
// 
//     pdbfile = fopen(pdbfile_name, "r");
//     if (pdbfile == NULL)
//     {
//         fprintf(stderr, "\n  ERROR: file \"%s\" not found. \n", pdbfile_name);
//         PrintTheseusTag();
//         exit(EXIT_FAILURE);
//     }
// 
//     if (cdsA->cds[cds_index]->vlen == 0)
//     {
//         while (fgets(buff, sizeof(buff), pdbfile))
//         {
//             if (strncmp(buff, "ENDMDL", 6) == 0 ||
//                 strncmp(buff, "END   ", 6) == 0)
//                 break;
//     
//             if (!strncmp(buff, "ATOM  ", 6)
//                 && (   strncmp(&buff[13], "CA ", 3) == 0
//                     || strncmp(&buff[13], "P  ", 3) == 0))
//             {
//                 alt = buff[16];     /* alternate position indicator */
//                 if (alt == ' ' || alt == 'A' || alt == '1' || alt == 'L' || alt == 'O')
//                     ++i;
//             }    
//         }
// 
//         cdsA->vlen = i;
//         rewind(pdbfile);
//     }
// 
//     for (i = 0; i < modelnum; ++i)
//     {
//         /* move to next model */
//         while (fgets(buff, sizeof(buff), pdbfile))
//         {
//             if (!strncmp(buff, "MODEL", 5))
//             {
//                 cdsA->cds[cds_index + i]->model = i;
//                 break;
//             }
//         }
// 
//         /* read in the model's cds */
//         ncoord = 0;
//         while (fgets(buff, sizeof(buff), pdbfile))
//         {
//             if (!strncmp(buff, "ENDMDL", 6) ||
//                 !strncmp(buff, "END   ", 6))
//                 break;
// 
//             if (!strncmp(buff, "ATOM  ", 6) &&
//                 (!strncmp(&buff[13], "CA ", 3) ||
//                  /*!strncmp(&buff[13], "CB ", 3) ||*/
//                  !strncmp(&buff[13], "P  ", 3)))
//             {
//                 alt = buff[16]; /* alternate position indicator */
//                 if (alt == ' ' || alt == 'A' || alt == '1' || alt == 'L' || alt == 'O')
//                 {
//                     sscanf(&buff[17],
//                            "%3c%*1c%4d%*4c%8lf%8lf%8lf%6lf%6lf",
//                             cdsA->cds[cds_index + i]->resName[ncoord],
//                            &cdsA->cds[cds_index + i]->resSeq[ncoord],
//                            &cdsA->cds[cds_index + i]->x[ncoord],
//                            &cdsA->cds[cds_index + i]->y[ncoord],
//                            &cdsA->cds[cds_index + i]->z[ncoord],
//                            &cdsA->cds[cds_index + i]->o[ncoord],
//                            &cdsA->cds[cds_index + i]->b[ncoord]);
//     
//                     ncoord++;
//                 }
//             }
//         }
//     }
// 
//     fclose(pdbfile);
//     return (EXIT_SUCCESS);
// }


void
WriteModelFile(PDBCdsArray *pdbA, char *outfile_name)
{
    FILE           *pdbfile = NULL;
    int             i;

    /* ////////////////////////////////////////////////////////////// */
    pdbfile = myfopen(outfile_name, "w");
    if (pdbfile ==NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);

    for (i = 0; i < pdbA->cnum; ++i)
    {
        fprintf(pdbfile, "MODEL %8d\n", i+1);
        PrintPDBCds(pdbfile, pdbA->cds[i]);
        fprintf(pdbfile, "ENDMDL\n");
    }
    fprintf(pdbfile, "END\n");

    fclose(pdbfile);
}


void
WriteTheseusModelFile(PDBCdsArray *pdbA, Algorithm *algo, Statistics *stats, char *outfile_name)
{
    FILE           *pdbfile = NULL;
    int             i;

    /* ////////////////////////////////////////////////////////////// */
    
/*     pdbfile = fopen(outfile_name, "w"); */
/*     if (pdbfile ==NULL) */
/*     { */
/*         perror("\n  ERROR"); */
/*         fprintf(stderr, */
/*                 "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name); */
/*         PrintTheseusTag(); */
/*         exit(EXIT_FAILURE); */
/*     } */

    pdbfile = myfopen(outfile_name, "w");
    if (pdbfile == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);
    PrintModelFileStats(pdbfile, pdbA, algo, stats);

    for (i = 0; i < pdbA->cnum; ++i)
    {
        fprintf(pdbfile, "MODEL %8d\n", i+1);
        PrintPDBCds(pdbfile, pdbA->cds[i]);
        fprintf(pdbfile, "ENDMDL\n");
    }
    fprintf(pdbfile, "END\n");

    fclose(pdbfile);
}


void
WriteTheseusModelFileNoStats(PDBCdsArray *pdbA, Algorithm *algo, char *outfile_name)
{
    FILE           *pdbfile = NULL;
    int             i;

    /* ////////////////////////////////////////////////////////////// */
    
/*     pdbfile = fopen(outfile_name, "w"); */
/*     if (pdbfile ==NULL) */
/*     { */
/*         perror("\n  ERROR"); */
/*         fprintf(stderr, */
/*                 "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name); */
/*         PrintTheseusTag(); */
/*         exit(EXIT_FAILURE); */
/*     } */

    pdbfile = myfopen(outfile_name, "w");
    if (pdbfile == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);

    for (i = 0; i < pdbA->cnum; ++i)
    {
        fprintf(pdbfile, "MODEL %8d\n", i+1);
        PrintPDBCds(pdbfile, pdbA->cds[i]);
        fprintf(pdbfile, "ENDMDL\n");
    }
    fprintf(pdbfile, "END\n");

    fclose(pdbfile);
}


void
WriteTheseusCdsModelFile(CdsArray *cdsA, char *outfile_name)
{
    FILE           *pdbfile = NULL;
    int             i;

    /* ////////////////////////////////////////////////////////////// */

    pdbfile = myfopen(outfile_name, "w");
    if (pdbfile == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);
    //PrintModelFileStats(pdbfile, pdbA, algo, stats);

    for (i = 0; i < cdsA->cnum; ++i)
    {
        fprintf(pdbfile, "MODEL %8d\n", i+1);
        PrintCds2File(pdbfile, cdsA->cds[i]);
        fprintf(pdbfile, "ENDMDL\n");
    }
    fprintf(pdbfile, "END\n");

    fclose(pdbfile);
}


void
WriteTheseusPDBFiles(PDBCdsArray *pdbA, Algorithm *algo, Statistics *stats)
{
    FILE           *pdbfile = NULL;
    int             i;
    char            outfile_name[FILENAME_MAX];

    for (i = 0; i < pdbA->cnum; ++i)
    {
        strncpy(outfile_name, algo->rootname, strlen(algo->rootname)+1);
        strcat(outfile_name, "_");
        strncat(outfile_name, pdbA->cds[i]->filename, strlen(pdbA->cds[i]->filename)+1);
        //strcpy(outfile_name, getroot(pdbA->cds[i]->filename));
        //strcat(outfile_name, "_ths.pdb");

        pdbfile = myfopen(outfile_name, "w");
        if (pdbfile == NULL)
        {
            perror("\n  ERROR");
            fprintf(stderr,
                    "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name);
            PrintTheseusTag();
            exit(EXIT_FAILURE);
        }

        PrintTheseusModelHeader(pdbfile);
        PrintModelFileStats(pdbfile, pdbA, algo, stats);
        PrintPDBCds(pdbfile, pdbA->cds[i]);
        fprintf(pdbfile, "END\n");
        fclose(pdbfile);
    }
}


void
WriteOlveModelFile(PDBCdsArray *pdbA, Algorithm *algo, Statistics *stats, char *outfile_name)
{
    FILE           *pdbfile = NULL;
    int             i;

    /* ////////////////////////////////////////////////////////////// */
    pdbfile = myfopen(outfile_name, "w");
    if (pdbfile ==NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);
    PrintModelFileStats(pdbfile, pdbA, algo, stats);

    for (i = 0; i < pdbA->cnum; ++i)
    {
        fprintf(pdbfile, "MODEL %8d\n", i+1);
        PrintOccPDBCds(pdbfile, pdbA->cds[i]);
        fprintf(pdbfile, "ENDMDL\n");
    }
    fprintf(pdbfile, "END\n");

    fclose(pdbfile);
}


void
PrintTheseusModelHeader(FILE *pdbfile)
{
    time_t          tod;

    time(&tod);

    fprintf(pdbfile, "REMARK ===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-=\n");
    fprintf(pdbfile, "REMARK + File made by THESEUS                                                  +\n");
    fprintf(pdbfile, "REMARK + Multiple maximum likelihood superpositioning                          +\n");
    fprintf(pdbfile, "REMARK + Author Douglas L. Theobald                                            +\n");
    fprintf(pdbfile, "REMARK + dtheobald@brandeis.edu                                                +\n");
    fprintf(pdbfile, "REMARK =-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===\n");
    fprintf(pdbfile, "REMARK\n");
    fprintf(pdbfile, "REMARK   THESEUS v%s run by user '%-.21s' at %s", VERSION, getenv("USER"), asctime(localtime(&tod)));
    fprintf(pdbfile, "REMARK   on machine '%-.55s'\n", getenv("HOST"));
    fprintf(pdbfile, "REMARK   in directory '%-.55s'\n", getenv("PWD"));
    fprintf(pdbfile, "REMARK =-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===\n");
}


void
PrintModelFileStats(FILE *pdbfile, PDBCdsArray *pdbA, Algorithm *algo, Statistics *stats)
{
    int             i;

    for (i = 0; i < strlen(algo->cmdline); i += 71)
        fprintf(pdbfile, "REMARK   %-.71s\n", &algo->cmdline[i]);
    fprintf(pdbfile, "REMARK\n");
    for (i = 0; i < pdbA->cnum; ++i)
        fprintf(pdbfile, "REMARK   MODEL %8d    %s  %6d\n", i+1, pdbA->cds[i]->filename, pdbA->cds[i]->vlen);
    fprintf(pdbfile, "REMARK\n");
    fprintf(pdbfile, "REMARK =-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===\n");
    fprintf(pdbfile, "REMARK   unweighted <sigma>         %10.5f\n", stats->stddev);
    fprintf(pdbfile, "REMARK   Classical pairwise <RMSD>  %10.5f\n", stats->ave_paRMSD);
    fprintf(pdbfile, "REMARK   Maximum Likelihood <RMSD>  %10.5f\n", stats->mlRMSD);
    if (algo->hierarch != 0)
        fprintf(pdbfile, "REMARK   Hierarchical var (%3.2e, %3.2e) chi^2  %10.5f\n",
                stats->hierarch_p1, stats->hierarch_p2, stats->hierarch_chi2);
    if (algo->htrans != 0)
        fprintf(pdbfile, "REMARK   Translation normal chi^2   %10.5f\n", stats->htrans_chi2);
    fprintf(pdbfile, "REMARK   Log Likelihood          %11.2f\n", stats->logL);
    fprintf(pdbfile, "REMARK   AIC                     %11.2f\n", stats->AIC);
    fprintf(pdbfile, "REMARK   BIC                     %11.2f\n", stats->BIC);

    fprintf(pdbfile, "REMARK   Rotational, translational, covar chi^2      %11.2f\n",
            stats->chi2);

    if (algo->hierarch != 0)
    {
        fprintf(pdbfile, "REMARK   Hierarchical var (%3.2e, %3.2e) chi^2 %11.2f\n",
                stats->hierarch_p1, stats->hierarch_p2, stats->hierarch_chi2);
        fprintf(pdbfile, "REMARK   Omnibus chi^2                               %11.2f\n",
                stats->omnibus_chi2);
    }

    if (algo->htrans != 0)
        fprintf(pdbfile, "REMARK   Translation normal chi^2      %10.5f\n", stats->htrans_chi2);

    fprintf(pdbfile, "REMARK   skewness               %7.3f\n", stats->skewness[3]);
    fprintf(pdbfile, "REMARK   skewness Z-value       %7.3f\n", fabs(stats->skewness[3]/stats->SES));
    fprintf(pdbfile, "REMARK   kurtosis               %7.3f\n", stats->kurtosis[3]);
    fprintf(pdbfile, "REMARK   kurtosis Z-value       %7.3f\n", fabs(stats->kurtosis[3]/stats->SEK));
    fprintf(pdbfile, "REMARK =-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===-===\n");
}


void
WriteCdsFile(Cds *cds, char *outfile_name)
{
    PDBCdsArray *pdbA = PDBCdsArrayInit();
    PDBCdsArrayAlloc(pdbA, 0, cds->vlen);
    CopyCds2PDB(pdbA->avecds, cds);
    WritePDBCdsFile(pdbA->avecds, outfile_name);
    PDBCdsArrayDestroy(&pdbA);
}


void
WritePDBCdsFile(PDBCds *cds, char *file_name)
{
    FILE           *pdbfile = NULL;

    pdbfile = myfopen(file_name, "w");
    if (pdbfile ==NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n", file_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);
    PrintPDBCds(pdbfile, cds);
    fprintf(pdbfile, "END\n\n");

    fclose(pdbfile);
}


void
WriteAveCdsFile(CdsArray *cdsA, char *outfile_name)
{
    PDBCdsArray *pdbA = PDBCdsArrayInit();
    PDBCdsArrayAlloc(pdbA, 0, cdsA->vlen);
    CopyCds2PDB(pdbA->avecds, cdsA->avecds);
    WriteAvePDBCdsFile(pdbA, outfile_name);
    PDBCdsArrayDestroy(&pdbA);
}


void
WriteAveCds(CdsArray *cdsA, char *outfile_name)
{
    FILE           *pdbfile = NULL;

    pdbfile = myfopen(outfile_name, "w");
    if (pdbfile == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n", outfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);
    PrintCds2File(pdbfile, cdsA->avecds);
    fprintf(pdbfile, "END\n");
    fclose(pdbfile);
}


/* writes a pdb file of the average cds */
void
WriteAvePDBCdsFile(PDBCdsArray *pdbA, char *outfile_name)
{
    FILE           *pdbfile = NULL;

    /* char            avecds_filename[512]; */

    /* strcpy(avecds_filename, getroot(outfile_name)); */
    /* strcat(outfile_name, "_ave.pdb"); */

    pdbfile = myfopen(outfile_name, "w");
    if (pdbfile ==NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR99: could not open file '%s' for writing. \n\n", outfile_name);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    PrintTheseusModelHeader(pdbfile);
    PrintPDBCds(pdbfile, pdbA->avecds);
    fprintf(pdbfile, "END\n\n");

    fclose(pdbfile);
}


void
WriteBinPDBCdsArray(PDBCdsArray *pdbA)
{
    int             i, cnum, vlen;
    FILE           *fp = NULL;

    fp = fopen("theseus.bin", "wb");

    cnum = pdbA->cnum;
    vlen = pdbA->vlen;

    fwrite("theseus binary", sizeof(char), 14, fp);
    fwrite(&cnum, sizeof(int), 1, fp);
    fwrite(&vlen, sizeof(int), 1, fp);

/*     fwrite(pdbA->var,    sizeof(double), vlen, fp); */
/*     fwrite(pdbA->paRMSD, sizeof(double), cnum, fp); */

    for (i = 0; i < cnum; ++i)
        WriteBinPDBCds(pdbA->cds[i], fp);

    WriteBinPDBCds(pdbA->avecds, fp);

    fclose(fp);
}


PDBCdsArray
*ReadBinPDBCdsArray(char *filename)
{
    int             i, cnum, vlen;
    FILE           *fp = NULL;
    PDBCdsArray *pdbA;
    char            bincheck[14];

    fp = fopen(filename, "rb");
 
    if (fp == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR738: file \"%s\" not found. \n", filename);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    fread(bincheck, sizeof(char), 14, fp);

    if (strncmp(bincheck, "theseus binary", 14) != 0)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR748: file \"%s\" does not appear to be a THESEUS binary data file. \n", filename);
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    fread(&cnum, sizeof(int), 1, fp);
    fread(&vlen, sizeof(int), 1, fp);

    pdbA = PDBCdsArrayInit();
    PDBCdsArrayAllocNum(pdbA, cnum);

/*     fread(pdbA->var,    sizeof(double), vlen, fp); */
/*     fread(pdbA->paRMSD, sizeof(double), cnum, fp); */

    for (i = 0; i < cnum; ++i)
        ReadBinPDBCds(pdbA->cds[i], fp);

    ReadBinPDBCds(pdbA->avecds, fp);

    fclose(fp);
    return(pdbA);
}


void
WriteBinPDBCds(PDBCds *pdbcds, FILE *fp)
{
    int             vlen;

    vlen = pdbcds->vlen;

    fwrite(&vlen, sizeof(int), 1, fp);

    fwrite(pdbcds->filename, sizeof(char), FILENAME_MAX, fp);
    fwrite(&pdbcds->model, sizeof(int), 1, fp);

    WriteBinMatrix(pdbcds->matrix, 3, 3, fp);
    fwrite(pdbcds->translation, sizeof(double), 3, fp);

    fwrite(pdbcds->serial,     sizeof(int),    vlen, fp);
    fwrite(pdbcds->Hnum,       sizeof(char),   vlen, fp);
    fwrite(pdbcds->altLoc,     sizeof(char),   vlen, fp);
    fwrite(pdbcds->xchainID,   sizeof(char),   vlen, fp);
    fwrite(pdbcds->chainID,    sizeof(char),   vlen, fp);
    fwrite(pdbcds->resSeq,     sizeof(int),    vlen, fp);
    fwrite(pdbcds->iCode,      sizeof(char),   vlen, fp);
    fwrite(pdbcds->x,          sizeof(double), vlen, fp);
    fwrite(pdbcds->y,          sizeof(double), vlen, fp);
    fwrite(pdbcds->z,          sizeof(double), vlen, fp);
    fwrite(pdbcds->occupancy,  sizeof(double), vlen, fp);
    fwrite(pdbcds->tempFactor, sizeof(double), vlen, fp);

    fwrite(pdbcds->record_space,  sizeof(char), 8 * vlen, fp);
    fwrite(pdbcds->name_space,    sizeof(char), 4 * vlen, fp);
    fwrite(pdbcds->resName_space, sizeof(char), 4 * vlen, fp);
    fwrite(pdbcds->segID_space,   sizeof(char), 8 * vlen, fp);
    fwrite(pdbcds->element_space, sizeof(char), 4 * vlen, fp);
    fwrite(pdbcds->charge_space,  sizeof(char), 4 * vlen, fp);
}


void
ReadBinPDBCds(PDBCds *pdbcds, FILE *fp)
{
    int             vlen;

    fread(&vlen, sizeof(int), 1, fp);
    PDBCdsAlloc(pdbcds, vlen);

    fread(pdbcds->filename, sizeof(char), FILENAME_MAX, fp);
    fread(&pdbcds->model, sizeof(int), 1, fp);
    ReadBinMatrix(pdbcds->matrix, fp);
    fread(pdbcds->translation, sizeof(double), 3, fp);

    fread(pdbcds->serial,     sizeof(int),    vlen, fp);
    fread(pdbcds->Hnum,       sizeof(char),   vlen, fp);
    fread(pdbcds->altLoc,     sizeof(char),   vlen, fp);
    fread(pdbcds->xchainID,   sizeof(char),   vlen, fp);
    fread(pdbcds->chainID,    sizeof(char),   vlen, fp);
    fread(pdbcds->resSeq,     sizeof(int),    vlen, fp);
    fread(pdbcds->iCode,      sizeof(char),   vlen, fp);
    fread(pdbcds->x,          sizeof(double), vlen, fp);
    fread(pdbcds->y,          sizeof(double), vlen, fp);
    fread(pdbcds->z,          sizeof(double), vlen, fp);
    fread(pdbcds->occupancy,  sizeof(double), vlen, fp);
    fread(pdbcds->tempFactor, sizeof(double), vlen, fp);

    fread(pdbcds->record_space,  sizeof(char), 8 * vlen, fp);
    fread(pdbcds->name_space,    sizeof(char), 4 * vlen, fp);
    fread(pdbcds->resName_space, sizeof(char), 4 * vlen, fp);
    fread(pdbcds->segID_space,   sizeof(char), 8 * vlen, fp);
    fread(pdbcds->element_space, sizeof(char), 4 * vlen, fp);
    fread(pdbcds->charge_space,  sizeof(char), 4 * vlen, fp);
}


void
WriteBinMatrix(double **mat, int rows, int cols, FILE *fp)
{
    fwrite(&rows, sizeof(int), 1, fp);
    fwrite(&cols, sizeof(int), 1, fp);
    fwrite(&mat[0][0], sizeof(double), rows * cols, fp);
}


void
ReadBinMatrix(double **mat, FILE *fp)
{
    int             rows, cols;

    fread(&rows, sizeof(int), 1, fp);
    fread(&cols, sizeof(int), 1, fp);
    fread(&mat[0][0], sizeof(double), rows * cols, fp);
}
