/*
 * International Chemical Identifier (InChI)
 * Version 1
 * Software version 1.05
 * January 27, 2017
 *
 * The InChI library and programs are free software developed under the
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
 * Originally developed at NIST.
 * Modifications and additions by IUPAC and the InChI Trust.
 * Some portions of code were developed/changed by external contributors
 * (either contractor or volunteer) which are listed in the file
 * 'External-contributors' included in this distribution.
 *
 * IUPAC/InChI-Trust Licence No.1.0 for the
 * International Chemical Identifier (InChI)
 * Copyright (C) IUPAC and InChI Trust Limited
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0,
 * or any later version.
 *
 * Please note that this library is distributed WITHOUT ANY WARRANTIES
 * whatsoever, whether expressed or implied.
 * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details.
 *
 * You should have received a copy of the IUPAC/InChI Trust InChI
 * Licence No. 1.0 with this library; if not, please write to:
 *
 * The InChI Trust
 * 8 Cavendish Avenue
 * Cambridge CB1 7US
 * UK
 *
 * or e-mail to alan@inchi-trust.org
 *
 */


#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "mode.h"
#include "mol_fmt.h"
#include "ichierr.h"
#include "util.h"
#include "strutil.h"
#include "inchi_api.h"

/*
    Convert input molecular data to internal representation

*/

/* Local prototypes */

int ReadMolfileToInpAtoms( INCHI_IOSTREAM *inp_file,
                           int bDoNotAddH, inp_ATOM **at,
                           MOL_COORD **szCoord,
                           OrigAtDataPolymer **polymer,
                           OrigAtDataV3000 **v3000,
                           int max_num_at,
                           int *num_dimensions,
                           int *num_bonds,
                           const char *pSdfLabel,
                           char *pSdfValue,
                           long *Id,
                           long *lMolfileNumber,
                           INCHI_MODE *pInpAtomFlags,
                           int *err,
                           char *pStrErr );
inp_ATOM* MakeInpAtomsFromMolfileData( MOL_FMT_DATA* mfdata,
                                       int *num_atoms,
                                       int *num_bonds,
                                       inp_ATOM* at_inp,
                                       int bDoNotAddH,
                                       int *err,
                                       char *pStrErr );
int SetInpAtomsXYZ( MOL_FMT_DATA* mfdata,
                    int num_atoms,
                    inp_ATOM* at,
                    int *err,
                    char *pStrErr );
void calculate_valences ( MOL_FMT_DATA* mfdata,
                          inp_ATOM* at,
                          int *num_atoms,
                          int bDoNotAddH,
                          int *err,
                          char *pStrErr);


int SetExtOrigAtDataByMolfileExtInput( MOL_FMT_DATA* mfdata,
                                       OrigAtDataPolymer **polymer,
                                       OrigAtDataV3000 **v3000,
                                       char *pStrErr );




/*
    Create OrigInpData From Molfile
*/
int CreateOrigInpDataFromMolfile( INCHI_IOSTREAM *inp_file,
                                  ORIG_ATOM_DATA *orig_at_data,
                                  int bMergeAllInputStructures,
                                  int bGetOrigCoord,
                                  int bDoNotAddH,
                                  const char *pSdfLabel,
                                  char *pSdfValue,
                                  long *lSdfId,
                                  long *lMolfileNumber,
                                  INCHI_MODE *pInpAtomFlags,
                                  int *err,
                                  char *pStrErr )
{
int                num_dimensions_new;
int                num_inp_bonds_new;
int                num_inp_atoms_new;
inp_ATOM        *at_new     = NULL;
inp_ATOM        *at_old     = NULL;
int                nNumAtoms  = 0;
MOL_COORD        *szCoordNew = NULL;
MOL_COORD        *szCoordOld = NULL;
OrigAtDataPolymer        *polymer = NULL;
OrigAtDataV3000        *v3000 = NULL;

int                i, j;
int                max_num_at;


    if ( pStrErr )
        pStrErr[0] = '\0';


    /* NB: currently (v. 1.04) legacy CLI option "MERGE" is unsupported
           so the loop below is always a single pass */

    max_num_at = MAX_ATOMS;
    if ( !( *pInpAtomFlags  & FLAG_SET_INP_LARGE_ALLOWED ) )
        max_num_at = NORMALLY_ALLOWED_INP_MAX_ATOMS;

    do    /* while ( !*err && bMergeAllInputStructures ) */
    {

        at_old     = orig_at_data    ? orig_at_data->at:NULL; /*  save pointer to the previous allocation */

        szCoordOld = orig_at_data    ? orig_at_data->szCoord:NULL;


        num_inp_atoms_new = ReadMolfileToInpAtoms( inp_file, bDoNotAddH,
                                                   orig_at_data ? &at_new:NULL,
                                                   (bGetOrigCoord && orig_at_data) ? &szCoordNew : NULL,
                                                   &polymer, &v3000,
                                                   max_num_at, &num_dimensions_new, &num_inp_bonds_new,
                                                   pSdfLabel, pSdfValue, lSdfId, lMolfileNumber,
                                                   pInpAtomFlags, err, pStrErr );


        if ( num_inp_atoms_new <= 0 && !*err )
        {
            TREAT_ERR (*err, 0, "Empty structure");
            *err = 98;
        }
        else if ( orig_at_data && !num_inp_atoms_new &&
                  10 < *err && *err < 20 &&
                  orig_at_data->num_inp_atoms > 0 &&
                  bMergeAllInputStructures )
        {
            *err = 0; /* end of file */
            break;
        }
        else if ( num_inp_atoms_new > 0 && orig_at_data )
        {
            /*  merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */
            nNumAtoms = num_inp_atoms_new + orig_at_data->num_inp_atoms;
            if ( nNumAtoms >= max_num_at ) /*MAX_ATOMS )  */
            {
                TREAT_ERR (*err, 0, "Too many atoms [did you forget 'LargeMolecules' switch?]");
                *err = 70;
                orig_at_data->num_inp_atoms = -1;
            }
            else if ( !at_old )
            {
                /* the first structure */
                orig_at_data->at      = at_new;
                orig_at_data->szCoord = szCoordNew;
                at_new = NULL;
                szCoordNew = NULL;
                orig_at_data->num_inp_atoms  = num_inp_atoms_new;
                orig_at_data->num_inp_bonds  = num_inp_bonds_new;
                orig_at_data->num_dimensions = num_dimensions_new;
                /* v 1.05 */
                orig_at_data->polymer = polymer;
                orig_at_data->v3000 = v3000;
                polymer = NULL;
                v3000 = NULL;
            }
            else if ( (orig_at_data->at = ( inp_ATOM* ) inchi_calloc( nNumAtoms, sizeof(inp_ATOM) )) &&
                      (!szCoordNew || (orig_at_data->szCoord = (MOL_COORD *) inchi_calloc( nNumAtoms, sizeof(MOL_COORD) )))
                    )
            {
                /*  switch at_new <--> orig_at_data->at; */
                if ( orig_at_data->num_inp_atoms )
                {
                    memcpy( orig_at_data->at, at_old, orig_at_data->num_inp_atoms * sizeof(orig_at_data->at[0]) );
                    /*  adjust numbering in the newly read structure */
                    for ( i = 0; i < num_inp_atoms_new; i ++ )
                    {
                        for ( j = 0; j < at_new[i].valence; j ++ )
                        {
                            at_new[i].neighbor[j] += orig_at_data->num_inp_atoms;
                        }
                        at_new[i].orig_at_number += orig_at_data->num_inp_atoms; /* 12-19-2003 */
                    }
                    if ( orig_at_data->szCoord && szCoordOld )
                        memcpy( orig_at_data->szCoord, szCoordOld, orig_at_data->num_inp_atoms * sizeof(MOL_COORD) );
                }
                if ( at_old )
                {
                    inchi_free( at_old );
                    at_old = NULL;
                }
                if ( szCoordOld )
                {
                    inchi_free( szCoordOld );
                    szCoordOld = NULL;
                }

                /*  Copy newly read structure */
                memcpy( orig_at_data->at + orig_at_data->num_inp_atoms,
                        at_new,
                        num_inp_atoms_new * sizeof(orig_at_data->at[0]) );

                if ( orig_at_data->szCoord && szCoordNew )
                {
                    memcpy( orig_at_data->szCoord + orig_at_data->num_inp_atoms,
                            szCoordNew,
                            num_inp_atoms_new * sizeof(MOL_COORD) );
                }

                /*  Add other things */
                orig_at_data->num_inp_atoms += num_inp_atoms_new;
                orig_at_data->num_inp_bonds += num_inp_bonds_new;
                orig_at_data->num_dimensions = inchi_max(num_dimensions_new, orig_at_data->num_dimensions);
                /* v 1.05 */
                orig_at_data->polymer = polymer;
                orig_at_data->v3000 = v3000;
            }
            else
            {
                TREAT_ERR (*err, 0, "Out of RAM");
                *err = -1;
            }
        }
        else if ( num_inp_atoms_new > 0 )
        {
            nNumAtoms += num_inp_atoms_new;
        }

        if ( at_new )
        {
            inchi_free( at_new );
            at_new = NULL;
        }
        if ( polymer )
        {
            inchi_free( polymer );
            polymer = NULL;
        }
        if ( v3000 )
        {
            inchi_free( v3000 );
            v3000 = NULL;
        }
    } while ( !*err && bMergeAllInputStructures );


    if ( szCoordNew )
        inchi_free( szCoordNew );
    if ( at_new )
        inchi_free( at_new );

    if ( *err )
        FreeOrigAtData( orig_at_data );

    if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] )
    {
        TREAT_ERR (*err, 0, "Unknown error");  /*   <BRKPT> */
    }

    return orig_at_data    ? orig_at_data->num_inp_atoms
                        : nNumAtoms;
}


/*
    ReadMolfileToInpAtoms
*/
int ReadMolfileToInpAtoms( INCHI_IOSTREAM *inp_file,
                           int bDoNotAddH,
                           inp_ATOM **at,
                           MOL_COORD **szCoord,
                           OrigAtDataPolymer **polymer,
                           OrigAtDataV3000 **v3000,
                           int max_num_at,
                           int *num_dimensions,
                           int *num_bonds,
                           const char *pSdfLabel,
                           char *pSdfValue,
                           long *Id,
                           long *lMolfileNumber,
                           INCHI_MODE *pInpAtomFlags,
                           int *err,
                           char *pStrErr )
{
int num_atoms=0;
MOL_FMT_DATA *mfdata=NULL;
MOL_FMT_HEADER_BLOCK OnlyHeaderBlock,
                     *pOnlyHeaderBlock = NULL,
                     *pHdr;
MOL_FMT_CTAB OnlyCTab,
             *pOnlyCTab = NULL;
char cSdfValueFirstChar=0;
int treat_polymers = *pInpAtomFlags & FLAG_SET_INP_POLYMERS_RECOGNIZED;

    if ( at )
    {
        pOnlyHeaderBlock = NULL;
        if ( *at && max_num_at )
        {
            memset( *at, 0, max_num_at * sizeof(inp_ATOM) );
        }
        if ( szCoord && *szCoord )
        {
            inchi_free( *szCoord );
            *szCoord = NULL;
        }
    }
    else
    {
        pOnlyHeaderBlock = &OnlyHeaderBlock;
        pOnlyCTab        = &OnlyCTab;
    }

    if ( pSdfValue )
    {
        cSdfValueFirstChar = pSdfValue[0];
        pSdfValue[0] = '\0';
    }


    /* Read mol-formatted file (MOL or SD) block  */
    mfdata = ReadMolfile( inp_file,
                          pOnlyHeaderBlock,
                          pOnlyCTab,
                          NULL != szCoord,
                          treat_polymers,
                          NULL,
                          0,
                          Id,
                          pSdfLabel,
                          pSdfValue,
                          err,
                          pStrErr );

    pHdr = ( mfdata && !pOnlyHeaderBlock ) ?  &mfdata->hdr
                                            : ( !mfdata && pOnlyHeaderBlock ) ? pOnlyHeaderBlock
                                                                               : NULL;

    if ( lMolfileNumber && pHdr )
         *lMolfileNumber = MolfileExtractStrucNum( pHdr );

    if ( pSdfValue && !pSdfValue[0] &&
         pSdfLabel && pSdfLabel[0]  &&
         pHdr )
    {
         if ( !inchi_stricmp(pSdfLabel, "MolfileName") )
         {
             mystrncpy( pSdfValue, pHdr->molname, MAX_SDF_VALUE+1 );
             lrtrim( pSdfValue, NULL );
         }
         else if ( !inchi_stricmp(pSdfLabel, "MolfileLine2") )
         {
             mystrncpy( pSdfValue, pHdr->line2, MAX_SDF_VALUE+1 );
             lrtrim( pSdfValue, NULL );
         }
         else if ( !inchi_stricmp(pSdfLabel, "MolfileComment") )
         {
             mystrncpy( pSdfValue, pHdr->comment    , MAX_SDF_VALUE+1 );
             lrtrim( pSdfValue, NULL );
         }
         else if ( !inchi_stricmp(pSdfLabel, "MolfileIntRegNo") && pHdr->internal_regno )
         {
             sprintf( pSdfValue, "%ld", pHdr->internal_regno );
         }

         if ( !pSdfValue[0] )
             pSdfValue[0] = cSdfValueFirstChar;
    }


    if ( mfdata && at && !*err )
    {
        /* (*at) either points to already allocated memory or NULL */
        if ( mfdata->ctab.n_atoms <= max_num_at )
        {

            *at = MakeInpAtomsFromMolfileData( mfdata, &num_atoms, num_bonds,
                                               *at, bDoNotAddH, err, pStrErr );

            if ( *err >= 0 )
            {
                *num_dimensions = SetInpAtomsXYZ( mfdata, num_atoms,
                                                  *at, err, pStrErr );

                if ( szCoord )
                {
                    *szCoord = mfdata->ctab.coords;
                    mfdata->ctab.coords = NULL;
                }

                /* Check that all atoms nos of Hs are in allowed range ( <=MAXVAL, currently 20 ) */
                if ( 1 )
                {
                    int i;
                    for ( i = 0; i < num_atoms; i++ )
                    {
                        if ( (*at)[i].num_iso_H[0] + (*at)[i].num_iso_H[1] + (*at)[i].num_iso_H[2] > MAXVAL)
                        {
                            *err = 70 + 8;
                            TREAT_ERR(*err, 0, "Too many hydrogens at heavy atom");
                            num_atoms = -1;
                            break;
                        }
                    }
                }

                if ( !*err )
                {
                    *err = SetExtOrigAtDataByMolfileExtInput( mfdata, polymer, v3000, pStrErr );
                    if ( *err  )
                    {
                        /*TREAT_ERR (*err, 0, "Error while getting extended Molfile input");*/
                        *err = 80;
                        num_atoms = -1;
                    }
                }
            }

            else
            {
                if ( *err == -2 ) /* unk element */
                {
                    *err = 90;
                    num_atoms = -1;
                }
            }
        }

        else
        {
            /* non-affordable struct size */
            TREAT_ERR (*err, 0, "Too many atoms [did you forget 'LargeMolecules' switch?]");
            *err = 70;
            num_atoms = -1;
        }

        if ( *err > 0 )
            *err += 100;

        /* 11-16-2004: use Chiral flag */
        if ( num_atoms > 0 && at && *at && mfdata && pInpAtomFlags )
        {
            if ( mfdata->ctab.chiral_flag )
                *pInpAtomFlags |= FLAG_INP_AT_CHIRAL;
            else
                *pInpAtomFlags |= FLAG_INP_AT_NONCHIRAL;
        }
    }

    else if ( !at )
    {
        num_atoms = pOnlyCTab->n_atoms;
    }


    if ( !pOnlyHeaderBlock )
        FreeMolfileData( mfdata );

    return num_atoms;
}


/*
    Make Inp Atoms From Molfile Data
*/
inp_ATOM* MakeInpAtomsFromMolfileData( MOL_FMT_DATA* mfdata,
                                      int *num_atoms,
                                      int *num_bonds,
                                      inp_ATOM* at_inp,
                                      int bDoNotAddH,
                                      int *err, char *pStrErr )
{
inp_ATOM *at = NULL;
/* char      *bond_stereo = NULL; */
AT_NUMB  *p1, *p2;
int       i, a1, a2, n1, n2, bonds, iso_atw_diff;
char      bond_stereo, bond_type;
static int el_number_H = 0;

    if ( !el_number_H )
    {
        el_number_H = get_periodic_table_number( "H" ); /* one-time initialization */
    }

    *err = 0;

    *num_atoms = mfdata->ctab.n_atoms;
    *num_bonds = 0;


    if ( MolfileHasNoChemStruc(mfdata) )
        goto exit_function;


    /* Allocate memory if necessary */
    if ( at_inp )
    {
        at = at_inp;
    }
    else
    {
        at = CreateInpAtom( *num_atoms );
        if ( !at )
        {
            *err = -1;
            TREAT_ERR_AND_FIN (*err, -1, exit_function, "Out of RAM");
        }
    }


    /* Copy atoms info */
    for ( i = 0; i < *num_atoms; i ++ )
    {

        mystrncpy( at[i].elname, mfdata->ctab.atoms[i].symbol, sizeof(at->elname) );
        /* at[i].chem_bonds_valence = mfdata->ctab.atoms[i].valence; */
        /*  MOLfile valence; will change */

        at[i].orig_at_number     = (AT_NUMB)(i+1);
        at[i].iso_atw_diff       = mfdata->ctab.atoms[i].mass_difference;
        at[i].charge             = mfdata->ctab.atoms[i].charge;
        at[i].radical            = mfdata->ctab.atoms[i].radical;

        /* see SetInpAtomXYZ()
        at[i].x                  = mfdata->ctab.atoms[i].fx;
        at[i].y                  = mfdata->ctab.atoms[i].fy;
        at[i].z                  = mfdata->ctab.atoms[i].fz;
        */

        iso_atw_diff             = mfdata->ctab.atoms[i].mass_difference;
        at[i].iso_atw_diff       = iso_atw_diff==ZERO_ATW_DIFF ? 1
                                                               : iso_atw_diff>  0 ? iso_atw_diff+1
                                                                                  : iso_atw_diff;

#if ( SINGLET_IS_TRIPLET == 1 )
        if ( at[i].radical == RADICAL_SINGLET )
        {
            at[i].radical = RADICAL_TRIPLET;
        }
#endif
#if ( bRELEASE_VERSION != 1 )
        if ( isdigit( at[i].elname[0] ) ) { /*  for testing */
            mystrncpy( at[i].elname, "C", sizeof(at->elname) );
        }
#endif

        if ( ERR_ELEM == (n1 = get_periodic_table_number( at[i].elname ) ) )
        {
            /*  Case when elname contains more than 1 element: extract number of H if possible */
            at[i].num_H = extract_H_atoms( at[i].elname, at[i].num_iso_H );

            if ( !at[i].elname[0] && NUMH(at, i) )
            {
                /* alias contains only H. Added 2004-07-21, fixed 2004-07-22
                 * move the heaviest isotope to the "central atom"
                 * Note: this must be consistent with H-H treatment in remove_terminal_HDT()
                 */
                strcpy( at[i].elname, "H" );
                if ( NUM_ISO_H(at,i) )
                {
                    int j;
                    for ( j = NUM_H_ISOTOPES-1; 0 <= j; j -- )
                    {
                        if ( at[i].num_iso_H[j] )
                        {
                            at[i].num_iso_H[j] --;
                            at[i].iso_atw_diff = 1 + j;
                            break;
                        }
                    }
                }
                else
                {
                    at[i].num_H --;
                }
            }
            if ( ERR_ELEM == (n1 = get_periodic_table_number( at[i].elname ) ) )
            {
                n1 = 0;
            }
        } /* if ( ERR_ELEM == */


        at[i].el_number = (U_CHAR) n1;
        if ( !n1 )
        {
            *err = -2;
            TREAT_ERR (*err, -2, "Unknown element(s):");
            TREAT_ERR_AND_FIN  (*err, -2, exit_function, at[i].elname );
        }
        else
        /* replace explicit D or T with isotopic H (added 2003-06-02) */
        if ( el_number_H == n1 && !at[i].iso_atw_diff )
        {
            switch( at[i].elname[0] )
            {
            case 'D':
                at[i].iso_atw_diff = 2;
                mystrncpy( at[i].elname, "H", sizeof(at->elname) );
                break;
            case 'T':
                at[i].iso_atw_diff = 3;
                mystrncpy( at[i].elname, "H", sizeof(at->elname) );
                break;
            }
        }
    } /* eof copy atom info */



    /*---------------- stereo information notes. ------------------------

      Currently:  1. stereo sign
      =========   --------------
      MOLfile     (atom number = MOLfile atom number - 1, no stdata as an intermediate)
         |        if mfdata->ctab.bonds[i].atnum1 < mfdata->ctab.bonds[i].atnum2
         v        then
      inp_ATOM        stereo > 0
                  else
                     stereo < 0

                  2. neighbor z-coordinate
                  ------------------------
                  neighbor z-coord > 0 for Up if sign(stdata_bond_no) = sign(at[i].neighbor[j]-i)

    --------------------------------------------------------------------*/


    /* Copy bond info */
    for ( i = 0, bonds = 0; i < mfdata->ctab.n_bonds; i ++ )
    {

        bond_stereo = mfdata->ctab.bonds[i].bond_stereo;
        bond_type   = mfdata->ctab.bonds[i].bond_type;

        a1 = mfdata->ctab.bonds[i].atnum1-1;
        a2 = mfdata->ctab.bonds[i].atnum2-1;

        if ( a1 < 0 || a1 >= *num_atoms ||
             a2 < 0 || a2 >= *num_atoms ||
             a1 == a2 )
        {
            *err |= 1; /*  bond for impossible atom number(s); ignored */
            TREAT_ERR (*err, 0, "Bond to nonexistent atom");
            continue;
        }

        /*  check for multiple bonds between same atoms */
        p1 = is_in_the_list( at[a1].neighbor, (AT_NUMB)a2, at[a1].valence );
        p2 = is_in_the_list( at[a2].neighbor, (AT_NUMB)a1, at[a2].valence );

        if ( (p1 || p2) && (p1 || at[a1].valence < MAXVAL) && (p2 || at[a2].valence < MAXVAL) )
        {
            n1 = p1? (p1 - at[a1].neighbor) : at[a1].valence ++;
            n2 = p2? (p2 - at[a2].neighbor) : at[a2].valence ++;
            TREAT_ERR (*err, 0, "Multiple bonds between two atoms");
            *err |= 2; /*  multiple bonds between atoms */
        }
        else if ( !p1 && !p2 && at[a1].valence < MAXVAL && at[a2].valence < MAXVAL )
        {
            n1 = at[a1].valence ++;
            n2 = at[a2].valence ++;
            bonds ++;
        }
        else
        {
            char szMsg[64];
            *err |= 4; /*  too large number of bonds. Some bonds ignored. */
            sprintf( szMsg, "Atom '%s' has more than %d bonds",
                            at[a1].valence>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL );
            TREAT_ERR (*err, 0, szMsg);
            continue;
        }

        if ( bond_type < MIN_INPUT_BOND_TYPE || bond_type > MAX_INPUT_BOND_TYPE )
        {
            char szBondType[16];
            sprintf( szBondType, "%d", bond_type );
            bond_type = 1;
            TREAT_ERR (*err, 0, "Unrecognized bond type:");
            TREAT_ERR (*err, 0, szBondType);
            *err |= 8; /*  Unrecognized Bond type replaced with single bond */
        }

        /* bond type */
        at[a1].bond_type[n1] =
        at[a2].bond_type[n2] = bond_type;

        /* connection */
        at[a1].neighbor[n1] = (AT_NUMB)a2;
        at[a2].neighbor[n2] = (AT_NUMB)a1;

        /* stereo */
        if ( bond_stereo == INPUT_STEREO_DBLE_EITHER /* 3 */ )
        {
            at[a1].bond_stereo[n1] =
                at[a2].bond_stereo[n2] =
                                        STEREO_DBLE_EITHER;
        }
        else if ( bond_stereo == INPUT_STEREO_SNGL_UP     ||  /* 1 */
                  bond_stereo == INPUT_STEREO_SNGL_EITHER ||  /* 4 */
                  bond_stereo == INPUT_STEREO_SNGL_DOWN       /* 6 */ )
        {
            char cStereo;
            switch ( bond_stereo )
            {
            case INPUT_STEREO_SNGL_UP:
                cStereo = STEREO_SNGL_UP;
                break;
            case INPUT_STEREO_SNGL_EITHER:
                cStereo = STEREO_SNGL_EITHER;
                break;
            case INPUT_STEREO_SNGL_DOWN:
                cStereo = STEREO_SNGL_DOWN;
                break;
            }
            at[a1].bond_stereo[n1] =  cStereo; /*  >0: the wedge (pointed) end is at this atom, a1 */
            at[a2].bond_stereo[n2] = -cStereo; /*  <0: the wedge (pointed) end is at the opposite atom, a1 */
        }
        else if ( bond_stereo )
        {
            *err |= 16; /*  Ignored unrecognized Bond stereo */
            TREAT_ERR (*err, 0, "Unrecognized bond stereo");
            continue;
        }
    } /* eof copy bond info */

    *num_bonds = bonds;

    /* special valences */
    calculate_valences (mfdata, at, num_atoms, bDoNotAddH, err, pStrErr);


exit_function:;
    return at;
}


/*

*/
void calculate_valences ( MOL_FMT_DATA* mfdata,
                          inp_ATOM* at,
                          int *num_atoms,
                          int bDoNotAddH,
                          int *err,
                          char *pStrErr)
{
int bNonMetal;
int a1, a2, n1, n2, valence;
AT_NUMB  *p1;

    /* special valences */

    for ( bNonMetal = 0; bNonMetal < 2; bNonMetal ++ )
    {
        for ( a1 = 0; a1 < *num_atoms; a1 ++ )
        {
            int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1],
                              bond_type,
                              bHasMetalNeighbor;
                                    /* should the "!=" be replaced with "==" ??? */
            if ( bNonMetal == is_el_a_metal( at[a1].el_number ) )
            {
                /* first process all metals, after that all non-metals */
                continue;
            }

            memset( num_bond_type, 0, sizeof(num_bond_type) );

            /* valence = at[a1].chem_bonds_valence; */ /*  save atom valence if available */
            /* 2006-08-31: fix for uncharged >N(IV)- in an aromatic ring */

            valence =
                (mfdata && mfdata->ctab.atoms) ? mfdata->ctab.atoms[a1].valence
                                               : at[a1].chem_bonds_valence;

            at[a1].chem_bonds_valence = 0;
            bHasMetalNeighbor = 0;
            for ( n1 = 0; n1 < at[a1].valence; n1 ++ )
            {
                bond_type = at[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE;
                if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE )
                {
                    bond_type = 0;
                    TREAT_ERR (*err, 0, "Unknown bond type in MOLfile assigned as a single bond");
                }
                num_bond_type[ bond_type ] ++;
                /* -- too a radical solution -- removed from next to ver 1.12B --- */
            }

            for ( n1 = 0;
                    MIN_INPUT_BOND_TYPE + n1 <= 3 &&
                    MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE;
                        n1 ++ )
            {
                /* add all bond orders except for "aromatic" bonds */
                at[a1].chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1];
            }

            n2 = 0;
            if ( MIN_INPUT_BOND_TYPE <= BOND_TYPE_ALTERN &&
                 BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE &&
                 ( n2 = num_bond_type[BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) )
            {
                /* accept input aromatic bonds for now */
                switch ( n2 )
                {
                case 2:
                    at[a1].chem_bonds_valence += 3;  /* =A- */
                    break;
                case 3:
                    at[a1].chem_bonds_valence += 4;  /* =A< */
                    break;
                default:
                    /*  if 1 or >= 4 aromatic bonds then replace   */
                    /* such bonds with single bonds                */
                    /*  and detect an error in the input structure */
                    for ( n1 = 0; n1 < at[a1].valence; n1 ++ )
                    {
                        if ( at[a1].bond_type[n1] == BOND_TYPE_ALTERN )
                        {
                            a2 = at[a1].neighbor[n1];
                            p1 = is_in_the_list( at[a2].neighbor, ( AT_NUMB)a1,
                                                                    at[a2].valence );
                            if ( p1 )
                            {
                                at[a1].bond_type[n1] =
                                at[a2].bond_type[p1-at[a2].neighbor] = BOND_TYPE_SINGLE;
                            }
                            else
                            {
                                *err = -2;  /*  Program error */
                                TREAT_ERR (*err, 0, "Program error interpreting MOLfile");
                                return; /*  no structure */
                            }
                        }
                    }

                    at[a1].chem_bonds_valence += n2;
                    *err |= 32;
                    TREAT_ERR (*err, 0, "Atom has 1 or more than 3 aromatic bonds");
                    n2 = 0;
                    break;
                }
            }

            if ( n2 && !valence )
            {
                /* atom has aromatic bonds AND the chemical valence is not known */

                int num_H = NUMH(at, a1);
                /* bug fix 2006-08-25: aliased H result in num_H > 0 */
                /* => wrong call to detect_unusual_el_valence() */
                int chem_valence = at[a1].chem_bonds_valence /*+ num_H*/;

                int bUnusualValenceArom =
                    detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge,
                                                at[a1].radical, chem_valence,
                                                num_H, at[a1].valence );
                int bUnusualValenceNoArom =
                    detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge,
                                                at[a1].radical, chem_valence-1,
                                                num_H, at[a1].valence );

#if ( CHECK_AROMBOND2ALT == 1 )
                if ( bUnusualValenceArom &&
                     !bUnusualValenceNoArom &&
                     0 == nBondsValToMetal( at, a1) )
#else
                if ( bUnusualValenceArom && !bUnusualValenceNoArom )
#endif
                {
                    /* typically NH in 5-member aromatic ring */
                    at[a1].chem_bonds_valence --;
                }
            }
            else if ( n2 && valence )
            {
                /* atom has aromatic bonds AND the chemical valence is known */
                int num_H = NUMH(at, a1);
                int chem_valence = at[a1].chem_bonds_valence + num_H;
                if ( valence == chem_valence-1 )
                {
                    /* typically NH in 5-member aromatic ring */
                    at[a1].chem_bonds_valence --;
                }
            }

            /*  Set number of hydrogen atoms */
            if (mfdata)
            {
                at[a1].num_H = get_num_H( at[a1].elname,
                                          at[a1].num_H,
                                          at[a1].num_iso_H,
                                          at[a1].charge, at[a1].radical,
                                          at[a1].chem_bonds_valence,
                                          mfdata->ctab.atoms[a1].valence, /* instead of valence */
                                          mfdata->ctab.atoms[a1].atom_aliased_flag,
                                          bDoNotAddH,
                                          bHasMetalNeighbor );
            }
        }
    } /* for ( bNonMetal = ... */

    return;
}


/*
    SetInpAtomsXYZ
*/
int SetInpAtomsXYZ( MOL_FMT_DATA* mfdata,
                    int num_atoms,
                    inp_ATOM* at,
                    int *err,
                    char *pStrErr )
{
int      i, num_dimensions=0;

#if ( NORMALIZE_INP_COORD == 1 )
int do_scale_xyz=1;
#else
int do_scale_xyz=0;
#endif
double x0, y0, z0, xmin, ymin, zmin, scaler;


    num_dimensions = MolfileGetXYZDimAndNormFactors( mfdata,
                                                     do_scale_xyz,
                                                     &x0, &y0, &z0,
                                                     &xmin, &ymin, &zmin,
                                                     &scaler,
                                                     err, pStrErr );


    if ( num_dimensions==0 )
        goto exit_function;

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

        double x = mfdata->ctab.atoms[i].fx;
        double y = mfdata->ctab.atoms[i].fy;
        double z = mfdata->ctab.atoms[i].fz;

        if ( !do_scale_xyz )
        {
            at[i].x = x;
            at[i].y = y;
            at[i].z = z;
        }
        else
        {
            x = (x - xmin)*scaler + x0;
            y = (y - ymin)*scaler + y0;
            z = (z - zmin)*scaler + z0;
            /* floor() behavior is not well defined for negative arguments.
             * Use positive arguments only to get nearest integer.
             */
            at[i].x = ( x >= 0.0 )? (int)floor( x + 0.5 ) : -(int)floor( -x + 0.5 );
            at[i].y = ( y >= 0.0 )? (int)floor( y + 0.5 ) : -(int)floor( -y + 0.5 );
            at[i].z = ( z >= 0.0 )? (int)floor( z + 0.5 ) : -(int)floor( -z + 0.5 );
        }
    }


exit_function:;
    return num_dimensions;
}


/*

*/
inp_ATOM *CreateInpAtom( int num_atoms )
{
    void *p = inchi_calloc(num_atoms, sizeof(inp_ATOM) );
    return (inp_ATOM* )p;
}


/*

*/
void FreeInpAtom( inp_ATOM **at )
{
    if ( at && *at )
    {
        inchi_free( *at );
        *at = NULL;
    }
    return;
}


/*

*/
void FreeInpAtomData( INP_ATOM_DATA *inp_at_data )
{
    if ( inp_at_data )
    {
        FreeInpAtom( &inp_at_data->at );
        FreeInpAtom( &inp_at_data->at_fixed_bonds );
        memset( inp_at_data, 0, sizeof(*inp_at_data) );
    }
    return;
}


/*

*/
int CreateInpAtomData( INP_ATOM_DATA *inp_at_data,
                       int num_atoms,
                       int create_at_fixed_bonds )
{
    FreeInpAtomData( inp_at_data );

    if ( (inp_at_data->at = CreateInpAtom( num_atoms )) &&
         (!create_at_fixed_bonds || (inp_at_data->at_fixed_bonds = CreateInpAtom( num_atoms) ) ) )
    {
        inp_at_data->num_at = num_atoms;
        return 1;
    }

    FreeInpAtomData( inp_at_data );

    return 0;
}


/*

*/
void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data )
{
    FreeInpAtom( &inp_at_data->at );

    if ( inp_at_data->nOffsetAtAndH )
        inchi_free( inp_at_data->nOffsetAtAndH );
    memset( inp_at_data, 0, sizeof(*inp_at_data) );
}


#ifndef TARGET_API_LIB
/*

*/
int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data,
                        int num_atoms,
                        int num_components,
                        int bIntermediateTaut )
{
    FreeCompAtomData( inp_at_data );

    if ( (inp_at_data->at = CreateInpAtom( num_atoms )) &&
         (num_components <= 1 || bIntermediateTaut ||
            (inp_at_data->nOffsetAtAndH = (AT_NUMB*)inchi_calloc(sizeof(inp_at_data->nOffsetAtAndH[0]), 2*(num_components+1)))))
    {
        inp_at_data->num_at = num_atoms;
        inp_at_data->num_components = (num_components>1)? num_components : 0;
        return 1;
    }

    FreeCompAtomData( inp_at_data );

    return 0;
}
#endif

#ifndef COMPILE_ANSI_ONLY
/*
    FreeInfAtom
*/
void FreeInfAtom( inf_ATOM **at )
{
    if ( at && *at )
    {
        inchi_free( *at );
        *at = NULL;
    }
    return;
}


/*
    CreateInfAtom
*/
inf_ATOM *CreateInfAtom( int num_atoms )
{
    return (inf_ATOM* ) inchi_calloc(num_atoms, sizeof(inf_ATOM) );
}


/*
    FreeInfoAtomData
*/
void FreeInfoAtomData( INF_ATOM_DATA *inf_at_data )
{
    FreeInfAtom( &inf_at_data->at );
    if ( inf_at_data->pStereoFlags )
        inchi_free( inf_at_data->pStereoFlags );
    memset(inf_at_data, 0, sizeof(*inf_at_data));
    return;
}


/*
    CreateInfoAtomData
*/
int CreateInfoAtomData( INF_ATOM_DATA *inf_at_data,
                        int num_atoms,
                        int num_components )
{
    FreeInfoAtomData( inf_at_data );

    memset( inf_at_data, 0, sizeof(*inf_at_data) );

    if ( (inf_at_data->at = CreateInfAtom( num_atoms )) &&
         (num_components <= 1 ||
          (inf_at_data->pStereoFlags = (AT_NUMB *)inchi_calloc(num_components+1, sizeof(inf_at_data->pStereoFlags[0])))
         )
       )
    {
        inf_at_data->num_at = num_atoms;
        inf_at_data->num_components = num_components;
        return 1;
    }

    FreeInfoAtomData( inf_at_data );

    return 0;
}


/*
    AllocateInfoAtomData
*/
int AllocateInfoAtomData( INF_ATOM_DATA *inf_at_data,
                          int num_atoms,
                          int num_components )
{
    if ( inf_at_data->at = CreateInfAtom( num_atoms ) )
    {
        if ( num_components > 1 &&
            !(inf_at_data->pStereoFlags = (AT_NUMB *)inchi_calloc(num_components+1, sizeof(inf_at_data->pStereoFlags[0]))) )
        {
            FreeInfAtom( &inf_at_data->at );
            return 0;
        }
        return 1;
    }

    return 0;
}


/*
    DuplicateInfoAtomData
*/
int DuplicateInfoAtomData( INF_ATOM_DATA *inf_at_data_to,
                           const INF_ATOM_DATA *inf_at_data_from)
{
    *inf_at_data_to = *inf_at_data_from;

    if ( AllocateInfoAtomData( inf_at_data_to, inf_at_data_from->num_at, inf_at_data_from->num_components ) )
    {
        memcpy( inf_at_data_to->at, inf_at_data_from->at,
                inf_at_data_from->num_at * sizeof(inf_at_data_to->at[0]));
        if ( inf_at_data_to->pStereoFlags && inf_at_data_from->pStereoFlags )
        {
            memcpy( inf_at_data_to->pStereoFlags, inf_at_data_from->pStereoFlags,
                   (inf_at_data_from->num_components+1)*sizeof(inf_at_data_to->pStereoFlags[0]));
        }
        return 1;
    }

    return 0;
}
#endif /* COMPILE_ANSI_ONLY */



/*
    FreeOrigAtData
*/
void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data )
{
    if ( !orig_at_data )
        return;

    FreeInpAtom( &orig_at_data->at );

    if ( orig_at_data->nCurAtLen )
        inchi_free( orig_at_data->nCurAtLen );

    if ( orig_at_data->nOldCompNumber )
        inchi_free( orig_at_data->nOldCompNumber );

    if ( orig_at_data->szCoord )
        inchi_free( orig_at_data->szCoord );

    if ( orig_at_data->nEquLabels )
        inchi_free( orig_at_data->nEquLabels );

    if ( orig_at_data->nSortedOrder )
        inchi_free( orig_at_data->nSortedOrder );

    /* v 1.05 */
    FreeExtOrigAtData ( orig_at_data->polymer, orig_at_data->v3000 );

    memset( orig_at_data, 0, sizeof(*orig_at_data) );

    return;
}


/*
    Free v. 1.05 extensions stuff
*/
void FreeExtOrigAtData( OrigAtDataPolymer *pd, OrigAtDataV3000 *v3k )
{
    int k;

    OrigAtDataPolymer_Free( pd );
    pd = NULL;

    if ( v3k )
    {
        if ( v3k->atom_index_orig )
        {
            inchi_free( v3k->atom_index_orig );
            v3k->atom_index_orig = NULL;
        }
        if ( v3k->atom_index_fin )
        {
            inchi_free( v3k->atom_index_fin );
            v3k->atom_index_fin = NULL;
        }
        if ( v3k->n_haptic_bonds && v3k->lists_haptic_bonds )
        {
            for (k=0; k<v3k->n_haptic_bonds; k++)
                if ( v3k->lists_haptic_bonds[k] )
                {
                    inchi_free( v3k->lists_haptic_bonds[k] );
                    v3k->lists_haptic_bonds[k] = NULL;
                }
            inchi_free( v3k->lists_haptic_bonds );
            v3k->lists_haptic_bonds = NULL;
        }
        if ( v3k->n_steabs && v3k->lists_steabs )
        {
            for (k=0; k<v3k->n_steabs; k++)
                if ( v3k->lists_steabs[k] )
                {
                    inchi_free( v3k->lists_steabs[k] );
                    v3k->lists_steabs[k] = NULL;
                }
            inchi_free( v3k->lists_steabs );
            v3k->lists_steabs = NULL;
        }
        if ( v3k->n_sterel && v3k->lists_sterel )
        {
            for (k=0; k<v3k->n_sterel; k++)
                if ( v3k->lists_sterel[k] )
                {
                    inchi_free( v3k->lists_sterel[k] );
                    v3k->lists_sterel[k] = NULL;
                }
            inchi_free( v3k->lists_sterel );
            v3k->lists_sterel = NULL;
        }
        if ( v3k->n_sterac && v3k->lists_sterac )
        {
            for (k=0; k<v3k->n_sterac; k++)
                if ( v3k->lists_sterac[k] )
                {
                    inchi_free( v3k->lists_sterac[k] );
                    v3k->lists_sterac[k] = NULL;
                }
            inchi_free( v3k->lists_sterac );
            v3k->lists_sterac = NULL;
        }
        memset( v3k, 0, sizeof( *v3k ) );
    }

    return;
}


int SetExtOrigAtDataByMolfileExtInput( MOL_FMT_DATA* mfdata,
                                      OrigAtDataPolymer **ppPolymer,
                                      OrigAtDataV3000 **ppV3000,
                                      char *pStrErr )
{
    int    k, m, err = 0;
    OrigAtDataV3000 *pv=NULL;
    int nsgroups = mfdata->ctab.sgroups.used;

    /* Polymers */
    if ( nsgroups > 0 )
    {
        /* Prepare OrigAtDataPolymer container */
        *ppPolymer = (OrigAtDataPolymer *) inchi_calloc( 1, sizeof(OrigAtDataPolymer) );
        if ( !(*ppPolymer) )
        {
            TREAT_ERR( err, 9001, "Out of RAM");
            goto exitf;
        }


        /* Convert Molfile's Sgroup's to OrigAtDataPolymerUnit's */
        (*ppPolymer)->units = (OrigAtDataPolymerUnit**) inchi_calloc( nsgroups, sizeof((*ppPolymer)->units[0]) );
        if ( !(*ppPolymer)->units )
        {
            TREAT_ERR( err, 9001, "Out of RAM");
            goto exitf;
        }
        memset( (*ppPolymer)->units, 0, sizeof( *(*ppPolymer)->units ) );

        (*ppPolymer)->n                        =    nsgroups;
        (*ppPolymer)->valid                    =    -1;
        (*ppPolymer)->really_do_phase_shift =    0;
        (*ppPolymer)->is_in_reconn            =    0;
        (*ppPolymer)->star_atoms            =    NULL;

        for (k=0; k<nsgroups; k++ )
        {
            int q=0;
            MOL_FMT_SGROUP*     groupk    =    mfdata->ctab.sgroups.group[k];

            OrigAtDataPolymerUnit*     unitk    =    (*ppPolymer)->units[k] = (OrigAtDataPolymerUnit*) inchi_calloc( 1, sizeof(OrigAtDataPolymerUnit) );

            if (!unitk )
            {
                TREAT_ERR( err, 9001, "Out of RAM");
                goto exitf;
            }

            memset( unitk, 0, sizeof( *unitk ) );
            unitk->id                    =    groupk->id;
            unitk->type                    =    groupk->type;
            unitk->subtype                =    groupk->subtype;
            unitk->conn                    =    groupk->conn;
            unitk->label                =    groupk->label;
            unitk->real_kind            =    POLYMER_UNIT_KIND_UNKNOWN;

            for (q=0; q<4; q++)
            {
                unitk->xbr1[q] = groupk->xbr1[q];
                unitk->xbr2[q] = groupk->xbr2[q];
            }
            strcpy( unitk->smt, groupk->smt );
            unitk->na = groupk->alist.used;
            unitk->alist = (int *) inchi_calloc( unitk->na, sizeof(int) );
            if (!unitk->alist )
            {
                TREAT_ERR( err, 9001, "Out of RAM");
                goto exitf;
            }
            for (m=0; m<unitk->na; m++)
            {
                unitk->alist[m] = groupk->alist.item[m];
            }
            unitk->nb = groupk->blist.used;
            if ( unitk->nb > 0 )
            {
                unitk->blist = (int *) inchi_calloc( 2*unitk->nb, sizeof(int) );
                if (!unitk->blist )
                {
                    TREAT_ERR( err, 9001, "Out of RAM");
                    goto exitf;
                }
                for (m=0; m < groupk->blist.used; m++)
                {
                    int ib, ia1, ia2;
                    ib = groupk->blist.item[m];
                    if ( ib<1 || ib>mfdata->ctab.n_bonds )
                    {
                        TREAT_ERR( err, 9004, "Polymer unit in <olfile refers to invalid bond");
                        goto exitf;
                    }
                    ia1 = mfdata->ctab.bonds[ib-1].atnum1;
                    ia2 = mfdata->ctab.bonds[ib-1].atnum2;
                    unitk->blist[2*m]    = ia1;
                    unitk->blist[2*m+1]    = ia2;
                    if ( !strcmp(mfdata->ctab.atoms[ia1-1].symbol,"H") ||
                         !strcmp(mfdata->ctab.atoms[ia2-1].symbol,"H")  )
                    {
                        TREAT_ERR ( err, 9002, "Hydrogen as polymer end group is not supported");
                        goto exitf;
                    }
                }
            }
            else
                unitk->blist = NULL;
        }
    }

    /* V3000 Extensions */
    if ( mfdata->ctab.v3000 )
    {
        int m, k, nn;
        MOL_FMT_v3000 *mpv        = mfdata->ctab.v3000;

        *ppV3000    = (OrigAtDataV3000 *) inchi_calloc( 1, sizeof(OrigAtDataV3000) );
        pv            = *ppV3000;
        if ( !pv )
        {
            TREAT_ERR( err, 9001, "Out of RAM");
            goto exitf;
        }
        memset( pv, 0, sizeof(*pv) );


        pv->n_collections        = mpv->n_collections;
        pv->n_haptic_bonds        = mpv->n_haptic_bonds;
        pv->n_non_haptic_bonds    = mpv->n_non_haptic_bonds;
        pv->n_sgroups            = mpv->n_sgroups;
        pv->n_non_star_atoms    = mpv->n_non_star_atoms;
        pv->n_star_atoms        = mpv->n_star_atoms;
        pv->n_steabs            = mpv->n_steabs;
        pv->n_sterac            = mpv->n_sterac;
        pv->n_sterel            = mpv->n_sterel;
        pv->n_3d_constraints    = mpv->n_3d_constraints;

        if ( mpv->atom_index_orig )
        {
            pv->atom_index_orig = (int *) inchi_calloc( mfdata->ctab.n_atoms, sizeof(int) );
            if ( NULL==pv->atom_index_orig )
            {
                TREAT_ERR( err, 9001, "Out of RAM");
                goto exitf;
            }
            memcpy( pv->atom_index_orig, mpv->atom_index_orig, mfdata->ctab.n_atoms);
        }
        if ( mpv->atom_index_fin )
        {
            pv->atom_index_fin = (int *) inchi_calloc( mfdata->ctab.n_atoms, sizeof(int) );
            if ( NULL==pv->atom_index_fin )
            {
                TREAT_ERR( err, 9001, "Out of RAM");
                goto exitf;
            }
            memcpy( pv->atom_index_fin, mpv->atom_index_fin, mfdata->ctab.n_atoms);
        }
        if ( mpv->n_haptic_bonds && mpv->haptic_bonds )
        {
            pv->lists_haptic_bonds                =    (int **) calloc( mpv->n_haptic_bonds, sizeof (int*) );
            if ( NULL==pv->lists_haptic_bonds )        { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; }
            for (m=0; m<mpv->n_haptic_bonds; m++)
            {
                int *lst=NULL;
                int *mol_lst = mpv->haptic_bonds->lists[m];
                nn = mol_lst[2] + 3;
                lst = pv->lists_haptic_bonds[m] =    (int *) calloc( nn, sizeof (int) );
                if ( NULL==lst )                    { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; }
                for (k=0; k<nn; k++)                lst[k] = mol_lst[k];
            }
        }
        if ( mpv->n_steabs && mpv->steabs )
        {
            pv->lists_steabs                =    (int **) calloc( mpv->n_steabs, sizeof (int*) );
            if ( NULL==pv->lists_steabs )        { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf;  }
            for (m=0; m<mpv->n_steabs; m++)
            {
                int *lst=NULL;
                int *mol_lst = mpv->steabs->lists[m];
                nn = mol_lst[1] + 2;
                lst = pv->lists_steabs[m] =        (int *) calloc( nn, sizeof (int) );
                if ( NULL==lst )                { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; }
                for (k=0; k<nn; k++)            lst[k] = mol_lst[k];
            }
        }
        if ( mpv->n_sterac && mpv->sterac )
        {
            pv->lists_sterac                =    (int **) calloc( mpv->n_sterac, sizeof (int*) );
            if ( NULL==pv->lists_sterac )        { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf;  }
            for (m=0; m<mpv->n_sterac; m++)
            {
                int *lst=NULL;
                int *mol_lst = mpv->sterac->lists[m];
                nn = mol_lst[1] + 2;
                lst = pv->lists_sterac[m] =        (int *) calloc( nn, sizeof (int) );
                if ( NULL==lst )                { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; }
                for (k=0; k<nn; k++)            lst[k] = mol_lst[k];
            }
        }
        if ( mpv->n_sterel && mpv->sterel )
        {
            pv->lists_sterel                =    (int **) calloc( mpv->n_sterel, sizeof (int*) );
            if ( NULL==pv->lists_sterel )        { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf;  }
            for (m=0; m<mpv->n_sterel; m++)
            {
                int *lst=NULL;
                int *mol_lst = mpv->sterel->lists[m];
                nn = mol_lst[1] + 2;
                lst = pv->lists_sterel[m] =        (int *) calloc( nn, sizeof (int) );
                if ( NULL==lst )                { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; }
                for (k=0; k<nn; k++)            lst[k] = mol_lst[k];
            }
        }
    }


exitf:
    if ( err )
    {
        FreeExtOrigAtData ( (*ppPolymer), pv );
        *ppPolymer = NULL;
    }
    return err;
}
