/*
    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 <float.h>
#include <ctype.h>
#include "DLTutils.h"
#include "Error.h"
#include "pdbMalloc.h"
#include "Cds.h"
#include "PDBCds.h"
#include "Cds.h"
#include "PDBCds.h"
#include "DLTmath.h"


Seq2PDB
*Seq2pdbInit(void)
{
    Seq2PDB        *seq2pdb = NULL;

    seq2pdb = malloc(sizeof(Seq2PDB));

    seq2pdb->pdbfile_name = NULL;
    seq2pdb->seqname = NULL;
    seq2pdb->map = NULL;
    seq2pdb->singletons = NULL;
    seq2pdb->pdbfile_name_space = NULL;
    seq2pdb->seqname_space = NULL;

    return(seq2pdb);
}


void
Seq2pdbAlloc(Seq2PDB *seq2pdb, const int seqnum)
{
    int             i;

    seq2pdb->seqnum = seqnum;

    seq2pdb->pdbfile_name = (char **) malloc(seqnum * sizeof(char *));
    seq2pdb->seqname = (char **) malloc(seqnum * sizeof(char *));
    seq2pdb->map = (int *) calloc(seqnum, sizeof(int));

    /* allocate space for the fields in total */
    seq2pdb->pdbfile_name_space = (char *) malloc(FILENAME_MAX * seqnum * sizeof(char));
    seq2pdb->seqname_space = (char *) malloc(128 * seqnum * sizeof(char));

    if (   seq2pdb->pdbfile_name == NULL
        || seq2pdb->pdbfile_name == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR73: could not allocate memory in function Seq2pdbAlloc(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    /*  now 'point' the pointers */
    for (i = 0; i < seqnum; ++i)
    {
        seq2pdb->pdbfile_name[i] = seq2pdb->pdbfile_name_space  + (i * FILENAME_MAX);
        seq2pdb->seqname[i] = seq2pdb->seqname_space  + (i * 128);
    }
}


void
Seq2pdbDestroy(Seq2PDB **seq2pdb_ptr)
{
    Seq2PDB *seq2pdb = *seq2pdb_ptr;

    free(seq2pdb->pdbfile_name_space);
    free(seq2pdb->seqname_space);

    free(seq2pdb->pdbfile_name);
    free(seq2pdb->seqname);
    free(seq2pdb->map);
    if (seq2pdb->singletons != NULL)
        free(seq2pdb->singletons);

    MSAfree(&(seq2pdb->msa));

    free(seq2pdb);
    *seq2pdb_ptr = NULL;
}


void
AlgorithmDestroy(Algorithm *algo)
{
    int         i;
    /* printf("\n AlgorithmDestroy(%p)", algo);fflush(NULL); */
    if (algo->argv != NULL)
    {
        for (i = 0; i < algo->argc; ++i)
            if (algo->argv[i] != NULL)
                free(algo->argv[i]);

        free(algo->argv);
    }

    free(algo);
}


Algorithm
*AlgorithmInit(void)
{
    Algorithm      *algo = (Algorithm *) malloc (sizeof(Algorithm));
    if (algo == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR6: could not allocate memory in function CdsArrayInit(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }
    /* printf("\n AlgorithmInit(%p)", algo);fflush(NULL); */
    algo->argv         = NULL;
    mystrcpy(algo->rootname, "theseus");
    algo->weight       = 0;
    algo->verbose      = 0; /* verbose behavior = 1 */
    algo->method       = 3; /* 7: LAPACK SVD, 10: my classic Jacobi SVD */
    algo->print_trans  = 0;
    algo->write_file   = 1;
    algo->print_weight = 0;
    algo->precision    = 1e-7;
    algo->iterations   = 200;
    algo->selection    = NULL;
    algo->atomslxn     = NULL;
    algo->revsel       = 0;
    algo->atoms        = 0;
    algo->reflection   = 0;
    algo->embedave     = 0; /* 2 = biased ML embedded structure */
    algo->landmarks    = 0;
    algo->writestats   = 1;
    algo->random       = 0;
    algo->pca          = 0;
    algo->fullpca      = 0;
    algo->cormat       = 1;
    algo->tenberge     = 0;
    algo->morph        = 0;
    algo->stats        = 0;
    algo->constant     = -1e-40; /* If negative, finds min var empirically */
    algo->info         = 0;
    algo->princaxes    = 1;
    algo->nullrun      = 0;
    algo->binary       = 0;
    algo->modelpca     = 0;
    algo->raxes[0] =
    algo->raxes[1] = 
    algo->raxes[2] =   1.0;
    algo->mbias        = 0;
    algo->notrans      = 0;
    algo->norot        = 0;
    algo->alignment    = 0;
    algo->fmodel       = 0;
    algo->covweight    = 0;
    algo->varweight    = 1;
    algo->hierarch     = 1;
    algo->leastsquares = 0;
    algo->filenum      = 0;
    algo->infiles      = NULL;
    algo->noave        = 0;
    algo->noinnerloop  = 0;
    algo->htrans       = 0;
    algo->rounds       = 0;
    algo->innerrounds  = 0;
    algo->fasta        = 0;
    algo->olve         = 0;
    algo->abort        = 0;
    algo->seed         = 0;
    algo->mixture      = 1;
    algo->threads      = 0;
    algo->printlogL    = 0;
    algo->bfact        = 0;
    algo->convlele     = 0;
    algo->param[0] = algo->param[1] = 1.0;
    algo->radii[0] = algo->radii[1] = algo->radii[2] = 50.0;
    algo->ssm          = 0;
    algo->lele5        = 0;
    algo->bayes        = 0;
    algo->ipmat        = 0;
    algo->commandeur   = 0;
    algo->missing      = 0;
    algo->scale        = 0;
    algo->instfile     = 0;
    algo->pu           = 0;
    algo->FragDist     = 0;
    algo->atom_names   = 0;
    algo->scalefactor  = 1.0;

    return(algo);
}


Statistics
*StatsInit(void)
{
    Statistics     *stats = NULL;
    int             i;

    stats = (Statistics *) malloc(sizeof(Statistics));
    if (stats == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR6: could not allocate memory in function StatsInit(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < 4; ++i)
        stats->skewness[i] = stats->kurtosis[i] = 0.0;

    stats->hierarch_p1 = stats->hierarch_p2 = 1.0;
    stats->precision = 0.0;

    return(stats);
}


CdsArray
*CdsArrayInit(void)
{
    CdsArray *cdsA = NULL;

    cdsA = (CdsArray *) malloc(sizeof(CdsArray));
/* printf("\ncdsA malloc(%p)", (void *) cdsA); */
/* fflush(NULL); */
    if (cdsA == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR6: could not allocate memory in function CdsArrayInit(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    strncpy(cdsA->outfile_name, "theseus.pdb", 11);
    cdsA->anchorf_name = NULL;
    cdsA->mapfile_name = NULL;
    cdsA->msafile_name = NULL;
    cdsA->pdbA = NULL;
    cdsA->scratchA = NULL;

    cdsA->cds = NULL;
    cdsA->avecds = NULL;
    cdsA->tcds = NULL;
    cdsA->jkcds = NULL;

    cdsA->w = NULL;
    cdsA->var = NULL;
    cdsA->df = NULL;
    cdsA->S2 = NULL;

    cdsA->algo = AlgorithmInit();
    cdsA->stats = StatsInit();

    cdsA->residuals = NULL;

    cdsA->Var_matrix = NULL;
    cdsA->Dij_matrix = NULL;
    cdsA->distmat = NULL;
    cdsA->CovMat = NULL;
    cdsA->WtMat = NULL;
    cdsA->FullCovMat = NULL;
    cdsA->MVCovMat = NULL;
    cdsA->SCovMat = NULL;

    cdsA->pcamat = NULL;
    cdsA->pcavals = NULL;
    cdsA->modpcamat = NULL;
    cdsA->modpcavals = NULL;

    cdsA->tmpmat1 = NULL;
    cdsA->tmpmat2 = NULL;
    cdsA->tmpmatKK1 = NULL;
    cdsA->tmpmatKK2 = NULL;
    cdsA->tmpvecK = NULL;
    cdsA->tmpmat3K = NULL;
    cdsA->tmpmatK3a = NULL;
    cdsA->tmpmatK3b = NULL;

    cdsA->tmpmat3a = MatAlloc(3, 3);
    cdsA->tmpmat3b = MatAlloc(3, 3);
    cdsA->tmpmat3c = MatAlloc(3, 3);
    cdsA->tmpmat3d = MatAlloc(3, 3);

    return(cdsA);
}


void
CdsArrayAllocNum(CdsArray *cdsA, const int cnum)
{
    cdsA->cnum = cnum;

    cdsA->cds = (Cds **) malloc(cnum * sizeof(Cds *));
    if (cdsA->cds == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR6: could not allocate memory in function CdsArrayAlloc(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }
}


void
CdsArrayAllocLen(CdsArray *cdsA, const int vlen)
{
    int             i;

    cdsA->vlen = vlen;

    for (i = 0; i < cdsA->cnum; ++i)
    {
        cdsA->cds[i] = CdsInit();
        CdsAlloc(cdsA->cds[i], vlen);
    }

    cdsA->avecds = CdsInit();
    CdsAlloc(cdsA->avecds, vlen);
    cdsA->tcds = CdsInit();
    CdsAlloc(cdsA->tcds, vlen);
    cdsA->jkcds = CdsInit();
    CdsAlloc(cdsA->jkcds, vlen);

    cdsA->var = (double *) calloc(vlen, sizeof(double));
    cdsA->w   = (double *) calloc(vlen, sizeof(double));
    cdsA->df  = (int *) calloc(vlen, sizeof(int));
}


void
CdsArrayAlloc(CdsArray *cdsA, const int cnum, const int vlen)
{
    CdsArrayAllocNum(cdsA, cnum);
    CdsArrayAllocLen(cdsA, vlen);
}


void
CdsArraySetup(CdsArray *cdsA)
{
    memsetd(cdsA->var, 1.0, cdsA->vlen);
    memsetd(cdsA->w, 1.0, cdsA->vlen);
}


void
CdsArrayDestroy(CdsArray **cdsA_ptr)
{
    int             i;

    CdsArray *cdsA = *cdsA_ptr;

    free(cdsA->msafile_name);
    cdsA->msafile_name = NULL;
    free(cdsA->mapfile_name);
    cdsA->mapfile_name = NULL;

    for (i = 0; i < cdsA->cnum; ++i)
    {
        CdsDestroy(&(cdsA->cds[i]));
    }

    free(cdsA->cds);
    cdsA->cds = NULL;

    CdsDestroy(&(cdsA->avecds));
    CdsDestroy(&(cdsA->tcds));
    CdsDestroy(&(cdsA->jkcds));

    free(cdsA->stats);
    cdsA->stats = NULL;

    if (cdsA->algo->selection != NULL)
    {
        free(cdsA->algo->selection);
        cdsA->algo->selection = NULL;
    }

    if (cdsA->algo->atomslxn != NULL)
    {
        free(cdsA->algo->atomslxn);
        cdsA->algo->atomslxn = NULL;
    }

    AlgorithmDestroy(cdsA->algo);

    DistMatsDestroy(cdsA);
    CovMatsDestroy(cdsA);
    PCADestroy(cdsA);

    if (cdsA->residuals != NULL)
    {
        free(cdsA->residuals);
        cdsA->residuals = NULL;
    }

    free(cdsA->w);
    cdsA->w = NULL;
    free(cdsA->var);
    cdsA->var = NULL;
    free(cdsA->df);
    cdsA->df = NULL;

    if (cdsA->S2 != NULL)
    {
        free(cdsA->S2);
        cdsA->S2 = NULL;
    }

    free(cdsA);
    *cdsA_ptr = NULL;
}


void
DistMatsAlloc(CdsArray *cdsA)
{
    /* cdsA->Var_matrix = MatAlloc(cdsA->vlen, cdsA->vlen); */
    cdsA->Dij_matrix = MatAlloc(cdsA->vlen, cdsA->vlen);
    /* cdsA->distmat    = Mat3DInit(cdsA->cnum, cdsA->vlen, cdsA->vlen); */
}


void
DistMatsDestroy(CdsArray *cdsA)
{
/*     if (cdsA->Var_matrix != NULL) */
/*         MatDestroy(&(cdsA->Var_matrix)); */
    if (cdsA->Dij_matrix != NULL)
        MatDestroy(&(cdsA->Dij_matrix));
/*     if (cdsA->distmat != NULL) */
/*         Mat3DDestroy(&(cdsA->distmat)); */
}


void
CovMatsDestroy(CdsArray *cdsA)
{
    if (cdsA->CovMat != NULL)
        MatDestroy(&(cdsA->CovMat));
    if (cdsA->FullCovMat != NULL)
        MatDestroy(&(cdsA->FullCovMat));
    if (cdsA->WtMat != NULL)
        MatDestroy(&(cdsA->WtMat));
    if (cdsA->tmpmatKK1 != NULL)
        MatDestroy(&(cdsA->tmpmatKK1));
    if (cdsA->tmpmatKK2 != NULL)
        MatDestroy(&(cdsA->tmpmatKK2));
    if (cdsA->tmpvecK != NULL)
        free(cdsA->tmpvecK);
    if (cdsA->tmpmat1 != NULL)
        MatDestroy(&(cdsA->tmpmat1));
    if (cdsA->tmpmat2 != NULL)
        MatDestroy(&(cdsA->tmpmat2));
    if (cdsA->tmpmat3a != NULL)
        MatDestroy(&(cdsA->tmpmat3a));
    if (cdsA->tmpmat3b != NULL)
        MatDestroy(&(cdsA->tmpmat3b));
    if (cdsA->tmpmat3c != NULL)
        MatDestroy(&(cdsA->tmpmat3c));
    if (cdsA->tmpmat3d != NULL)
        MatDestroy(&(cdsA->tmpmat3d));
    if (cdsA->MVCovMat != NULL)
        MatDestroy(&(cdsA->MVCovMat));
    if (cdsA->SCovMat != NULL)
        MatDestroy(&(cdsA->SCovMat));
    if (cdsA->tmpmatK3a != NULL)
        MatDestroy(&(cdsA->tmpmatK3a));
    if (cdsA->tmpmatK3b != NULL)
        MatDestroy(&(cdsA->tmpmatK3b));
    if (cdsA->tmpmat3K != NULL)
        MatDestroy(&(cdsA->tmpmat3K));
}


void
PCAAlloc(CdsArray *cdsA)
{
    cdsA->pcamat = MatAlloc(cdsA->vlen, cdsA->vlen);
    cdsA->pcavals = malloc(cdsA->vlen * sizeof(double));
}


void
PCADestroy(CdsArray *cdsA)
{
    if (cdsA->pcamat != NULL)
        MatDestroy(&(cdsA->pcamat));
    if (cdsA->pcavals != NULL)
    {
        free(cdsA->pcavals);
        cdsA->pcavals = NULL;
    }
}


Cds
*CdsInit(void)
{
    int             i, j;
    Cds         *cds = NULL;

    cds = (Cds *) malloc(sizeof(Cds));

    strncpy(cds->filename, "", 1);

    cds->resName    = NULL;
    cds->resName_space = NULL;
    cds->chainID    = NULL;
    cds->resSeq     = NULL;
    cds->x          = NULL;
    cds->y          = NULL;
    cds->z          = NULL;
    cds->o          = NULL;
    cds->b          = NULL;
    cds->residual_x = NULL;
    cds->residual_y = NULL;
    cds->residual_z = NULL;
    cds->covx       = NULL;
    cds->covy       = NULL;
    cds->covz       = NULL;

    cds->innerprod = NULL;
    cds->vlen = 0;
    cds->innerprod2 = NULL;

    cds->matrix = MatAlloc(3, 3);
    cds->last_matrix = MatAlloc(3, 3);
    cds->evecs = MatAlloc(4,4);

    for (i = 0; i < 4; ++i)
        cds->evecs[i][0] = 1.0;

    for (i = 0; i < 3; ++i)
        for (j = 0; j < 3; ++j)
            cds->matrix[i][j] = cds->last_matrix[i][j] = 0.0;

    cds->tmpmat3a = MatAlloc(3, 3);
    cds->tmpmat3b = MatAlloc(3, 3);
    cds->tmpmat3c = MatAlloc(3, 3);
    cds->tmpmat3d = MatAlloc(3, 3);

    cds->bfact_c = 1.0;
    cds->scale = 1.0;

    return(cds);
}


void
CdsAlloc(Cds *cds, const int vlen)
{
    int             i;

    cds->vlen = vlen;

    cds->resName    = (char **)  calloc(vlen, sizeof(char *));
    cds->resName_space = (char *)  calloc(4 * vlen, sizeof(char));
    cds->chainID    = (char *)   calloc(vlen, sizeof(char));
    cds->resSeq     = (int *)    calloc(vlen, sizeof(int));
    cds->x          = (double *) calloc(vlen, sizeof(double));
    cds->y          = (double *) calloc(vlen, sizeof(double));
    cds->z          = (double *) calloc(vlen, sizeof(double));
    cds->o          = (double *) calloc(vlen, sizeof(double));
    cds->b          = (double *) calloc(vlen, sizeof(double));
    cds->prvar      = (double *) calloc(vlen, sizeof(double));
    cds->residual_x = (double *) calloc(vlen, sizeof(double));
    cds->residual_y = (double *) calloc(vlen, sizeof(double));
    cds->residual_z = (double *) calloc(vlen, sizeof(double));
    cds->covx       = (double *) calloc(vlen, sizeof(double));
    cds->covy       = (double *) calloc(vlen, sizeof(double));
    cds->covz       = (double *) calloc(vlen, sizeof(double));

    if (   cds->resName       == NULL
        || cds->resName_space == NULL
        || cds->chainID       == NULL
        || cds->resSeq        == NULL
        || cds->x             == NULL
        || cds->y             == NULL
        || cds->z             == NULL
        || cds->o             == NULL
        || cds->b             == NULL
        || cds->prvar         == NULL
        || cds->residual_x    == NULL
        || cds->residual_y    == NULL
        || cds->residual_z    == NULL
        || cds->covx          == NULL
        || cds->covy          == NULL
        || cds->covz          == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR5: could not allocate memory in function CdsAlloc(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < vlen; ++i)
        cds->resName[i] = cds->resName_space + (i * 4); /* 3 */

    CdsSetup(cds);
}


void
CdsSetup(Cds *cds)
{
    int             i;

    memsetd(cds->translation, 0.0, 3);
    memsetd(cds->jktranslation, 0.0, 3);
    memsetd(cds->transsum, 0.0, 3);

    for (i = 0; i < 4; ++i)
        cds->evecs[i][0] = 1.0;

    for (i = 0; i < 3; ++i)
        cds->matrix[i][i] = cds->last_matrix[i][i] = 1.0;
}


void
CdsDestroy(Cds **cds_ptr)
{
    Cds *cds = *cds_ptr;

    free(cds->resSeq);
    free(cds->resName_space);
    free(cds->chainID);
    free(cds->resName);
    free(cds->x);
    free(cds->y);
    free(cds->z);
    free(cds->o);
    free(cds->b);
    free(cds->prvar);
    free(cds->residual_x);
    free(cds->residual_y);
    free(cds->residual_z);
    free(cds->covx);
    free(cds->covy);
    free(cds->covz);
    MatDestroy(&(cds->matrix));
    MatDestroy(&(cds->last_matrix));

    if (cds->innerprod != NULL)
        MatDestroy(&(cds->innerprod));

    if (cds->innerprod2 != NULL)
        MatDestroy(&(cds->innerprod2));

    MatDestroy(&(cds->evecs));

    MatDestroy(&(cds->tmpmat3a));
    MatDestroy(&(cds->tmpmat3b));
    MatDestroy(&(cds->tmpmat3c));
    MatDestroy(&(cds->tmpmat3d));

    free(cds);
    *cds_ptr = NULL;
}


PDBCdsArray
*PDBCdsArrayInit(void)
{
    PDBCdsArray *pdbA = NULL; 

    pdbA = (PDBCdsArray *) malloc(sizeof(PDBCdsArray));
    if (pdbA == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR6: could not allocate memory in function PDBCdsArrayAlloc(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    pdbA->cds = NULL;
    pdbA->avecds = NULL;
    pdbA->upper = NULL;
    pdbA->lower = NULL;
    pdbA->scratchA = NULL;
    pdbA->cdsA = NULL;
    pdbA->seq2pdb = NULL;

    return(pdbA);
}


void
PDBCdsArrayAlloc(PDBCdsArray *pdbA, const int cnum, const int vlen)
{
    PDBCdsArrayAllocNum(pdbA, cnum);
    PDBCdsArrayAllocLen(pdbA, vlen);
}


void
PDBCdsArrayAllocNum(PDBCdsArray *pdbA, const int cnum)
{
    int             i;

    pdbA->cds = (PDBCds **) malloc(cnum * sizeof(PDBCds *));
    pdbA->cnum = cnum;

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

    pdbA->avecds = PDBCdsInit();
}


void
PDBCdsArrayAllocLen(PDBCdsArray *pdbA, const int vlen)
{
    int             i;

    if (pdbA == NULL
        || pdbA->cds == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr,
                "\n  ERROR6: could not allocate memory in function PDBCdsArrayAlloc(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    pdbA->vlen = vlen;

    for (i = 0; i < pdbA->cnum; ++i)
    {
        if (pdbA->cds[i]->vlen == 0) /* only allocate if it needs it */
            PDBCdsAlloc(pdbA->cds[i], vlen);
    }

    PDBCdsAlloc(pdbA->avecds, vlen);
}


void
PDBCdsArrayDestroy(PDBCdsArray **pdbA_ptr)
{
    PDBCdsArray *pdbA = *pdbA_ptr;
    int             i;

    for (i = 0; i < pdbA->cnum; ++i)
        PDBCdsDestroy(&(pdbA->cds[i]));

    PDBCdsDestroy(&(pdbA->avecds));

    if (pdbA->upper != NULL)
        free(pdbA->upper);

    if (pdbA->lower != NULL)
        free(pdbA->lower);

    if (pdbA->seq2pdb != NULL)
        Seq2pdbDestroy(&(pdbA->seq2pdb));

    free(pdbA->cds);
    free(pdbA);

    *pdbA_ptr = NULL;
}


PDBCds
*PDBCdsInit(void)
{
    PDBCds      *pdbcds = NULL;

    pdbcds = (PDBCds *) malloc(sizeof(PDBCds));
    pdbcds->vlen = 0; /* so that we know later if this has been allocated or not */
    pdbcds->translation = calloc(3, sizeof(double));
    pdbcds->matrix = MatAlloc(3, 3);
    memset(&pdbcds->matrix[0][0], 0, 9 * sizeof(double));

    return(pdbcds);
}


void
PDBCdsAlloc(PDBCds *pdbcds, const int vlen)
{
    int             i;

    pdbcds->vlen = vlen;

    /* allocate memory for the pointers to the fields */
    pdbcds->record     = (char **)  calloc(vlen, sizeof(char *));
    pdbcds->serial     = (unsigned int *) calloc(vlen, sizeof(unsigned int));
    pdbcds->Hnum       = (char *)   calloc(vlen, sizeof(char));
    pdbcds->name       = (char **)  calloc(vlen, sizeof(char *));
    pdbcds->altLoc     = (char *)   calloc(vlen, sizeof(char));
    pdbcds->resName    = (char **)  calloc(vlen, sizeof(char *));
    pdbcds->xchainID   = (char *)   calloc(vlen, sizeof(char));
    pdbcds->chainID    = (char *)   calloc(vlen, sizeof(char));
    pdbcds->resSeq     = (int *)    calloc(vlen, sizeof(int));
    pdbcds->iCode      = (char *)   calloc(vlen, sizeof(char));
    pdbcds->x          = (double *) calloc(vlen, sizeof(double));
    pdbcds->y          = (double *) calloc(vlen, sizeof(double));
    pdbcds->z          = (double *) calloc(vlen, sizeof(double));
    pdbcds->occupancy  = (double *) calloc(vlen, sizeof(double));
    pdbcds->tempFactor = (double *) calloc(vlen, sizeof(double));
    pdbcds->segID      = (char **)  calloc(vlen, sizeof(char *));
    pdbcds->element    = (char **)  calloc(vlen, sizeof(char *));
    pdbcds->charge     = (char **)  calloc(vlen, sizeof(char *));

    /* allocate space for the fields in total */
    pdbcds->record_space  = (char *)  calloc(8 * vlen, sizeof(char));
    pdbcds->name_space    = (char *)  calloc(4 * vlen, sizeof(char));
    pdbcds->resName_space = (char *)  calloc(4 * vlen, sizeof(char));
    pdbcds->segID_space   = (char *)  calloc(8 * vlen, sizeof(char));
    pdbcds->element_space = (char *)  calloc(4 * vlen, sizeof(char));
    pdbcds->charge_space  = (char *)  calloc(4 * vlen, sizeof(char));

    if (   pdbcds->record        == NULL
        || pdbcds->serial        == NULL
        || pdbcds->Hnum          == NULL
        || pdbcds->name          == NULL
        || pdbcds->altLoc        == NULL
        || pdbcds->resName       == NULL
        || pdbcds->xchainID      == NULL
        || pdbcds->chainID       == NULL
        || pdbcds->resSeq        == NULL
        || pdbcds->iCode         == NULL
        || pdbcds->x             == NULL
        || pdbcds->y             == NULL
        || pdbcds->z             == NULL
        || pdbcds->occupancy     == NULL
        || pdbcds->tempFactor    == NULL
        || pdbcds->segID         == NULL
        || pdbcds->element       == NULL
        || pdbcds->charge        == NULL
        || pdbcds->record_space  == NULL
        || pdbcds->name_space    == NULL
        || pdbcds->resName_space == NULL
        || pdbcds->segID_space   == NULL
        || pdbcds->element_space == NULL
        || pdbcds->charge_space  == NULL)
    {
        perror("\n  ERROR");
        fprintf(stderr, "\n  ERROR7: could not allocate memory in function PDBCdsAlloc(). \n");
        PrintTheseusTag();
        exit(EXIT_FAILURE);
    }

    /*  now 'point' the pointers */
    for (i = 0; i < pdbcds->vlen; ++i)
    {
        pdbcds->record[i]  = pdbcds->record_space  + (i * 8); /* 6 */
        pdbcds->name[i]    = pdbcds->name_space    + (i * 4); /* 3 */
        pdbcds->resName[i] = pdbcds->resName_space + (i * 4); /* 3 */
        pdbcds->segID[i]   = pdbcds->segID_space   + (i * 8); /* 4 */
        pdbcds->element[i] = pdbcds->element_space + (i * 4); /* 2 */
        pdbcds->charge[i]  = pdbcds->charge_space  + (i * 4); /* 2 */
    }
}


void
PDBCdsDestroy(PDBCds **pdbcds_ptr)
{
    PDBCds *pdbcds = *pdbcds_ptr;

    free(pdbcds->record_space);
    free(pdbcds->name_space);
    free(pdbcds->resName_space);
    free(pdbcds->segID_space);
    free(pdbcds->element_space);
    free(pdbcds->charge_space);

    MatDestroy(&(pdbcds->matrix));
    free(pdbcds->translation);

    free(pdbcds->record);
    free(pdbcds->serial);
    free(pdbcds->Hnum);
    free(pdbcds->name);
    free(pdbcds->altLoc);
    free(pdbcds->resName);
    free(pdbcds->xchainID);
    free(pdbcds->chainID);
    free(pdbcds->resSeq);
    free(pdbcds->iCode);
    free(pdbcds->x);
    free(pdbcds->y);
    free(pdbcds->z);
    free(pdbcds->occupancy);
    free(pdbcds->tempFactor);
    free(pdbcds->segID);
    free(pdbcds->element);
    free(pdbcds->charge);

    free(pdbcds);
    *pdbcds_ptr = NULL;
}
