/***********************************************************************
*  -/_|:|_|_\-
*                                                                      *
*  dssplite v.0.8                                                      *
*                                                                      *
*  Copyright (C) 2003-2015  Douglas L. Theobald                        *
*                                                                      *
*  dssplite 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.                              *
*                                                                      *
*  dssplite 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 dssplite in the file 'COPYING'; if not, write to the:    *
*                                                                      *
*  Free Software Foundation, Inc.,                                     *
*  59 Temple Place, Suite 330,                                         *
*  Boston, MA  02111-1307  USA                                         *
*                                                                      *
************************************************************************
*                                                                      *
*  Douglas Theobald                                                    *
*  Biochemistry Department                                             *
*  Brandeis University                                                 *
*  MS 009                                                              *
*  415 South St                                                        *
*  Waltham, MA  02454-9110                                             *
*                                                                      *
*  dtheobald@brandeis.edu                                              *
*  dtheobald@gmail.com                                                 *
*                                                                      *
***********************************************************************/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "dssplite.h"

#define SQR(a) ((a)*(a))
#define HB_DISTMAX        5.20
#define HB_DISTMAX_SQR   27.04 /* 5.20 squared */
#define MY_PI             3.14159265358979323846264338327950288419716939937511   /* pi */
#define TWOPI             6.2831853072
#define PIHALF            1.5707963268
#define MAXHBONDS         4
#define MAXBRIDGE         4

static void
vDiff(Vector x, Vector y, Vector z);

static double
vDot(Vector x, Vector y);

static void
vCross(Vector x, Vector y, Vector z);

/* static double */
/* vNorm(Vector x); */
/*  */
/* static double */
/* vLength(Vector u); */

static double
Atan2(double y, double x);

static double
Cosangle(double **Ad, int An,
         double **Bd, int Bn,
         double **Cd, int Cn,
         double **Dd, int Dn);


static void
MyFree(void *p)
{
    if (p)
    {
        free(p);
        p = NULL;
    }
//     else
//     {
//         fprintf(stderr, "\nERROR623: trying to free NULL ptr -- %s:%d:%p\n\n",
//                 __FILE__, __LINE__, p);
//         fflush(NULL);
//         //exit(EXIT_FAILURE);
//     }
}


char
*dssplite_interleave(double *coords,
                     char **resName, int *resSeq, char **name,
                     int len)
{
    int             i;
    double         *x = NULL, *y = NULL, *z = NULL;
    char           *summary = NULL;

    x = (double *) malloc(len * sizeof(double));
    y = (double *) malloc(len * sizeof(double));
    z = (double *) malloc(len * sizeof(double));
    if (x == NULL || y == NULL || z == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to mallocate room for x, y, z temporary coords in dspplite_interleave() ");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < len; i+= 3)
    {
        x[i] = coords[i+0];
        y[i] = coords[i+1];
        z[i] = coords[i+2];
    }

    summary = dssplite(x, y, z, resName, resSeq, name, len);

    free(x);
    free(y);
    free(z);

    return(summary);
}


char
*dssplite_mat(double **coords,
              char **resName, int *resSeq, char **name,
              int len)
{
    int             i;
    double         *x = NULL, *y = NULL, *z = NULL;
    char           *summary = NULL;

    x = (double *) malloc(len * sizeof(double));
    y = (double *) malloc(len * sizeof(double));
    z = (double *) malloc(len * sizeof(double));
    if (x == NULL || y == NULL || z == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to mallocate room for x, y, z temporary coords in dspplite_interleave() ");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < len; ++i)
    {
        x[i] = coords[i][0];
        y[i] = coords[i][1];
        z[i] = coords[i][2];
    }

    summary = dssplite(x, y, z, resName, resSeq, name, len);

    free(x);
    free(y);
    free(z);

    return(summary);
}


static int
GetResidueLen(char **name, int vlen)
{
    int             i, len;

    len = 0;
    for (i = 0; i < vlen; ++i)
    {
        //printf("\'%s\'\n", name[i]);
        if (strstr(name[i], "CA "))
            ++len;
    }

    return(len);
}


char
*dssplite(double *x, double *y, double *z,
          char **resName, int *resSeq, char **name,
          int vlen) /* vlen = # of atoms, full x vector length, len = # CA atoms */
{
    DSSP           *dssp = NULL;
    int             i, j;
    int             len;
    char           *summary = NULL;

    /* Setup DSSP structure */
    dssp = DSSPinit();
    dssp->x = x;
    dssp->y = y;
    dssp->z = z;
    dssp->resName = resName;
    dssp->resSeq = resSeq;
    dssp->name = name;

    len = GetResidueLen(name, vlen);
    DSSPalloc(dssp, len);

    for(i = 0, j = 0; i < vlen; ++i)
    {
        if (strncmp(name[i], "CA ", 3) == 0)
        {
            strncpy(dssp->residue[j], resName[i], 4);
            ++j;
        }
    }

    /* do the DSSP algorithm, Doug-style */
    GetCONHCA(dssp);
    FlagHBonds(dssp);

    FlagBends(dssp);
    FlagTurns(dssp);
    Find5Helices(dssp);
    Find3Helices(dssp);
    FlagBridges(dssp);
    FindStrands(dssp);
    Find4Helices(dssp);

/* PrintCONHCA(dssp); */
//PrintSummary(dssp);

    for (i = 0; i < len; ++i)
        putchar(dssp->summary[i]);
        /* printf("\n DSSP Summary [%3d] = %2c ", i, dssp->summary[i]); */
    putchar('\n');
    putchar('\n');

    summary = (char *) malloc(len * sizeof(char));
    strncpy(summary, dssp->summary, len);

    DSSPfree(&dssp);

    return(summary);
}


/* Finds H-bonds, flags them, records partners, returns total # of H-bonds */
int
FlagHBonds(DSSP *dssp)
{
    int             i, j, k;
    int             len = dssp->len;
    int             HBnum = 0;

    for (i = 0; i < len; ++i)
    {
        for (j = 0; j < len; ++j)
        {
            if (i == j || i == j-1 || i == j+1 || strncmp(dssp->residue[j], "PRO", 3) == 0)
               continue;

            if (HbondEnergy(dssp, i, j) == 1)
            {
                /* record to whom the H-bond is made, CO -> NH direction only */
                for (k = 0; k < MAXHBONDS; ++k)
                {
                    if (dssp->bondatm[i][k] == j)
                    {
                        break;
                    }
                    else if (dssp->bondatm[i][k] == 0)
                    {
                        dssp->bondatm[i][k] = j;
                        ++(dssp->Hbond[i]); /* set Hbond flags if H-bond is found */
                        ++HBnum;
                        break;
                    }
                }
            }
        }
    }

    return(HBnum);
}


int
TestHBond(DSSP *dssp, int hb1, int hb2)
{
    int             i;

    if (dssp->Hbond[hb1] != 0)
    {
        for (i = 0; i < MAXHBONDS && i < dssp->Hbond[hb1]; ++i)
        {
            if (dssp->bondatm[hb1][i] == hb2 && dssp->bondatm[hb1][i] != 0)
                return (1);
        }
    }

    return (0);
}


void
FlagBends(DSSP *dssp)
{
    int             i;
    double          kappa, skap, ckap;
    int             len = dssp->len;

    for (i = 2; i < len-2; ++i)
    {
        ckap = Cosangle(dssp->CA, i, dssp->CA, i-2, dssp->CA, i+2, dssp->CA, i);
        skap = sqrt(1.0 - SQR(ckap));
        kappa = Atan2(skap, ckap);
        /* printf("\n ckap skap bend angle [%3d] = %8.3f %8.3f %8.3f ", i+1, ckap, skap, kappa); */

        if (kappa > 1.222)
        {
            dssp->bend[i] = 1;
            dssp->summary[i] = 'S';
        }
    }
}


void
FlagTurns(DSSP *dssp)
{
    int             i;
    int             len = dssp->len;

    for (i = 0; i < len-3; ++i)
    {
        if (TestHBond(dssp, i, i+3) == 1)
        {
            dssp->turn3[i] = 1;
            dssp->summary[i+1] = dssp->summary[i+2] ='T'; /* only residues bracketed by the turn get labelled */
        }
    }

    for (i = 0; i < len-4; ++i)
    {
        if (TestHBond(dssp, i, i+4) == 1)
        {
            dssp->turn4[i] = 1;
            dssp->summary[i+1] = dssp->summary[i+2] = dssp->summary[i+3] = 'T';
        }
    }

    for (i = 0; i < len-5; ++i)
    {
        if (TestHBond(dssp, i, i+5) == 1)
        {
            dssp->turn5[i] = 1;
            dssp->summary[i+1] = dssp->summary[i+2] = dssp->summary[i+3] = dssp->summary[i+4] = 'T';
        }
    }
}


void
Find5Helices(DSSP *dssp)
{
    int             i, j;
    int             len = dssp->len;

    for (i = 1; i < len; ++i)
    {
        if (dssp->turn5[i-1] && dssp->turn5[i])
        {
            for (j = 0; j < 5; ++j)
                dssp->summary[i+j] = 'I';
        }
    }
}


void
Find3Helices(DSSP *dssp)
{
    int             i, j;
    int             len = dssp->len;

    for (i = 1; i < len; ++i)
    {
        if (dssp->turn3[i-1] && dssp->turn3[i])
        {
            for (j = 0; j < 3; ++j)
                dssp->summary[i+j] = 'G';
        }
    }
}


void
FlagBridges(DSSP *dssp)
{
    int             i, j;
    int             len = dssp->len;

    for (i = 1; i < len-1; ++i)
    {
        for (j = 1; j < len-1; ++j)
        {
            if (i == j-1 || i == j || i == j+1/* || dssp->summary[i] == 'T'*//* DLT */)
                continue;

            if (   (TestHBond(dssp, i-1, j) && TestHBond(dssp, j, i+1))
                || (TestHBond(dssp, j-1, i) && TestHBond(dssp, i, j+1)))
            {
                dssp->bridgeP[i][dssp->bridgePn[i]] = j;
                ++dssp->bridgePn[i];
                dssp->summary[i] = 'B';

            }

            if (   (TestHBond(dssp, i, j)     && TestHBond(dssp, j, i))
                || (TestHBond(dssp, i-1, j+1) && TestHBond(dssp, j-1, i+1)))
            {
                dssp->bridgeA[i][dssp->bridgeAn[i]] = j;
                ++dssp->bridgeAn[i];
                dssp->summary[i] = 'B';
            }
        }
    }
}


void
FindStrands(DSSP *dssp)
{
    int             i, j, k, m;
    int             len = dssp->len;
    char           *ss = dssp->summary;

    for (i = 0; i < len-1; ++i)
    {
        /* for parallel bridges */
        /* connect adjacent bridges on strand 1*/
        if (eqbridgeP(dssp, i, i+1))
        {
            for (j = 0; j < dssp->bridgePn[i]; ++j) /* for each bridge 'j' made by residue 'i' */
            {
                for (k = 0; k < dssp->bridgePn[i+1]; ++k) /* for each bridge 'k' made by residue 'i+1' */
                {
                    if (abs(dssp->bridgeP[i+1][j] - dssp->bridgeP[i][k]) < 6.0) /* connect gaps of 4 or less on strand 2 */
                    {
                        for (m = dssp->bridgeP[i][k]; m <= dssp->bridgeP[i+1][j]; ++m)
                            ss[m] = 'E';
                        ss[i] = ss[i+1] = 'E';
                    }
                }
            }
        }

        /* connect bridges on strand 1 separated by a single-residue gap */
        if (eqbridgeP(dssp, i, i+2)) /* we avoid array overflow here because I've over allocated */
        {
            for (j = 0; j < dssp->bridgePn[i]; ++j) /* for each H-bond 'j' made by bridge residue 'i' */
            {
                for (k = 0; k < dssp->bridgePn[i+2]; ++k) /* for each H-bond 'k' of bridge residue 'i+1' */
                {
                    if (abs(dssp->bridgeP[i+2][j] - dssp->bridgeP[i][k]) < 6.0) /* connect gaps of 4 or less on strand 2 */
                    {
                        for (m = dssp->bridgeP[i][k]; m <= dssp->bridgeP[i+2][j]; ++m)
                            ss[m] = 'E';
                        ss[i] = ss[i+1] = ss[i+2] = 'E';
                    }
                }
            }
        }

        /* for anti-parallel bridges */
        /* connect adjacent bridges on strand 1 */
        if (eqbridgeA(dssp, i, i+1))
        {
            for (j = 0; j < dssp->bridgeAn[i]; ++j) /* for each H-bond 'j' made by bridge residue 'i' */
            {
                for (k = 0; k < dssp->bridgeAn[i+1]; ++k) /* for each H-bond 'k' of bridge residue 'i+1' */
                {
                    if (abs(dssp->bridgeA[i][j] - dssp->bridgeA[i+1][k]) < 6.0) /* connect gaps of 4 or less on strand 2 */
                    {
                        for (m = dssp->bridgeA[i+1][k]; m <= dssp->bridgeA[i][j]; ++m)
                            ss[m] = 'E';
                        ss[i] = ss[i+1] = 'E';
                    }
                }
            }
        }

        /* connect bridges on strand 1 separated by a single-residue gap */
        if (eqbridgeA(dssp, i, i+2)) /* we avoid array overflow here because I've over allocated */
        {
            for (j = 0; j < dssp->bridgeAn[i]; ++j) /* for each bridge 'j' made by residue 'i' */
            {
                for (k = 0; k < dssp->bridgeAn[i+2]; ++k) /* for each bridge 'k' made by residue 'i+2' */
                {
                    if (abs(dssp->bridgeA[i][j] - dssp->bridgeA[i+2][k]) < 6.0) /* connect gaps of 4 or less on strand 2 */
                    {
                        for (m = dssp->bridgeA[i+2][k]; m <= dssp->bridgeA[i][j]; ++m)
                            ss[m] = 'E';
                        ss[i] = ss[i+1] = ss[i+2] = 'E';
                    }
                }
            }
        }
    }
}


int
isbridgeP(DSSP *dssp, int atom)
{
    if (dssp->bridgePn[atom])
        return(1);
    else
        return(0);
}


int
isbridgeA(DSSP *dssp, int atom)
{
    if (dssp->bridgeAn[atom])
        return(1);
    else
        return(0);
}


int
eqbridgeP(DSSP *dssp, int atom1, int atom2)
{
    if (dssp->bridgePn[atom1] && dssp->bridgePn[atom2])
        return(1);
    else
        return(0);
}


int
eqbridgeA(DSSP *dssp, int atom1, int atom2)
{
    if (dssp->bridgeAn[atom1] && dssp->bridgeAn[atom2])
        return(1);
    else
        return(0);
}


void
Find4Helices(DSSP *dssp)
{
    int             i, j;
    int             len = dssp->len;

    for (i = 1; i < len; ++i)
    {
        if (dssp->turn4[i-1] && dssp->turn4[i])
        {
            for (j = 0; j < 4; ++j)
                dssp->summary[i+j] = 'H';
        }
    }
}


void
FindDisulphides(DSSP *dssp)
{
    int             i;
    int             len = dssp->len;

    for (i = 0; i < len; ++i)
    {

    }
}


/* Selects a new subset of coordinates based on a key string and a string
   array to test the key against */
static void
CoordsKeySelxn(double **newc, int newclen, DSSP *oldc,
               const char **teststr, const char *key, int keylen)
{
    int             i, j;

    i = j = 0;
    while(j < newclen)
    {
        if (strncmp(teststr[i], key, keylen) == 0)
        {
            newc[0][j] = oldc->x[i];
            newc[1][j] = oldc->y[i];
            newc[2][j] = oldc->z[i];
            ++j;
        }
        ++i;
    }
}


void
GetCONHCA(DSSP *dssp)
{
    int             len = dssp->len;
    int             i, j;
    double          COdist;

    CoordsKeySelxn(dssp->C,  len, dssp, (const char **) dssp->name, "C  ", 3);
    CoordsKeySelxn(dssp->O,  len, dssp, (const char **) dssp->name, "O  ", 3);
    CoordsKeySelxn(dssp->N,  len, dssp, (const char **) dssp->name, "N  ", 3);
    CoordsKeySelxn(dssp->CA, len, dssp, (const char **) dssp->name, "CA ", 3);

    /* Build the NH hydrogen atom coords */
    /* This is Kabsch & Sanders' quick method (buried in the DsspCMBI.c source).
       It assumes trans, planar peptides and it makes the NH bond
       exactly 1.0 angstroms long (which is correct, BTW).
       It takes advantage of the fact that the OC (from the residue before)
       and the NH bond vectors have the same orientation and direction,
       given the above assumptions. Dividing by the OC distance normalizes
       the OC vector to 1.0 angstroms,
       and then we add that to the nitrogen to get the hydrogen coords.
       */
    for (i = 1; i < len; ++i)
    {
        COdist = Distance(dssp->O, i-1, dssp->C, i-1);
        for (j = 0; j < 3; ++j)
            dssp->H[j][i] = dssp->N[j][i] + (dssp->C[j][i-1] - dssp->O[j][i-1]) / COdist;
    }
}


/* returns 1 if H-bond is made, 0 otherwise */
int
HbondEnergy(DSSP *dssp, int co, int nh)
{
    double          energy;
    double          rON, rCH, rOH, rCN;
    double          cutoff = -0.01793; /* = -0.5 / (0.20 * 0.42 * 332) */

    rOH = Distance(dssp->O, co, dssp->H, nh);
    if (rOH > HB_DISTMAX)
        return(0);

    rCN = Distance(dssp->C, co, dssp->N, nh);
    if (rCN < 1.35)
        return(0);

    rON = Distance(dssp->O, co, dssp->N, nh);
    rCH = Distance(dssp->C, co, dssp->H, nh);

    energy = (1.0/rON) + (1.0/rCH) - (1.0/rOH) - (1.0/rCN);

/*     if (co > 84 && co < 121 && nh > 103 && nh < 124) */
/*     { */
/*         printf("\n [%3d %3d] --- distance = %8.3f ", co+1, nh+1, rON); */
/*         printf("\n ######### --- Cx %8.3f Ox %8.3f ", dssp->C[0][co], dssp->O[0][co]); */
/*         printf("\n ######### --- Nx %8.3f Hx %8.3f \n", dssp->N[0][nh], dssp->H[0][nh]); */
/*  */
/*         printf("\n rON rCH rOH rCN = %8.3f %8.3f %8.3f %8.3f ", rON, rCH, rOH, rCN); */
/*         printf("\n energy = %8.3f", energy); */
/*         putchar('\n'); */
/*  */
/*         fflush(NULL); */
/*     } */

    if (energy < cutoff)
        return(1);
    else
        return(0);
}


double
Distance(double **atom1, int id1, double **atom2, int id2)
{
    return(sqrt(DistanceSqr(atom1, id1, atom2, id2)));
}


double
DistanceSqr(double **atom1, int id1, double **atom2, int id2)
{
    double          dist;

    dist = SQR(atom2[0][id2] - atom1[0][id1])
         + SQR(atom2[1][id2] - atom1[1][id1])
         + SQR(atom2[2][id2] - atom1[2][id1]);

    return(dist);
}


/*////////////////////////////////////////////////////////////*/
/* Vector functions                                           */
/*////////////////////////////////////////////////////////////*/
static void
vDiff(Vector x, Vector y, Vector z)
{
    z[0] = x[0] - y[0];
    z[1] = x[1] - y[1];
    z[2] = x[2] - y[2];
}


static double
vDot(Vector x, Vector y)
{
    return (x[0]*y[0] + x[1]*y[1] + x[2]*y[2]);
}


static void
vCross(Vector x, Vector y, Vector z)
{
    z[0] = x[1]*y[2] - y[1]*x[2];
    z[1] = x[2]*y[0] - y[2]*x[0];
    z[2] = x[0]*y[1] - y[0]*x[1];
}


/* returns input vector x normalized to unit length.
   xnorm is the original length of x. */
/*
static double
vNorm(Vector x)
{
    double          temp0, temp1, temp2, xnorm;

    temp0 = x[0];
    temp1 = x[1];
    temp2 = x[2];

    xnorm = SQR(temp0) + SQR(temp1) + SQR(temp2);

    if (xnorm <= 0.0)
        return (0.0);

    xnorm = sqrt(xnorm);

    x[0] /= xnorm;
    x[1] /= xnorm;
    x[2] /= xnorm;

    return(xnorm);
}


static double
vLength(Vector u)
{
    return (sqrt(SQR(u[0]) + SQR(u[1]) + SQR(u[2])));
}
*/

/*////////////////////////////////////////////////////////////*/
/* Math utils                                                 */
/*////////////////////////////////////////////////////////////*/
static double
Atan2(double y, double x)
{
    double          z;

    if (x != 0.0)
        z = atan(y / x);
    else if (y > 0.0)
        z = PIHALF;
    else if (y < 0.0)
        z = -PIHALF;
    else
        z = TWOPI;

    if (x >= 0.0)
        return (z);
    if (y > 0.0)
        z += MY_PI;
    else
        z -= MY_PI;

    return (z);
}


static double
Cosangle(double **Ad, int An,
         double **Bd, int Bn,
         double **Cd, int Cn,
         double **Dd, int Dn)
{
    Vector          u, v, v1, v2, v3, v4;
    double          x;
    int             i;

    for (i = 0; i < 3; ++i)
        v1[i] = Ad[i][An];
    for (i = 0; i < 3; ++i)
        v2[i] = Bd[i][Bn];
    for (i = 0; i < 3; ++i)
        v3[i] = Cd[i][Cn];
    for (i = 0; i < 3; ++i)
        v4[i] = Dd[i][Dn];

    vDiff(v1, v2, u);
    vDiff(v3, v4, v);
    x = vDot(u, u) * vDot(v, v);
    if (x > 0.0)
        return (vDot(u, v) / sqrt(x));
    else
        return (0.0);
}


/* Calculates torsion angle of a set of 4 atoms a1-a2-a3-a4. Dihedralangle
   is the angle between the projection of a1-a2 and the projection of a4-a3
   onto a plane normal to bond a2-a3. */
double
Dihedralangle(double **Ad, int An,
              double **Bd, int Bn,
              double **Cd, int Cn,
              double **Dd, int Dn)
{
    int             i;
    double          angle, u, v;
    Vector          A, B, C, D, AB, DC, BC, x, y, p;

    for (i = 0; i < 3; ++i)
        A[i] = Ad[i][An];
    for (i = 0; i < 3; ++i)
        B[i] = Bd[i][Bn];
    for (i = 0; i < 3; ++i)
        C[i] = Cd[i][Cn];
    for (i = 0; i < 3; ++i)
        D[i] = Dd[i][Dn];

    vDiff(A, B, AB);
    vDiff(D, C, DC);
    vDiff(B, C, BC);
    vCross(BC, AB, p);
    vCross(BC, DC, x);
    vCross(BC, x, y);
    u = vDot(x, x);
    v = vDot(y, y);
    angle = TWOPI;;

    if (u <= 0.0 || v <= 0.0)
        return (angle);

    u = vDot(p, x) / sqrt(u);
    v = vDot(p, y) / sqrt(v);

    if (u != 0.0 || v != 0.0)
        /* return (Atan2(v, u) * RADIAN); */
        return (Atan2(v, u));
    else
        return (angle);
}


/*////////////////////////////////////////////////////////////*/
/* Memory allocation                                          */
/*////////////////////////////////////////////////////////////*/
DSSP
*DSSPinit(void)
{
    DSSP        *dssp = NULL;

    dssp = (DSSP *) calloc(1, sizeof(DSSP));

    dssp->C = dssp->O = dssp->N = dssp->H = NULL;
    dssp->Hbond = NULL;
    dssp->bondatm = NULL;
    dssp->bridgePn = dssp->bridgeAn = NULL;
    dssp->bridgeP = dssp->bridgeA = NULL;
    dssp->bend = NULL;
    dssp->turn3 = dssp->turn4 = dssp->turn5 = NULL;
    dssp->disulph = NULL;
    dssp->summary = NULL;

    return(dssp);
}


void
DSSPalloc(DSSP *dssp, int len)
{
    int             i;
    int            *bondatmspace = NULL, *b1space = NULL, *b2space = NULL;
    char           *residue_space = NULL;
    double         *Cspace = NULL, *Ospace = NULL, *Nspace = NULL, *Hspace = NULL,
                   *CAspace = NULL;

    dssp->len = len;

    Cspace = Ospace = Nspace = Hspace = CAspace = NULL;
    bondatmspace = b1space = b2space = NULL;

    if (len < 6)
    {
        puts("\n ERROR: Length of DSSP vector is less than 6 ");
        exit(EXIT_FAILURE);
    }

    /* allocate bondatm space */
    bondatmspace = (int *) calloc(MAXHBONDS * len, sizeof(int));
    if (bondatmspace == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to allocate room for DSSP \'bondatm\' pointers ");
        exit(EXIT_FAILURE);
    }

    dssp->bondatm = (int **) malloc(len * sizeof(int *));
    if (dssp->bondatm == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to allocate room for pointers to \'bondatm\' ");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < len; i++)
        dssp->bondatm[i] = bondatmspace + (MAXHBONDS*i);

    /* allocate CONHCA space */
    Cspace  = (double *) calloc(3 * len, sizeof(double));
    Ospace  = (double *) calloc(3 * len, sizeof(double));
    Nspace  = (double *) calloc(3 * len, sizeof(double));
    Hspace  = (double *) calloc(3 * len, sizeof(double));
    CAspace = (double *) calloc(3 * len, sizeof(double));
    if (Cspace == NULL || Ospace == NULL || Nspace == NULL || Hspace == NULL || CAspace == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to allocate room for DSSP \'CONH\' pointers ");
        exit(EXIT_FAILURE);
    }

    dssp->C  = (double **) malloc(3 * sizeof(double *));
    dssp->O  = (double **) malloc(3 * sizeof(double *));
    dssp->N  = (double **) malloc(3 * sizeof(double *));
    dssp->H  = (double **) malloc(3 * sizeof(double *));
    dssp->CA = (double **) malloc(3 * sizeof(double *));
    if (dssp->C == NULL || dssp->O == NULL || dssp->N == NULL || dssp->H == NULL || dssp->CA == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to allocate room for pointers to \'CONH\' ");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < 3; i++)
    {
        dssp->C[i]  = Cspace + (len*i);
        dssp->O[i]  = Ospace + (len*i);
        dssp->N[i]  = Nspace + (len*i);
        dssp->H[i]  = Hspace + (len*i);
        dssp->CA[i] = CAspace + (len*i);
    }

    /* allocate bridge space */
    b1space  = (int *) calloc(MAXBRIDGE * (len+1), sizeof(int));
    b2space  = (int *) calloc(MAXBRIDGE * (len+1), sizeof(int));
    if (b1space == NULL || b2space == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to allocate room for DSSP \'CONH\' pointers ");
        exit(EXIT_FAILURE);
    }

    dssp->bridgeP  = (int **) malloc((len+1) * sizeof(int *));
    dssp->bridgeA  = (int **) malloc((len+1) * sizeof(int *));
    if (dssp->bridgeP == NULL || dssp->bridgeA == NULL)
    {
        perror("\n ERROR");
        puts("\n ERROR: Failure to allocate room for pointers to \'CONH\' ");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < len+1; i++)
    {
        dssp->bridgeP[i]  = b1space + (MAXBRIDGE*i);
        dssp->bridgeA[i]  = b2space + (MAXBRIDGE*i);
    }

    /* allocate the residue space */
    dssp->residue =  (char **) malloc(len * sizeof(char *));
    residue_space =  (char *) malloc(4 * len * sizeof(char));

    for (i = 0; i < len; ++i)
        dssp->residue[i] = residue_space + (i*4); /* 3 */

    /* allocate the rest */
    dssp->Hbond =    (int *) calloc(len,   sizeof(int));
    dssp->bridgePn = (int *) calloc(len+1, sizeof(int));
    dssp->bridgeAn = (int *) calloc(len+1, sizeof(int));
    dssp->bend =     (int *) calloc(len,   sizeof(int));
    dssp->turn5 =    (int *) calloc(len+1, sizeof(int));
    dssp->turn4 =    (int *) calloc(len+1, sizeof(int));
    dssp->turn3 =    (int *) calloc(len+1, sizeof(int));
    dssp->cys =      (int *) calloc(len,   sizeof(int));
    dssp->disulph =  (int *) calloc(len,   sizeof(int));
    dssp->summary =  (char *) calloc(len, sizeof(char));

    for (i = 0; i < len; ++i)
        dssp->summary[i] = ' ';

    return;
}


void
DSSPfree(DSSP **dssp_ptr)
{
    if (dssp_ptr)
    {
        DSSP *dssp = *dssp_ptr;

        if (dssp)
        {
            MyFree(dssp->C[0]);
            MyFree(dssp->C);
            MyFree(dssp->O[0]);
            MyFree(dssp->O);
            MyFree(dssp->N[0]);
            MyFree(dssp->N);
            MyFree(dssp->H[0]);
            MyFree(dssp->H);
            MyFree(dssp->CA[0]);
            MyFree(dssp->CA);

            MyFree(dssp->bondatm[0]);
            MyFree(dssp->bondatm);

            MyFree(dssp->residue[0]);
            MyFree(dssp->residue);

            MyFree(dssp->Hbond);
            MyFree(dssp->bridgeP[0]);
            MyFree(dssp->bridgeP);
            MyFree(dssp->bridgeA[0]);
            MyFree(dssp->bridgeA);
            MyFree(dssp->bridgePn);
            MyFree(dssp->bridgeAn);
            MyFree(dssp->bend);
            MyFree(dssp->turn5);
            MyFree(dssp->turn4);
            MyFree(dssp->turn3);
            MyFree(dssp->cys);
            MyFree(dssp->disulph);
            MyFree(dssp->summary);
            MyFree(dssp);
            dssp = NULL;
        }
    }
}


void
PrintCONHCA(DSSP *dssp)
{
    int             i;
    int             len = dssp->len;

    for (i = 0; i < len; ++i)
        printf("  %3s C[%3d] = %8.3f%8.3f%8.3f \n", dssp->residue[i], i+1, dssp->C[0][i], dssp->C[1][i], dssp->C[2][i]);
    putchar('\n');
    for (i = 0; i < len; ++i)
        printf("  %3s O[%3d] = %8.3f%8.3f%8.3f \n", dssp->residue[i], i+1, dssp->O[0][i], dssp->O[1][i], dssp->O[2][i]);
    putchar('\n');
    for (i = 0; i < len; ++i)
        printf("  %3s N[%3d] = %8.3f%8.3f%8.3f \n", dssp->residue[i], i+1, dssp->N[0][i], dssp->N[1][i], dssp->N[2][i]);
    putchar('\n');
    for (i = 0; i < len; ++i)
        printf("  %3s H[%3d] = %8.3f%8.3f%8.3f \n", dssp->residue[i], i+1, dssp->H[0][i], dssp->H[1][i], dssp->H[2][i]);
    putchar('\n');
    for (i = 0; i < len; ++i)
        printf(" %3s CA[%3d] = %8.3f%8.3f%8.3f \n", dssp->residue[i], i+1, dssp->CA[0][i], dssp->CA[1][i], dssp->CA[2][i]);
    putchar('\n');
}


void
PrintSummary(DSSP *dssp)
{
    int             i;
    int             len = dssp->len;

    printf(  "\n\n atom[num] res SS [H-bnd] -                 - B1 B2  S T3 T4 T5 \n");
    printf(    " --------- --- -- -------                     -- -- -- -- -- -- \n");

    for (i = 0; i < len; ++i)
    {
        printf(" atom[%3d] %3s %c  %3d %3d - %3d %3d %3d %3d - %2d %2d %2d %2d %2d %2d \n",
                i+1, dssp->residue[i], dssp->summary[i],
                dssp->bondatm[i][0]+1, dssp->bondatm[i][1]+1,
                dssp->bridgeP[i][0], dssp->bridgeP[i][1], dssp->bridgeA[i][0], dssp->bridgeA[i][1],
                dssp->bridgePn[i], dssp->bridgeAn[i], dssp->bend[i],
                dssp->turn3[i], dssp->turn4[i], dssp->turn5[i]);
    }
    putchar('\n');
    putchar('\n');

    for (i = 0; i < len; ++i)
        putchar(dssp->summary[i]);
        /* printf("\n DSSP Summary [%3d] = %2c ", i, dssp->summary[i]); */
    putchar('\n');
}


/*
static void
h_vect(float *a, float *b, float cosa, float sina, float *c)
{
    *(c) = *(a) * cosa + *(b) * sina;
    *(c + 1) = *(a + 1) * cosa + *(b + 1) * sina;
    *(c + 2) = *(a + 2) * cosa + *(b + 2) * sina;
}

static void
vect(float *a, float *b, float *c)
{
    *(a) = *(b) - *(c);
    *(a + 1) = *(b + 1) - *(c + 1);
    *(a + 2) = *(b + 2) - *(c + 2);
}
*/

/* calculate position of main chain hydrogens */
/*
int
H_calc(int nres, XB xb[], char seq[])
{
    int             i, k;
    float           x[3], y[3], z[3];
    float           alph, sina, cosa;

    alph = (3.14159 * 123.0) / 180.0;
    cosa = cos(alph);
    sina = sin(alph);

    k = 0;
    for (i = 1; i <= nres; i++)
    {
        if (xb[i].h[0] != MISSING)
            continue;
        if (seq[i] == 'P')
            continue;
        if (xb[i - 1].c[0] == MISSING || xb[i].n[0] == MISSING
            || xb[i].ca[0] == MISSING)
            continue;
        vect(x, xb[i - 1].c, xb[i].n);
        vect(y, xb[i].ca, xb[i].n);
        cross(y, x, z);
        cross(z, x, y);
        norm(x);
        norm(y);
        h_vect(x, y, cosa, sina, z);
        xb[i].h[0] = z[0] + xb[i].n[0];
        xb[i].h[1] = z[1] + xb[i].n[1];
        xb[i].h[2] = z[2] + xb[i].n[2];
        k++;
    }
    return (k);
}
*/

#undef SQR
#undef HB_DISTMAX
#undef HB_DISTMAX_SQR
#undef MY_PI
#undef TWOPI
#undef PIHALF
#undef MAXHBONDS
#undef MAXBRIDGE
