/*
    Theseus - maximum likelihood superpositioning of macromolecular structures

    Copyright (C) 2004-2015 Douglas L. Theobald

    This file is part of THESEUS.

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

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

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

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

#include <stdlib.h>
#include <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 = calloc(1, sizeof(Seq2PDB));

    seq2pdb->pdbfile_name = NULL;
    seq2pdb->seqname = NULL;
    seq2pdb->map = NULL;
    seq2pdb->msa = 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 **) calloc(seqnum, sizeof(char *));
    seq2pdb->seqname = (char **) calloc(seqnum, sizeof(char *));
    seq2pdb->map = (int *) calloc(seqnum, sizeof(int));

    /* allocate space for the fields in total */
    seq2pdb->pdbfile_name_space = (char *) calloc(FILENAME_MAX * seqnum, sizeof(char));
    seq2pdb->seqname_space = (char *) calloc(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)
{
    if (seq2pdb_ptr)
    {
        Seq2PDB *seq2pdb = *seq2pdb_ptr;

        if (seq2pdb)
        {
            MyFree(seq2pdb->pdbfile_name_space);
            MyFree(seq2pdb->seqname_space);

            MyFree(seq2pdb->pdbfile_name);
            MyFree(seq2pdb->seqname);
            MyFree(seq2pdb->map);
            MyFree(seq2pdb->singletons);

            MSAdestroy(&(seq2pdb->msa));

            MyFree(seq2pdb);
            seq2pdb = NULL;
        }
    }
}


/* frees all but msa and seq2pdb */
void
Seq2pdbDealloc(Seq2PDB **seq2pdb_ptr)
{
    if (seq2pdb_ptr)
    {
        Seq2PDB *seq2pdb = *seq2pdb_ptr;

        if (seq2pdb)
        {
            MyFree(seq2pdb->pdbfile_name_space);
            MyFree(seq2pdb->seqname_space);

            MyFree(seq2pdb->pdbfile_name);
            MyFree(seq2pdb->seqname);
            MyFree(seq2pdb->map);
            MyFree(seq2pdb->singletons);
        }
    }
}


void
AlgorithmDestroy(Algorithm **algo_ptr)
{
    if (algo_ptr)
    {
        Algorithm *algo = *algo_ptr;

        if (algo)
        {
            int         i;

            if (algo->argv)
            {
                for (i = 0; i < algo->argc; ++i)
                    MyFree(algo->argv[i]);

                MyFree(algo->argv);
            }

            MyFree(algo->selection);
            algo->selection = NULL;
            MyFree(algo->atomslxn);
            algo->atomslxn = NULL;
            MyFree(algo);
            algo = NULL;
        }
    }
}


Algorithm
*AlgorithmInit(void)
{
    Algorithm      *algo = (Algorithm *) calloc (1, 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->verbose      = 0; /* verbose behavior = 1 */
    algo->write_file   = 1;
    algo->precision    = 1e-7;
    algo->iterations   = 10000;
    algo->selection    = NULL;
    algo->atomslxn     = NULL;
    algo->revsel       = 0;
    algo->atoms        = 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->info         = 0;
    algo->princaxes    = 1;
    algo->nullrun      = 0;
    algo->binary       = 0;
    algo->mbias        = 0;
    algo->doave        = 1;
    algo->domp         = 1;
    algo->dotrans      = 1;
    algo->dorot        = 1;
    algo->dohierarch   = 1;
    algo->docovars     = 1;
    algo->alignment    = 0;
    algo->fmodel       = 0;
    algo->covweight    = 0;
    algo->varweight    = 1;
    algo->hierarch     = 6;
    algo->leastsquares = 0;
    algo->filenum      = 0;
    algo->infiles      = NULL;
    algo->noinnerloop  = 1;
    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->bayes        = 0;
    algo->ipmat        = 0;
    algo->missing      = 0;
    algo->scale        = 0;
    algo->instfile     = 0;
    algo->pu           = 0;
    algo->FragDist     = 0;
    algo->amber        = 0;
    algo->atom_names   = 0;
    algo->scalefactor  = 1.0;
    algo->morphfile    = 0;
    algo->scaleanchor  = 0;
    algo->randgibbs    = 0;
    algo->covnu        = 1;
    algo->crush        = 0;
    algo->mg_crush     = 0;
    algo->parallel     = 0;
    algo->ndf          = 0; // default set in MultiPose, 3 for -v, vlen for -c

    return(algo);
}


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

    stats = (Statistics *) calloc(1, 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 *) calloc(1, 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->ac = NULL;
    cdsA->tcds = NULL;
    cdsA->tc = NULL;

    cdsA->w = NULL;
    cdsA->var = NULL;
    cdsA->invvar = NULL;
    cdsA->evals = NULL;
    cdsA->samplevar = NULL;
    cdsA->df = NULL;
    cdsA->S2 = NULL;

    cdsA->residuals = NULL;

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

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

    cdsA->tmpmatKK1 = NULL;
    cdsA->tmpmatKK2 = NULL;
    cdsA->tmpvecK = NULL;

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

    cdsA->tmpvec3a = calloc(3, sizeof(double));

    return(cdsA);
}


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

    cdsA->cds = (Cds **) calloc(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->ac = MatAlloc(3, vlen);
    cdsA->tc = MatAlloc(3, vlen);

    cdsA->var = (double *) calloc(vlen, sizeof(double));
    cdsA->invvar = (double *) calloc(vlen, sizeof(double));
    cdsA->evals = (double *) calloc(vlen, sizeof(double));
    cdsA->samplevar = (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->evals, 1.0, cdsA->vlen);
    memsetd(cdsA->samplevar, 1.0, cdsA->vlen);
    memsetd(cdsA->w, 1.0, cdsA->vlen);
}


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

    if (cdsA_ptr)
    {
        CdsArray *cdsA = *cdsA_ptr;

        if (cdsA)
        {
            MyFree(cdsA->msafile_name);
            cdsA->msafile_name = NULL;
            MyFree(cdsA->mapfile_name);
            cdsA->mapfile_name = NULL;

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

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

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

            MatDestroy(&(cdsA->ac));
            MatDestroy(&(cdsA->tc));

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

            MyFree(cdsA->tmpvec3a);

            MyFree(cdsA->residuals);
            cdsA->residuals = NULL;
            MyFree(cdsA->w);
            cdsA->w = NULL;
            MyFree(cdsA->var);
            cdsA->var = NULL;
            MyFree(cdsA->invvar);
            cdsA->var = NULL;
            MyFree(cdsA->evals);
            cdsA->evals = NULL;
            MyFree(cdsA->samplevar);
            cdsA->samplevar = NULL;
            MyFree(cdsA->df);
            cdsA->df = NULL;
            MyFree(cdsA->S2);
            cdsA->S2 = NULL;

            MyFree(cdsA);
            cdsA = 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) */
/*         MatDestroy(&(cdsA->Var_matrix)); */
    if (cdsA->Dij_matrix)
        MatDestroy(&(cdsA->Dij_matrix));
/*     if (cdsA->distmat) */
/*         Mat3DDestroy(&(cdsA->distmat)); */
}


void
CovMatsDestroy(CdsArray *cdsA)
{
    MatDestroy(&(cdsA->CovMat));
    MatDestroy(&(cdsA->FullCovMat));
    MatDestroy(&(cdsA->WtMat));
    MatDestroy(&(cdsA->tmpmatKK1));
    MatDestroy(&(cdsA->tmpmatKK2));
    MyFree(cdsA->tmpvecK);
    MatDestroy(&(cdsA->tmpmat3a));
    MatDestroy(&(cdsA->tmpmat3b));
    MatDestroy(&(cdsA->tmpmat3c));
    MatDestroy(&(cdsA->tmpmat3d));
}


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


void
PCADestroy(CdsArray *cdsA)
{
    MatDestroy(&(cdsA->pcamat));
    MyFree(cdsA->pcavals);
}


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

    cds = (Cds *) calloc(1, sizeof(Cds));

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

    cds->resName    = NULL;
    cds->resName_space = NULL;
    cds->chainID    = NULL;
    cds->resSeq     = NULL;
    cds->wc         = NULL;
    cds->x          = NULL;
    cds->y          = NULL;
    cds->z          = NULL;
    cds->o          = NULL;
    cds->b          = NULL;
    cds->nu         = NULL;
    cds->sc         = NULL;
    cds->sx         = NULL;
    cds->sy         = NULL;
    cds->sz         = NULL;
    cds->so         = NULL;
    cds->sb         = NULL;
    cds->cc         = NULL;
    cds->covx       = NULL;
    cds->covy       = NULL;
    cds->covz       = NULL;
    cds->prvar      = NULL;

    cds->outerprod = NULL;

    cds->vlen = 0;
    cds->innerprod = MatAlloc(3, 3);
    cds->matrix = MatAlloc(3, 3);
    cds->last_matrix = MatAlloc(3, 3);
    cds->last_outer_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)
    {
        cds->matrix[i][i] = cds->last_matrix[i][i] = cds->last_outer_matrix[i][i] = 1.0;
        cds->center[i] = cds->last_center[i] = 0.0;
    }

    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->wc            = MatAlloc(5, vlen);
    cds->x             = cds->wc[0];
    cds->y             = cds->wc[1];
    cds->z             = cds->wc[2];
    cds->o             = cds->wc[3];
    cds->b             = cds->wc[4];
    cds->nu            = (int *) calloc(vlen, sizeof(int));
    cds->sc            = MatAlloc(5, vlen);
    cds->sx            = cds->sc[0];
    cds->sy            = cds->sc[1];
    cds->sz            = cds->sc[2];
    cds->so            = cds->sc[3];
    cds->sb            = cds->sc[4];
    cds->cc            = MatAlloc(3, vlen);// DLT could be moved to CovMatAlloc
    cds->covx          = cds->cc[0];
    cds->covy          = cds->cc[1];
    cds->covz          = cds->cc[2];
    cds->prvar         = (double *) calloc(vlen, sizeof(double));

    if (   cds->resName       == NULL
        || cds->resName_space == NULL
        || cds->chainID       == NULL
        || cds->resSeq        == NULL
        || cds->wc            == NULL
        || cds->sc            == NULL
        || cds->cc            == NULL
        || cds->prvar         == 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 */
        cds->resName[i][3] = '\0';
    }

    CdsSetup(cds);
}


void
CdsSetup(Cds *cds)
{
    int             i;

    memsetd(cds->translation, 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] = cds->last_outer_matrix[i][i] = 1.0;
}


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

        if (cds)
        {
            MyFree(cds->resSeq);
            MyFree(cds->resName_space);
            MyFree(cds->chainID);
            MyFree(cds->resName);
            MatDestroy(&(cds->wc));
            MatDestroy(&(cds->sc));
            MatDestroy(&(cds->cc));
            MyFree(cds->nu);
            MyFree(cds->prvar);
            MatDestroy(&(cds->matrix));
            MatDestroy(&(cds->last_matrix));
            MatDestroy(&(cds->last_outer_matrix));
            MatDestroy(&(cds->innerprod));
            MatDestroy(&(cds->outerprod));
            MatDestroy(&(cds->evecs));

            MyFree(cds);
            cds = NULL;
        }
    }
}


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

    pdbA = (PDBCdsArray *) calloc(1, 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 **) calloc(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)
{
    if (pdbA_ptr)
    {
        PDBCdsArray *pdbA = *pdbA_ptr;

        if (pdbA)
        {
            int             i;

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

            PDBCdsDestroy(&(pdbA->avecds));

            MyFree(pdbA->upper);
            MyFree(pdbA->lower);

            //Seq2pdbDestroy(&(pdbA->seq2pdb)); DLT FIX

            MyFree(pdbA->cds);
            MyFree(pdbA);

            pdbA = NULL;
        }
    }
}


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

    pdbcds = (PDBCds *) calloc(1, sizeof(PDBCds));
    pdbcds->vlen = 0; /* so that we know later if this has been allocated or not */
    pdbcds->scale = 1.0;
    pdbcds->translation = calloc(3, sizeof(double));
    pdbcds->matrix = MatAlloc(3, 3);

    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->c          = MatAlloc(5, vlen);

    pdbcds->x          = pdbcds->c[0];
    pdbcds->y          = pdbcds->c[1];
    pdbcds->z          = pdbcds->c[2];
    pdbcds->occupancy  = pdbcds->c[3];
    pdbcds->tempFactor = pdbcds->c[4];

    pdbcds->segID      = (char **)  calloc(vlen, sizeof(char *));
    pdbcds->element    = (char **)  calloc(vlen, sizeof(char *));
    pdbcds->charge     = (char **)  calloc(vlen, sizeof(char *));

    pdbcds->nu         = (int *)    calloc(vlen, sizeof(int));

    /* 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->c             == NULL
        || pdbcds->segID         == NULL
        || pdbcds->element       == NULL
        || pdbcds->charge        == NULL
        || pdbcds->nu            == 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 */

        pdbcds->record[i][6]  = '\0'; /* 6 */
        pdbcds->name[i][3]    = '\0'; /* 3 */
        pdbcds->resName[i][3] = '\0'; /* 3 */
        pdbcds->segID[i][4]   = '\0'; /* 4 */
        pdbcds->element[i][2] = '\0'; /* 2 */
        pdbcds->charge[i][2]  = '\0'; /* 2 */
    }
}


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

        if (pdbcds)
        {
            MyFree(pdbcds->record_space);
            MyFree(pdbcds->name_space);
            MyFree(pdbcds->resName_space);
            MyFree(pdbcds->segID_space);
            MyFree(pdbcds->element_space);
            MyFree(pdbcds->charge_space);

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

            MyFree(pdbcds->record);
            MyFree(pdbcds->serial);
            MyFree(pdbcds->Hnum);
            MyFree(pdbcds->name);
            MyFree(pdbcds->altLoc);
            MyFree(pdbcds->resName);
            MyFree(pdbcds->xchainID);
            MyFree(pdbcds->chainID);
            MyFree(pdbcds->resSeq);
            MyFree(pdbcds->iCode);
            MatDestroy(&(pdbcds->c));
            MyFree(pdbcds->segID);
            MyFree(pdbcds->element);
            MyFree(pdbcds->charge);

            MyFree(pdbcds->nu);

            MyFree(pdbcds);
            pdbcds = NULL;
        }
    }
}

