~ubuntu-branches/debian/stretch/openbabel/stretch

« back to all changes in this revision

Viewing changes to src/formats/inchi102/ichirvr7.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Leidert (dale)
  • Date: 2009-07-17 00:18:06 UTC
  • mfrom: (6.1.1 sid)
  • Revision ID: james.westby@ubuntu.com-20090717001806-sy3mzs3e1d1adbs9
Tags: 2.2.2-2
* debian/control (Uploaders): Removed LI Daobing. Thanks for your work!
  (Standards-Version): Bumped to 3.8.2.
  (Vcs-Svn): Fixed vcs-field-uses-not-recommended-uri-format.
* debian/patches/537102_fix_tr1_memory_detection.patch: Added.
  - configure.in, configure, src/config.h.in: Fix detection of tr1/memory to
    prevent building the package with boost (closes: #537102).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * International Chemical Identifier (InChI)
 
3
 * Version 1
 
4
 * Software version 1.02
 
5
 * October 31, 2008
 
6
 * Developed at NIST
 
7
 *
 
8
 * The InChI library and programs are free software developed under the
 
9
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC);
 
10
 * you can redistribute this software and/or modify it under the terms of 
 
11
 * the GNU Lesser General Public License as published by the Free Software 
 
12
 * Foundation:
 
13
 * http://www.opensource.org/licenses/lgpl-license.php
 
14
 */
 
15
 
 
16
 
 
17
#include <stdio.h>
 
18
#include <stdlib.h>
 
19
#include <string.h>
 
20
#include <ctype.h>
 
21
#include <math.h>
 
22
 
 
23
/*^^^ */
 
24
/* #define CHECK_WIN32_VC_HEAP */
 
25
#include "mode.h"
 
26
 
 
27
#if( READ_INCHI_STRING == 1 )
 
28
 
 
29
#include "ichicomp.h"
 
30
#include "ichi.h"
 
31
#include "ichitime.h"
 
32
#include "ichierr.h"
 
33
#include "util.h"
 
34
#include "strutil.h"
 
35
 
 
36
/* reverse InChI */
 
37
#include "ichimain.h"
 
38
#include "extr_ct.h"
 
39
#include "ichitaut.h"
 
40
#include "ichister.h"
 
41
#include "strutil.h"
 
42
#include "ichisize.h"
 
43
#include "ichiring.h"
 
44
#include "ichinorm.h"
 
45
 
 
46
#include "ichirvrs.h"
 
47
#include "inchicmp.h"
 
48
 
 
49
/******************************************************************************************************/
 
50
int InChI2Atom( ICHICONST INPUT_PARMS *ip,  STRUCT_DATA *sd, const char *szCurHdr, long num_inp,
 
51
                StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int  bI2A_Flag, int bHasSomeFixedH, InpInChI *OneInput)
 
52
{
 
53
    int           iINChI   = (bI2A_Flag & I2A_FLAG_RECMET)? INCHI_REC : INCHI_BAS;
 
54
    int           bMobileH = (bI2A_Flag & I2A_FLAG_FIXEDH)? TAUT_NON  : TAUT_YES;
 
55
    INChI        *pInChI[TAUT_NUM];
 
56
    int           ret = 0;
 
57
 
 
58
    memset( pInChI, 0, sizeof(pInChI) );
 
59
    /* disconnected or reconnected */
 
60
    if ( iINChI == INCHI_REC ) {
 
61
        if ( !OneInput->nNumComponents[iINChI][TAUT_YES] ) {
 
62
            iINChI = INCHI_BAS;
 
63
        }
 
64
    }
 
65
    if ( iComponent >= OneInput->nNumComponents[iINChI][TAUT_YES] ) {
 
66
        return 0; /* component does not exist */
 
67
    }
 
68
    /* mobile or fixed H */
 
69
    pStruct->bFixedHExists = 0;
 
70
    if ( bMobileH == TAUT_NON ) {
 
71
        if ( !OneInput->nNumComponents[iINChI][bMobileH] ) {
 
72
            /* only one InChI exists (no mobile H) */
 
73
            bMobileH = TAUT_YES;
 
74
        }
 
75
    } 
 
76
    
 
77
    if ( iComponent >= OneInput->nNumComponents[iINChI][bMobileH] ) {
 
78
        return 0; /* component does not exist */
 
79
    }
 
80
    /* pointer to the InChI that is going to be reversed */
 
81
    pInChI[0] = &OneInput->pInpInChI[iINChI][bMobileH][iComponent];
 
82
    pStruct->bMobileH = bMobileH;
 
83
    pStruct->iINCHI   = iINChI;
 
84
    /* deleted component only in case Mobile-H and compound contains only protons */
 
85
    if ( pInChI[0]->bDeleted ) {
 
86
        return 0; /* deleted component, presumably H(+) */
 
87
    }
 
88
 
 
89
    if ( bMobileH == TAUT_NON && OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) {
 
90
        pStruct->nNumRemovedProtonsMobHInChI = 
 
91
            OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons[iComponent].nNumRemovedProtons;
 
92
    }
 
93
 
 
94
    if ( bMobileH == TAUT_NON || bMobileH == TAUT_YES && OneInput->pInpInChI[iINChI][TAUT_NON] &&
 
95
         OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].nNumberOfAtoms > 0 &&
 
96
        !OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].bDeleted ) {
 
97
        pStruct->bFixedHExists = 1;
 
98
    }
 
99
    if ( bMobileH == TAUT_NON && iComponent < OneInput->nNumComponents[iINChI][TAUT_YES] &&
 
100
         OneInput->pInpInChI[iINChI][TAUT_YES] &&
 
101
         OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].nNumberOfAtoms > 0 &&
 
102
         !OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].bDeleted ) {
 
103
        /* pointer to the Mobile-H InChI if we are reversing Fixed-H InChI */
 
104
        pInChI[1] = &OneInput->pInpInChI[iINChI][TAUT_YES][iComponent];
 
105
    }
 
106
    pStruct->num_inp_actual = OneInput->num_inp;
 
107
    ret = OneInChI2Atom( ip, sd, szCurHdr, num_inp, pStruct, iComponent, iAtNoOffset, bHasSomeFixedH, pInChI);
 
108
    return ret; /* same interpretation as in ProcessOneStructure ??? */
 
109
}
 
110
 
 
111
/*******************************************************************/
 
112
void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput )
 
113
{
 
114
    int iInchiRec, cur_num_comp, k;
 
115
    /* eliminate Fixed-H InChI that are exactly came as the corresponding Mobile-H structures */
 
116
    for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) {
 
117
        cur_num_comp = inchi_min(pOneInput->nNumComponents[iInchiRec][TAUT_YES],
 
118
                                 pOneInput->nNumComponents[iInchiRec][TAUT_NON]);
 
119
        for ( k = 0; k < cur_num_comp; k ++ ) {
 
120
            if ( !CompareReversedINChI( pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k,
 
121
                                        pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, NULL, NULL ) ) {
 
122
                Free_INChI_Members( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k );
 
123
                memset( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, 0, sizeof(pOneInput->pInpInChI[0][0][0]) );
 
124
            }
 
125
        }
 
126
    }
 
127
}
 
128
/*******************************************************************/
 
129
int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput )
 
130
{
 
131
    /* mark Disconnected InChI components that are exactly came as Reconnected ones */
 
132
    /* Disconnected will have a negative number of the reconnected component */
 
133
    /* Reconnected will have a positive number of the disconnected component */
 
134
    int k1, k2, num_marked = 0;
 
135
    for ( k1 = 0; k1 < inchi_max(pOneInput->nNumComponents[INCHI_BAS][TAUT_YES],
 
136
                                 pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]); k1 ++ ) {
 
137
    for ( k2 = 0; k2 < inchi_max(pOneInput->nNumComponents[INCHI_REC][TAUT_YES],
 
138
                                 pOneInput->nNumComponents[INCHI_REC][TAUT_NON]); k2 ++ ) {
 
139
        int eqM = ( k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] &&
 
140
                    k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_YES] &&
 
141
                   !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink && /* already linked */
 
142
                   !pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].bDeleted &&
 
143
                    pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms &&
 
144
                    pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms ==
 
145
                    pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nNumberOfAtoms &&
 
146
                   !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].bDeleted &&
 
147
                    !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k2,
 
148
                                           pOneInput->pInpInChI[INCHI_BAS][TAUT_YES]+k1,
 
149
                                           NULL, NULL ));
 
150
        int isF1 = (k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] &&
 
151
                    0 == pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].bDeleted &&
 
152
                    0  < pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms );
 
153
        int isF2 = (k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_NON] &&
 
154
                    0 == pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].bDeleted &&
 
155
                    0  < pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms );
 
156
        int eqF =  isF1 && isF2 &&
 
157
                   !pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink &&
 
158
                    pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms ==
 
159
                    pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms &&
 
160
                    !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_NON]+k2,
 
161
                                           pOneInput->pInpInChI[INCHI_BAS][TAUT_NON]+k1,
 
162
                                           NULL, NULL );
 
163
        if ( eqM && (!isF1 && !isF2 || eqF ) ) {
 
164
            pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nLink = -(k2+1);
 
165
            pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink =  (k1+1);
 
166
            if ( eqF ) {
 
167
                pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nLink = -(k2+1);
 
168
                pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink =  (k1+1);
 
169
            }
 
170
            num_marked ++;
 
171
            break; /* equal InChI has been deleted from the disconnected layer, get next k1 */
 
172
        }
 
173
    }
 
174
    }
 
175
    return num_marked;
 
176
 
 
177
}
 
178
/**************************************************************/
 
179
void SetUpSrm( SRM *pSrm )
 
180
{
 
181
    /* structure restore parms !!!!! */
 
182
    memset( pSrm, 0, sizeof(pSrm[0]) );
 
183
    pSrm->bFixStereoBonds      = FIX_STEREO_BOND_ORDER;
 
184
    pSrm->nMetal2EndpointMinBondOrder  = 1;
 
185
    pSrm->nMetal2EndpointInitEdgeFlow  = 0;
 
186
    if ( METAL_FREE_CHARGE_VAL == 1 ) { 
 
187
        pSrm->bMetalAddFlower      = 1;
 
188
        /* the next 3 parameters: */
 
189
        /* 0, 0, 0 => all bonds 0, no init radical on metal */
 
190
        /* 0, 0, 1 => all bonds 0,    init radical on metal */
 
191
        /* 0, 1, 0 => wrong */
 
192
        /* 0, 1, 1 => all bonds 1, no init radical on metal */
 
193
        /* 1, 0, 1 => min bond order 1, all bonds to metal have order 1 */
 
194
        /* 1, 1, 0 => wrong */
 
195
        /* 1, 1, 1 => wrong */
 
196
        pSrm->nMetalMinBondOrder   = 0; 
 
197
        pSrm->nMetalInitEdgeFlow   = 1;  
 
198
        pSrm->nMetalInitBondOrder  = 1; 
 
199
        pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds;
 
200
        pSrm->nMetalFlowerParam_D     = 16;
 
201
        pSrm->nMetalMaxCharge_D       = 16;
 
202
    } else {
 
203
        pSrm->bMetalAddFlower      = 0;
 
204
        pSrm->nMetalMinBondOrder   = 1;
 
205
        pSrm->nMetalInitEdgeFlow   = 0;
 
206
        pSrm->nMetalInitBondOrder  = 1;
 
207
        pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds;
 
208
        pSrm->nMetalFlowerParam_D     = 16;
 
209
        pSrm->nMetalMaxCharge_D       = 0;
 
210
    }
 
211
    /*
 
212
    pSrm->nMetalInitBondOrder  = pSrm->nMetalMinBondOrder 
 
213
                             + pSrm->nMetalInitEdgeFlow;
 
214
    */
 
215
    pSrm->nMetal2EndpointInitBondOrder = pSrm->nMetal2EndpointMinBondOrder 
 
216
                                     + pSrm->nMetal2EndpointInitEdgeFlow;
 
217
 
 
218
}
 
219
/**************************************************************************************/
 
220
int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr,
 
221
                         ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM],
 
222
                         InpInChI *pOneInput )
 
223
{
 
224
    int iInchiRec, iMobileH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH;
 
225
    int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp;
 
226
    int *nAtomOffs=NULL, *nDelHOffs=NULL;
 
227
    StrFromINChI *pStruct1;
 
228
    inp_ATOM *at=NULL, *a;
 
229
 
 
230
    ret = 0;
 
231
    pOneInput->num_atoms = 0;
 
232
    /* select highest detail level */
 
233
    if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) {
 
234
        iInchiRec = INCHI_REC;
 
235
        iMobileH  = TAUT_NON;
 
236
    } else
 
237
    if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) {
 
238
        iInchiRec = INCHI_REC;
 
239
        iMobileH  = TAUT_YES;
 
240
    } else
 
241
    if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) {
 
242
        iInchiRec = INCHI_BAS;
 
243
        iMobileH  = TAUT_NON;
 
244
    } else
 
245
    if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) {
 
246
        iInchiRec = INCHI_BAS;
 
247
        iMobileH  = TAUT_YES;
 
248
    } else {
 
249
        return 0; /* no components available */
 
250
    }
 
251
 
 
252
    nAtomOffs = (int*)inchi_malloc((num_components+1) * sizeof(nAtomOffs[0]));
 
253
    nDelHOffs = (int*)inchi_malloc((num_components+1) * sizeof(nDelHOffs[0]));
 
254
    if ( !nAtomOffs || !nDelHOffs ) {
 
255
        ret = RI_ERR_ALLOC;
 
256
        goto exit_function;
 
257
    }
 
258
    /* count number of atoms and removed H */
 
259
    tot_just_atoms = tot_removed_H = tot_atoms = 0;
 
260
    iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1;
 
261
    nAtomOffs[0] = nDelHOffs[0] = 0;
 
262
    for ( k = 0; k < num_components; k ++ ) {
 
263
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k :
 
264
                   iAlternH>=0 &&
 
265
                   pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL;
 
266
        if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms || pStruct1->bDeleted ) {
 
267
            cur_nA = cur_nH = 0;
 
268
        } else {
 
269
            cur_nA = pStruct1->num_atoms;
 
270
            cur_nH = pStruct1->num_deleted_H;
 
271
        }
 
272
        nAtomOffs[k+1] = nAtomOffs[k] + cur_nA;
 
273
        nDelHOffs[k+1] = nDelHOffs[k] + cur_nH;
 
274
    }
 
275
    tot_just_atoms = nAtomOffs[num_components];
 
276
    /* shift all H to the end */
 
277
    for ( k = 0; k <= num_components; k ++ ) {
 
278
        nDelHOffs[k] += tot_just_atoms;
 
279
    }
 
280
    tot_atoms = nDelHOffs[num_components];
 
281
 
 
282
    /* merge atoms together: 1. Allocate */
 
283
    if ( NULL == (at = (inp_ATOM *)inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) {
 
284
        ret = RI_ERR_ALLOC;
 
285
        goto exit_function;
 
286
    }
 
287
    if ( !tot_atoms ) {
 
288
        ret = 0;
 
289
        goto exit_function; /* empty structure */
 
290
    }
 
291
    /* merge atoms together: 2. Copy */
 
292
    for ( k = 0; k < num_components; k ++ ) {
 
293
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k :
 
294
                   iAlternH>=0 &&
 
295
                   pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL;
 
296
        if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) {
 
297
            memcpy( at + nAtomOffs[k], pStruct1->at2, len * sizeof(at[0]) );
 
298
            if ( len2 = nDelHOffs[k+1] - nDelHOffs[k] ) {
 
299
                memcpy( at + nDelHOffs[k], pStruct1->at2+len, len2 * sizeof(at[0]) );
 
300
            }
 
301
        }
 
302
    }
 
303
    /* merge atoms together: 3. Update atom numbers */
 
304
    icomp = 0;
 
305
    for ( k = 0; k < num_components; k ++ ) {
 
306
        iCurAtomOffs = nAtomOffs[k];
 
307
        iNxtAtomOffs = nAtomOffs[k+1];
 
308
        iCurDelHOffs = nDelHOffs[k];
 
309
        iNxtDelHOffs = nDelHOffs[k+1];
 
310
        len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */
 
311
        iShiftH      = iCurDelHOffs - len;
 
312
        if ( !len ) {
 
313
            continue;
 
314
        }
 
315
        icomp ++; /* current component number */
 
316
        /* update atoms */
 
317
        for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) {
 
318
            
 
319
            a = at+i;
 
320
 
 
321
            a->endpoint = 0;
 
322
            a->bAmbiguousStereo = 0;
 
323
            a->at_type = 0;
 
324
            a->bCutVertex = 0;
 
325
            a->bUsed0DParity = 0;
 
326
            a->cFlags = 0;
 
327
            a->nBlockSystem = 0;
 
328
            a->nNumAtInRingSystem = 0;
 
329
            a->nRingSystem = 0;
 
330
           
 
331
            for ( j = 0; j < a->valence; j ++ ) {
 
332
                if ( a->neighbor[j] < len ) {
 
333
                    a->neighbor[j] += iCurAtomOffs; /* atom */
 
334
                } else {
 
335
                    a->neighbor[j] += iShiftH;      /* explicit H */
 
336
                }
 
337
            }
 
338
            a->orig_at_number += iCurAtomOffs;
 
339
            a->component = icomp;
 
340
            if ( a->p_parity ) {
 
341
                for ( j = 0; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) {
 
342
                    if ( a->p_orig_at_num[j] <= len ) {
 
343
                        /* originally, orig_at_num = atom_index+1, therefore <= instead of < */
 
344
                        a->p_orig_at_num[j] += iCurAtomOffs;
 
345
                    } else {
 
346
                        a->p_orig_at_num[j] += iShiftH;
 
347
                    }
 
348
                }
 
349
            }
 
350
            for ( j = 0; j < MAX_NUM_STEREO_BONDS && a->sb_parity[j]; j ++ ) {
 
351
                if ( a->sn_orig_at_num[j] <= len ) {
 
352
                    /* originally, orig_at_num = atom_index+1, therefore <= instead of < */
 
353
                    a->sn_orig_at_num[j] += iCurAtomOffs;
 
354
                } else {
 
355
                    a->sn_orig_at_num[j] += iShiftH;
 
356
                }
 
357
            }
 
358
        }
 
359
        /* update fixed-H */
 
360
        for ( i = iCurDelHOffs; i < iNxtDelHOffs; i ++ ) {
 
361
            a = at+i;
 
362
            a->neighbor[0]    += iCurAtomOffs;
 
363
            a->orig_at_number += iShiftH;
 
364
 
 
365
        }
 
366
    }
 
367
    /* save the results */
 
368
    pOneInput->atom      = at;
 
369
    pOneInput->num_atoms = tot_atoms;
 
370
    at = NULL;
 
371
 
 
372
exit_function:
 
373
    if ( at )        inchi_free( at );  /* in case of failure */
 
374
    if ( nAtomOffs ) inchi_free( nAtomOffs );
 
375
    if ( nDelHOffs ) inchi_free( nDelHOffs );
 
376
    return ret;
 
377
}
 
378
#ifndef INCHI_ANSI_ONLY
 
379
static PER_DRAW_PARMS pdp;
 
380
/******************************************************************************************************/
 
381
int DisplayAllRestoredComponents( inp_ATOM *at, int num_at, const char *szCurHdr )
 
382
{
 
383
    int    ret;
 
384
    char     szTitle[512];
 
385
    DRAW_PARMS dp;
 
386
    TBL_DRAW_PARMS tdp;
 
387
    if ( num_at <= 0 ) {
 
388
        return 0;
 
389
    }
 
390
    memset( &dp, 0, sizeof(dp));
 
391
    memset( &tdp, 0, sizeof(tdp) );
 
392
    //memset( &pdp, 0, sizeof(pdp) );
 
393
    dp.sdp.tdp       = &tdp;
 
394
    dp.pdp           = &pdp;
 
395
    dp.sdp.nFontSize = -9;
 
396
    sprintf( szTitle, "All Components of Restored %s Structure", szCurHdr? szCurHdr : "(No structure name)");
 
397
    ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/,
 
398
                      0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/,
 
399
                      1 /*int bIsotopic*/, 0 /*bTautomeric*/,
 
400
                      NULL /* pINChI */, NULL /* INChI_Aux **cur_INChI_Aux*/,
 
401
                      0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle );
 
402
    return 0;
 
403
}
 
404
/******************************************************************************************************/
 
405
int DisplayOneRestoredComponent( StrFromINChI *pStruct, inp_ATOM *at,
 
406
                                 int iComponent, int nNumComponents, int bMobileH,
 
407
                                 const char *szCurHdr )
 
408
{
 
409
    int    ret, k;
 
410
    int    num_at        = pStruct->num_atoms;
 
411
    XYZ_COORD *pxyz      = pStruct->pXYZ;
 
412
    char     szTitle[512];
 
413
    DRAW_PARMS dp;
 
414
    TBL_DRAW_PARMS tdp;
 
415
    int         iInchiRec = pStruct->iInchiRec;
 
416
    int         iMobileH  = pStruct->iMobileH;
 
417
    INChI     **pInChI = NULL;
 
418
    INChI_Aux **pAux   = NULL;
 
419
    int         nNumRemovedProtons         = pAux? pAux[iMobileH]->nNumRemovedProtons : 0;
 
420
    NUM_H      *nNumRemovedProtonsIsotopic = pAux? pAux[iMobileH]->nNumRemovedIsotopicH : NULL;
 
421
 
 
422
 
 
423
    if ( num_at <= 0 || !pxyz ) {
 
424
        return 0;
 
425
    }
 
426
    if ( iInchiRec && !pStruct->RevInChI.pINChI_Aux[iInchiRec][0] ) {
 
427
        iInchiRec = 0;
 
428
    }
 
429
    k = iMobileH;
 
430
    if ( !bRevInchiComponentExists( pStruct, iInchiRec, k, 0 ) ) {
 
431
        k = ALT_TAUT(k);
 
432
    }
 
433
    pInChI = pStruct->RevInChI.pINChI[iInchiRec][0];
 
434
    pAux   = pStruct->RevInChI.pINChI_Aux[iInchiRec][0];
 
435
    
 
436
 
 
437
    memset( &dp, 0, sizeof(dp));
 
438
    memset( &tdp, 0, sizeof(tdp) );
 
439
    //memset( &pdp, 0, sizeof(pdp) );
 
440
    dp.sdp.tdp       = &tdp;
 
441
    dp.pdp           = &pdp;
 
442
    dp.sdp.nFontSize = -9;
 
443
    sprintf( szTitle, "Restored %s Component %d of %d %c%c",
 
444
                      szCurHdr? szCurHdr : "(No structure name)", iComponent+1, nNumComponents,
 
445
                      pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' );
 
446
    ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/,
 
447
                      nNumRemovedProtons, /*NULL*/ nNumRemovedProtonsIsotopic,
 
448
                      1 /*int bIsotopic*/, k,
 
449
                      pInChI, pAux,
 
450
                      0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle );
 
451
    return 0;
 
452
}
 
453
/******************************************************************************************************/
 
454
int DisplayRestoredComponent( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr )
 
455
{
 
456
    int    i, ret;
 
457
    int    num_at        = pStruct->num_atoms;
 
458
    int    num_deleted_H = pStruct->num_deleted_H;
 
459
    inp_ATOM *atom       = pStruct->at2;
 
460
    XYZ_COORD *pxyz      = pStruct->pXYZ;
 
461
    inp_ATOM *at         = NULL;
 
462
    char     szTitle[512];
 
463
    DRAW_PARMS dp;
 
464
    TBL_DRAW_PARMS tdp;
 
465
    if ( !atom || num_at <= 0 || !pxyz ) {
 
466
        return 0;
 
467
    }
 
468
    at = (inp_ATOM *)inchi_calloc( num_at + num_deleted_H, sizeof(at[0]) );
 
469
    if ( !at ) {
 
470
        return RI_ERR_ALLOC;
 
471
    }
 
472
    memcpy( at, atom, (num_at + num_deleted_H) * sizeof(at[0]) );
 
473
    for ( i = 0; i < num_at; i ++ ) {
 
474
        at[i].x = pxyz[i].xyz[0]; 
 
475
        at[i].y = pxyz[i].xyz[1]; 
 
476
        at[i].z = pxyz[i].xyz[2]; 
 
477
    }
 
478
    memset( &dp, 0, sizeof(dp));
 
479
    memset( &tdp, 0, sizeof(tdp) );
 
480
    //memset( &pdp, 0, sizeof(pdp) );
 
481
    dp.sdp.tdp       = &tdp;
 
482
    dp.pdp           = &pdp;
 
483
    dp.sdp.nFontSize = -9;
 
484
    sprintf( szTitle, "DBG Restored %s Component %d %c%c", szCurHdr? szCurHdr : "(No structure name)", iComponent+1, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' );
 
485
    ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/,
 
486
                      0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/,
 
487
                      1 /*int bIsotopic*/, 0 /*bTautomeric*/,
 
488
                      &pInChI, NULL /* INChI_Aux **cur_INChI_Aux*/,
 
489
                      0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle );
 
490
    inchi_free( at );
 
491
    return 0;
 
492
}
 
493
/**************************************************************************************/
 
494
int DisplayStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr,
 
495
                         ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM],
 
496
                         InpInChI *pOneInput )
 
497
{
 
498
    int iInchiRec, iMobileH, iCurMobH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH;
 
499
    int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp;
 
500
    int *nAtomOffs=NULL, *nDelHOffs=NULL, bNoCoord=0, iNewCoord=0, nNewCoord=0;
 
501
    double x_max=-1.0e16, x_min = 1.0e16, y_max=-1.0e16, y_min=1.0e16, delta = 0.0;
 
502
    StrFromINChI *pStruct1;
 
503
    inp_ATOM *at=NULL, *a;
 
504
 
 
505
    if (!ip->bDisplayCompositeResults && !ip->bDisplay ) {
 
506
        return 0;
 
507
    }
 
508
 
 
509
    ret = 0;
 
510
    pOneInput->num_atoms = 0;
 
511
    /* select highest detail level */
 
512
    if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) {
 
513
        iInchiRec = INCHI_REC;
 
514
        iMobileH  = TAUT_NON;
 
515
    } else
 
516
    if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) {
 
517
        iInchiRec = INCHI_REC;
 
518
        iMobileH  = TAUT_YES;
 
519
    } else
 
520
    if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) {
 
521
        iInchiRec = INCHI_BAS;
 
522
        iMobileH  = TAUT_NON;
 
523
    } else
 
524
    if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) {
 
525
        iInchiRec = INCHI_BAS;
 
526
        iMobileH  = TAUT_YES;
 
527
    } else {
 
528
        return 0; /* no components available */
 
529
    }
 
530
    for ( k = 0; k < num_components; k ++ ) {
 
531
        if ( pStruct[iInchiRec][iMobileH][k].bDeleted )
 
532
            break;
 
533
    }
 
534
    num_components = k;
 
535
 
 
536
    nAtomOffs = (int*)inchi_malloc((num_components+1) * sizeof(nAtomOffs[0]));
 
537
    nDelHOffs = (int*)inchi_malloc((num_components+1) * sizeof(nDelHOffs[0]));
 
538
    if ( !nAtomOffs || !nDelHOffs ) {
 
539
        ret = RI_ERR_ALLOC;
 
540
        goto exit_function;
 
541
    }
 
542
    /* count number of atoms and removed H */
 
543
    tot_just_atoms = tot_removed_H = tot_atoms = 0;
 
544
    iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1;
 
545
    nAtomOffs[0] = nDelHOffs[0] = 0;
 
546
    for ( k = 0; k < num_components; k ++ ) {
 
547
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k :
 
548
                   iAlternH>=0 &&
 
549
                   pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL;
 
550
        if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms ) {
 
551
            cur_nA = cur_nH = 0;
 
552
        } else {
 
553
            cur_nA = pStruct1->num_atoms;
 
554
            cur_nH = pStruct1->num_deleted_H;
 
555
            if ( cur_nA && !pStruct1->pXYZ ) {
 
556
                if ( !k ) {
 
557
                    ret = 0; /* no coordinates available */
 
558
                    goto exit_function;
 
559
                } else {
 
560
                    bNoCoord ++;
 
561
                }
 
562
            }
 
563
        }
 
564
        nAtomOffs[k+1] = nAtomOffs[k] + cur_nA;
 
565
        nDelHOffs[k+1] = nDelHOffs[k] + cur_nH;
 
566
    }
 
567
    tot_just_atoms = nAtomOffs[num_components];
 
568
    /* shift all H to the end */
 
569
    for ( k = 0; k <= num_components; k ++ ) {
 
570
        nDelHOffs[k] += tot_just_atoms;
 
571
    }
 
572
    tot_atoms = nDelHOffs[num_components];
 
573
 
 
574
    /* merge atoms together: 1. Allocate */
 
575
    if ( NULL == (at = (inp_ATOM *)inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) {
 
576
        ret = RI_ERR_ALLOC;
 
577
        goto exit_function;
 
578
    }
 
579
    if ( !tot_atoms ) {
 
580
        ret = 0;
 
581
        goto exit_function; /* empty structure */
 
582
    }
 
583
    /* merge atoms together: 2. Copy */
 
584
    for ( k = 0; k < num_components; k ++ ) {
 
585
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iMobileH]+k :
 
586
                   iAlternH>=0 &&
 
587
                   pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iAlternH]+k : NULL;
 
588
        if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) {
 
589
            XYZ_COORD *pxyz = pStruct1->pXYZ;
 
590
            len2 = nDelHOffs[k+1] - nDelHOffs[k]; /* do not separate H from the atom: we will not need them */
 
591
            iCurAtomOffs = nAtomOffs[k];
 
592
            a = at + iCurAtomOffs;
 
593
            memcpy( a, pStruct1->at2, (len+len2) * sizeof(at[0]) );
 
594
            DisconnectedConnectedH( a, len, len2 );
 
595
            if ( pxyz ) {
 
596
                for ( i = 0; i < len; i ++ ) {
 
597
                    a[i].x = pxyz[i].xyz[0];
 
598
                    x_max = inchi_max( x_max, pxyz[i].xyz[0] );
 
599
                    x_min = inchi_min( x_min, pxyz[i].xyz[0] );
 
600
                    a[i].y = pxyz[i].xyz[1];
 
601
                    y_max = inchi_max( y_max, pxyz[i].xyz[1] );
 
602
                    y_min = inchi_min( y_min, pxyz[i].xyz[1] );
 
603
                    a[i].z = pxyz[i].xyz[2];
 
604
                    nNewCoord ++;
 
605
                }
 
606
            } else {
 
607
                if ( !iNewCoord ) {
 
608
                    if ( !nNewCoord ) {
 
609
                        ret = 0;
 
610
                        goto exit_function; /* empty structure */
 
611
                    }
 
612
                    delta = inchi_max(x_max - x_min, y_max - y_min);
 
613
                    if ( delta == 0.0 ) {
 
614
                        delta = 0.5 * (x_max+x_min);
 
615
                        if ( delta == 0.0 )
 
616
                            delta = 1.0;
 
617
                    } else {
 
618
                        delta /= sqrt( (double)(nNewCoord+1) );
 
619
                    }
 
620
                }
 
621
                for ( i = 0; i < len; i ++ ) {
 
622
                    a[i].x = x_max + delta;
 
623
                    a[i].y = y_max - iNewCoord * delta;
 
624
                    a[i].z = 0.0;
 
625
                    iNewCoord ++;
 
626
                }
 
627
                if ( pStruct1->pXYZ = (XYZ_COORD *)inchi_calloc(len, sizeof(pStruct1->pXYZ[0]) ) ) {
 
628
 
 
629
                    for ( i = 0; i < len; i ++ ) {
 
630
                        pStruct1->pXYZ[i].xyz[0] = a[i].x;
 
631
                        pStruct1->pXYZ[i].xyz[1] = a[i].y;
 
632
                        pStruct1->pXYZ[i].xyz[2] = 0.0;
 
633
                    }
 
634
                }
 
635
            }
 
636
            if ( ip->bDisplay || ip->bDisplayCompositeResults && 1 == num_components ) {
 
637
               DisplayOneRestoredComponent( pStruct1, a, k, num_components, iCurMobH, szCurHdr );
 
638
            }
 
639
            if ( !pxyz && pStruct1->pXYZ ) {
 
640
                inchi_free( pStruct1->pXYZ );
 
641
                pStruct1->pXYZ = NULL;
 
642
            }
 
643
        }
 
644
    }
 
645
    /* merge atoms together: 3. Update atom numbers */
 
646
    icomp = 0;
 
647
    if ( ip->bDisplayCompositeResults && num_components > 1 ) {
 
648
        for ( k = 0; k < num_components; k ++ ) {
 
649
            /* display each restored component if requested */
 
650
            iCurAtomOffs = nAtomOffs[k];
 
651
            iNxtAtomOffs = nAtomOffs[k+1];
 
652
            iCurDelHOffs = nDelHOffs[k];
 
653
            iNxtDelHOffs = nDelHOffs[k+1];
 
654
            len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */
 
655
            iShiftH      = iCurDelHOffs - len;
 
656
            if ( !len ) {
 
657
                continue;
 
658
            }
 
659
            icomp ++; /* current component number */
 
660
            /* update atoms */
 
661
            for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) {
 
662
                a = at+i;
 
663
                for ( j = 0; j < a->valence; j ++ ) {
 
664
                    if ( a->neighbor[j] < len ) {
 
665
                        a->neighbor[j] += iCurAtomOffs; /* atom */
 
666
                    } else {
 
667
                        ret = RI_ERR_PROGR;  /* explicit H */
 
668
                        goto exit_function;
 
669
                    }
 
670
                }
 
671
                a->orig_at_number += iCurAtomOffs;
 
672
            }
 
673
        }
 
674
        tot_atoms = nAtomOffs[num_components];
 
675
        DisplayAllRestoredComponents( at, tot_atoms, szCurHdr );
 
676
 
 
677
    }
 
678
 
 
679
exit_function:
 
680
    if ( at )        inchi_free( at );  /* in case of failure */
 
681
    if ( nAtomOffs ) inchi_free( nAtomOffs );
 
682
    if ( nDelHOffs ) inchi_free( nDelHOffs );
 
683
    return ret;
 
684
}
 
685
#endif
 
686
/**************************************************************************************/
 
687
int AllInchiToStructure( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, long num_inp, char *szCurHdr,
 
688
                         ICHICONST SRM *pSrm, int bHasSomeFixedH, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM],
 
689
                         InpInChI *pOneInput )
 
690
{
 
691
    int iInchiRec, iMobileH, cur_num_comp, bCurI2A_Flag, k, ret, num_err;
 
692
    INPUT_PARMS *ip, ip_loc;
 
693
    STRUCT_DATA *sd, sd_loc;
 
694
    long          ulProcessingTime = 0;
 
695
    inchiTime     ulTStart;
 
696
 
 
697
    InchiTimeGet( &ulTStart );
 
698
    ip  = &ip_loc;
 
699
    *ip = *ip_inp;
 
700
    sd  = &sd_loc;
 
701
    memset( sd, 0, sizeof(*sd));
 
702
    sd->ulStructTime = sd_inp->ulStructTime;
 
703
    ret = 0;
 
704
    num_err = 0;
 
705
    for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { /* Disconnected/Connected */
 
706
        for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { /* Mobile/Fixed H */
 
707
            cur_num_comp = pOneInput->nNumComponents[iInchiRec][iMobileH];
 
708
            if ( !cur_num_comp ) {
 
709
                continue;
 
710
            }
 
711
            /* allocate memory for all existing components */
 
712
            pStruct[iInchiRec][iMobileH] = (StrFromINChI *)inchi_calloc( cur_num_comp, sizeof(pStruct[0][0][0]));
 
713
            if ( !pStruct[iInchiRec][iMobileH] ) {
 
714
                ret = RI_ERR_ALLOC;
 
715
                goto exit_error;
 
716
            }
 
717
            /* set conversion mode */
 
718
            bCurI2A_Flag = (iMobileH? 0: I2A_FLAG_FIXEDH) | (iInchiRec? I2A_FLAG_RECMET : 0);
 
719
            if ( iMobileH ) {
 
720
                ip->nMode &= ~REQ_MODE_BASIC;
 
721
            } else {
 
722
                ip->nMode |= REQ_MODE_BASIC;
 
723
            }
 
724
            /* InChI --> structure conversion for all components except duplicated */
 
725
            for ( k = 0; k < cur_num_comp; k ++ ) { /* components */
 
726
                if ( !iMobileH && !pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms ||
 
727
                     pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted ||
 
728
                     pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink < 0 ) {
 
729
 
 
730
                    pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink;
 
731
                    pStruct[iInchiRec][iMobileH][k].bDeleted = pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted;
 
732
                    continue; /* do not create a structure out of an unavailable
 
733
                                 Fixed-H InChI or out of the one present in Reconnected layer */
 
734
#ifdef NEVER  /* a wrong attempt to process deleted components here */
 
735
                    if ( pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink ) {
 
736
                        continue; /* do not create a structure out of an unavailable
 
737
                                     Fixed-H InChI or out of the one present in Reconnected layer */
 
738
                    } else
 
739
                    if ( iMobileH && pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms &&
 
740
                         pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted &&
 
741
                         pOneInput->pInpInChI[iInchiRec][iMobileH][0].bDeleted ) {
 
742
                        /* all components are protons */
 
743
                        ;
 
744
                    } else {
 
745
                        continue;
 
746
                    }
 
747
#endif
 
748
                }
 
749
                if ( bHasSomeFixedH && iMobileH && k < pOneInput->nNumComponents[iInchiRec][TAUT_NON] &&
 
750
                     pOneInput->pInpInChI[iInchiRec][TAUT_NON][k].nNumberOfAtoms ) {
 
751
                    continue; /* do not process Mobile-H if Fixed-H is requested and exists */
 
752
                }
 
753
                pStruct[iInchiRec][iMobileH][k].pSrm      = pSrm;
 
754
                pStruct[iInchiRec][iMobileH][k].iInchiRec = iInchiRec;
 
755
                pStruct[iInchiRec][iMobileH][k].iMobileH  = iMobileH;
 
756
 
 
757
                /****************************************************/
 
758
                /*                                                  */
 
759
                /* Convert InChI of one component into a Structure  */
 
760
                /*                                                  */
 
761
                /****************************************************/
 
762
 
 
763
                ret = InChI2Atom( ip, sd, szCurHdr, num_inp, pStruct[iInchiRec][iMobileH]+k, k,
 
764
                                   0 /* AtNoOffset*/, bCurI2A_Flag, bHasSomeFixedH, pOneInput );
 
765
                pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink;
 
766
                if ( ret < 0 ) {
 
767
#if( bRELEASE_VERSION != 1 )
 
768
#ifndef INCHI_LIBRARY
 
769
                    /* !!! Conversion Error -- Ignore for now !!! */
 
770
                    fprintf( stdout, "%ld %s Conversion failed: %d, %c%c comp %d\n",
 
771
                        num_inp, szCurHdr? szCurHdr : "Struct", ret, iInchiRec? 'R':'D', iMobileH? 'M':'F', k+1); 
 
772
#endif
 
773
#endif
 
774
                    if ( ret == CT_USER_QUIT_ERR ) {
 
775
                        goto exit_error;
 
776
                    }
 
777
                    pStruct[iInchiRec][iMobileH][k].nError = ret;
 
778
                    ret = 0; /* force to ignore the errors for now !!!! */
 
779
                    num_err ++;
 
780
                }
 
781
            }
 
782
        }
 
783
    }
 
784
exit_error:
 
785
    ulProcessingTime += InchiTimeElapsed( &ulTStart );
 
786
    sd->ulStructTime += ulProcessingTime;
 
787
    return ret<0? ret : num_err;
 
788
}
 
789
/**************************************************************************************/
 
790
int AddProtonAndIsoHBalanceToMobHStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd,
 
791
                                         long num_inp, int bHasSomeFixedH, char *szCurHdr,
 
792
                             StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput)
 
793
{
 
794
    COMPONENT_REM_PROTONS nToBeRemovedByNormFromRevrs[INCHI_NUM];
 
795
    int                   nRemovedByNormFromRevrs[INCHI_NUM];
 
796
    int                   nRemovedByRevrs[INCHI_NUM];
 
797
 
 
798
    int   nDeltaFromDisconnected = 0, nRemovedProtonsByNormFromRevrs, nRemovedProtonsByRevrs, num_changes = 0;
 
799
    NUM_H nIsoDeltaFromDisconnected[NUM_H_ISOTOPES];
 
800
    int iInchiRec, i, k, k1, ret = 0;
 
801
    int  nChargeInChI, nChargeRevrs;
 
802
 
 
803
    if ( bHasSomeFixedH ) {
 
804
        return 0; /* 2005-03-01 */
 
805
    }
 
806
 
 
807
    /* num protons removed by InChI Normalization from the original structure */
 
808
    for ( i = 0; i < INCHI_NUM; i ++ ) {
 
809
        nToBeRemovedByNormFromRevrs[i].nNumRemovedProtons = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedProtons;
 
810
        for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) {
 
811
            nToBeRemovedByNormFromRevrs[i].nNumRemovedIsotopicH[k] = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedIsotopicH[k];
 
812
        }
 
813
    }
 
814
    /* accumulate here num. protons removed by the normalization from the reversed structure */
 
815
    nRemovedByNormFromRevrs[INCHI_BAS] =
 
816
    nRemovedByNormFromRevrs[INCHI_REC] = 0;
 
817
    nRemovedByRevrs[INCHI_REC] =
 
818
    nRemovedByRevrs[INCHI_BAS] = 0;
 
819
    /* protons added/removed by InChI Normalization to/from Restored Structure might have been added by StructureRestore */
 
820
    for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) {
 
821
        for ( k = 0; k < pOneInput->nNumComponents[iInchiRec][TAUT_YES]; k ++ ) {
 
822
            if ( !bInpInchiComponentExists( pOneInput, iInchiRec, TAUT_YES, k ) ) {
 
823
                continue;
 
824
            }
 
825
            nRemovedProtonsByNormFromRevrs = 0; /* Num protons removed from the Restored Structure by InChI Normalization */
 
826
            nRemovedProtonsByRevrs         = 0; /* Num protons removed by the Reconstruction from the Restored Structure */
 
827
            if ( iInchiRec == INCHI_REC || iInchiRec == INCHI_BAS && (k1=pStruct[iInchiRec][TAUT_YES][k].nLink) >= 0 ) {
 
828
                
 
829
                REV_INCHI  *pRevInChI   = &pStruct[iInchiRec][TAUT_YES][k].RevInChI;
 
830
                INChI_Aux  **pINChI_Aux2 = pRevInChI->pINChI_Aux[iInchiRec][0]; /* component 0*/
 
831
                INChI      **pINChI_Revr = pRevInChI->pINChI[iInchiRec][0];
 
832
                INChI       *pINChI_Orig = pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k;
 
833
                nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT;
 
834
                nChargeInChI = pINChI_Orig->nTotalCharge;
 
835
                if ( pINChI_Aux2 ) {
 
836
                    nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons;
 
837
                }
 
838
                nRemovedProtonsByRevrs = pStruct[iInchiRec][TAUT_YES][k].nNumRemovedProtonsByRevrs;
 
839
                pStruct[iInchiRec][TAUT_YES][k].nChargeRevrs = nChargeRevrs;
 
840
                pStruct[iInchiRec][TAUT_YES][k].nChargeInChI = nChargeInChI;
 
841
            } else
 
842
            if ( 0 <= ( k1 = -(1+pStruct[iInchiRec][TAUT_YES][k].nLink) ) ) {
 
843
                REV_INCHI  *pRevInChI   = &pStruct[INCHI_REC][TAUT_YES][k1].RevInChI;
 
844
                INChI_Aux  **pINChI_Aux2 = pRevInChI->pINChI_Aux[INCHI_BAS][0]; /* component 0 */
 
845
                INChI      **pINChI_Revr = pRevInChI->pINChI[INCHI_BAS][0];
 
846
                INChI       *pINChI_Orig = pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k1;
 
847
                nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT;
 
848
                nChargeInChI = pINChI_Orig->nTotalCharge;
 
849
                if ( pINChI_Aux2 ) {
 
850
                    nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons;
 
851
                }
 
852
                /* this component cannot be disconnected because it is same as in reconnected layer */
 
853
                nRemovedProtonsByRevrs = pStruct[INCHI_REC][TAUT_YES][k1].nNumRemovedProtonsByRevrs;
 
854
                pStruct[iInchiRec][TAUT_YES][k1].nChargeRevrs = nChargeRevrs;
 
855
                pStruct[iInchiRec][TAUT_YES][k1].nChargeInChI = nChargeInChI;
 
856
            }
 
857
            /* how many protons (to be removed by InChI Normalization) to add = 
 
858
               (proton balance in InChI} - 
 
859
               {number of protons known to be removed by InChI Normalization from Reconstructed structure} */
 
860
            nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons -= nRemovedProtonsByNormFromRevrs;
 
861
            nRemovedByNormFromRevrs[iInchiRec] += nRemovedProtonsByNormFromRevrs;
 
862
            nRemovedByRevrs[iInchiRec]         += nRemovedProtonsByRevrs;
 
863
            pStruct[iInchiRec][TAUT_YES][k].nRemovedProtonsByNormFromRevrs = nRemovedProtonsByNormFromRevrs;
 
864
        }
 
865
    }
 
866
 
 
867
    /* Since fixed-H layer is missing we need to add proton balance to the components */
 
868
    memset( nIsoDeltaFromDisconnected, 0, sizeof(nIsoDeltaFromDisconnected) );
 
869
    for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) {
 
870
        /*
 
871
        if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] &&
 
872
              pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) {
 
873
        */
 
874
             int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]);
 
875
             /* bHasRecMobH means all components that could not be disconnected are in reconnected part */
 
876
             if ( iInchiRec==INCHI_BAS ) {
 
877
                 /* second pass: common structures have been changed */
 
878
                 nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedProtons += nDeltaFromDisconnected;
 
879
             }
 
880
             /* after proton removal InChI is recalculated */
 
881
 
 
882
             ret = AddRemProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES],
 
883
                                     pOneInput->nNumComponents[iInchiRec][TAUT_YES],
 
884
                                     bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL,
 
885
                                     bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0,
 
886
                                     &nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons,
 
887
                                     (iInchiRec==INCHI_REC)?&nDeltaFromDisconnected : NULL);
 
888
             if ( ret < 0 ) {
 
889
                 goto exit_function;
 
890
             }
 
891
             num_changes += ret;
 
892
        /*
 
893
        }
 
894
        */
 
895
    }
 
896
    /* if fixed-H layer is missing then we need to add isotopic exchangeable proton balance to the components */
 
897
    for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) {
 
898
        /*
 
899
        if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] &&
 
900
              pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) {
 
901
        */
 
902
             int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]);
 
903
             /* bHasRecMobH means all components that could not be disconnected are in reconnected part */
 
904
             if ( iInchiRec==INCHI_BAS ) {
 
905
                 /* second pass: common structures have been changed */
 
906
                 for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) {
 
907
                     nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedIsotopicH[k] += nIsoDeltaFromDisconnected[k];
 
908
                 }
 
909
             }
 
910
             /* after proton removal InChI is recalculated */
 
911
             ret = AddRemIsoProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES],
 
912
                                     pOneInput->nNumComponents[iInchiRec][TAUT_YES],
 
913
                                     bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL,
 
914
                                     bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0,
 
915
                                     nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedIsotopicH,
 
916
                                     (iInchiRec==INCHI_REC)?nIsoDeltaFromDisconnected : NULL);
 
917
             if ( ret < 0 ) {
 
918
                 goto exit_function;
 
919
             }
 
920
             num_changes += ret;
 
921
        /*
 
922
        }
 
923
        */
 
924
    }
 
925
 
 
926
exit_function:
 
927
    return ret;
 
928
}
 
929
 
 
930
/*************************************************************/
 
931
void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] )
 
932
{
 
933
    int iInchiRec, iMobileH, cur_num_comp, k, j;
 
934
    StrFromINChI *pStruct1;
 
935
    for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) {
 
936
        for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) {
 
937
            cur_num_comp = nNumComponents[iInchiRec][iMobileH];
 
938
            if ( !cur_num_comp || !(pStruct1=pStruct[iInchiRec][iMobileH]) ) {
 
939
                continue;
 
940
            }
 
941
            for ( k = 0; k < cur_num_comp; k ++ ) {
 
942
                if ( pStruct1[k].at ) {
 
943
                    inchi_free(pStruct1[k].at);
 
944
                }
 
945
                if ( pStruct1[k].at2 ) {
 
946
                    inchi_free(pStruct1[k].at2);
 
947
                }
 
948
                if ( pStruct1[k].st ) {
 
949
                    inchi_free(pStruct1[k].st);
 
950
                }
 
951
                if ( pStruct1[k].pVA ) {
 
952
                    inchi_free(pStruct1[k].pVA);
 
953
                }
 
954
                /*
 
955
                if ( pStruct1[k].ti.t_group ) {
 
956
                    inchi_free( pStruct1[k].ti.t_group );
 
957
                }
 
958
                */
 
959
                if ( pStruct1[k].pXYZ ) {
 
960
                    inchi_free(pStruct1[k].pXYZ);
 
961
                }
 
962
                /*==== begin ====*/
 
963
                free_t_group_info( &pStruct1[k].ti );
 
964
                if ( pStruct1[k].endpoint ) {
 
965
                    inchi_free(pStruct1[k].endpoint);
 
966
                }
 
967
                if ( pStruct1[k].fixed_H ) {
 
968
                    inchi_free(pStruct1[k].fixed_H);
 
969
                }
 
970
                for ( j = 0; j < TAUT_NUM; j ++ ) {
 
971
                    if ( pStruct1[k].nAtno2Canon[j] )
 
972
                        inchi_free( pStruct1[k].nAtno2Canon[j] );
 
973
                    if ( pStruct1[k].nCanon2Atno[j] )
 
974
                        inchi_free( pStruct1[k].nCanon2Atno[j] );
 
975
                }
 
976
                /*===== end ======*/
 
977
                /*  free INChI memory */
 
978
                FreeAllINChIArrays( pStruct1[k].RevInChI.pINChI,
 
979
                                    pStruct1[k].RevInChI.pINChI_Aux,
 
980
                                    pStruct1[k].RevInChI.num_components );
 
981
#ifdef NEVER
 
982
                /* don't do that: these are just pointers to OneInput structure members */
 
983
                Free_INChI( &pStruct1[k].pINChI );
 
984
                Free_INChI_Aux( &pStruct1[k].pINChI_Aux );
 
985
                if ( pStruct1[k].inp_norm_data ) {
 
986
                    FreeInpAtomData( pStruct1[k].inp_norm_data );
 
987
                    inchi_free( pStruct1[k].inp_norm_data );
 
988
                }
 
989
#endif
 
990
            }
 
991
            inchi_free(pStruct[iInchiRec][iMobileH]);
 
992
            pStruct[iInchiRec][iMobileH] = NULL;
 
993
        }
 
994
    }
 
995
}
 
996
/********************************************************************/
 
997
void FreeInpInChI( InpInChI *pOneInput )
 
998
{
 
999
    int iINChI, k, j;
 
1000
    for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) {
 
1001
        for ( j = 0; j < TAUT_NUM; j ++ ) {
 
1002
            if ( pOneInput->pInpInChI[iINChI][j] ) {
 
1003
                for ( k = 0; k < pOneInput->nNumComponents[iINChI][j]; k ++ ) {
 
1004
                    Free_INChI_Members( &pOneInput->pInpInChI[iINChI][j][k] );
 
1005
                }
 
1006
                inchi_free(pOneInput->pInpInChI[iINChI][j]);
 
1007
                pOneInput->pInpInChI[iINChI][j] = NULL;
 
1008
            }
 
1009
            if ( pOneInput->nNumProtons[iINChI][j].pNumProtons ) {
 
1010
                inchi_free( pOneInput->nNumProtons[iINChI][j].pNumProtons );
 
1011
                pOneInput->nNumProtons[iINChI][j].pNumProtons = NULL;
 
1012
            }
 
1013
        }
 
1014
    }
 
1015
    if ( pOneInput->atom ) inchi_free(pOneInput->atom);
 
1016
    memset( pOneInput, 0, sizeof(*pOneInput) );
 
1017
}
 
1018
 
 
1019
/***********************************************************************************************/
 
1020
int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut,
 
1021
                                  long num_inp, char *szCurHdr)
 
1022
{
 
1023
    int i, iInchiRec, iMobileH, iMobileHpStruct, num_components, iComponent, ret=0;
 
1024
    COMPONENT_REM_PROTONS nCurRemovedProtons, nNumRemovedProtons;
 
1025
    INChI     *pInChI[TAUT_NUM];
 
1026
    INCHI_MODE  CompareInchiFlags[TAUT_NUM];
 
1027
    memset( pOneInput->CompareInchiFlags[0], 0, sizeof(pOneInput->CompareInchiFlags[0]) );
 
1028
    memset( &nNumRemovedProtons, 0, sizeof(nNumRemovedProtons) );
 
1029
    
 
1030
    /* do we have reconnected InChI ?*/
 
1031
    iInchiRec = INCHI_REC;
 
1032
    iMobileH  = TAUT_NON;
 
1033
    if ( !pOneInput->nNumComponents[iInchiRec][TAUT_YES] && !pOneInput->nNumComponents[iInchiRec][TAUT_NON] ) {
 
1034
        iInchiRec = INCHI_BAS;
 
1035
    }
 
1036
    /* do we have Mobile or Fixed-H ? */
 
1037
    if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] || !bReqNonTaut ) {
 
1038
        iMobileH = TAUT_YES;  /* index for pOneInput */
 
1039
    }
 
1040
    /* if a restored structure has Fixed-H InChI then its mobile-H restored InChI is in Fixed-H pStruct */
 
1041
    num_components = pOneInput->nNumComponents[iInchiRec][iMobileH];
 
1042
    for ( iComponent = 0; iComponent < num_components; iComponent ++ ) {
 
1043
        int bMobileH = iMobileH;
 
1044
        pInChI[0]     = pInChI[1]     = NULL;
 
1045
        if (  pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].nNumberOfAtoms &&
 
1046
             !pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].bDeleted ) {
 
1047
            /* the requested InChI layer exists */
 
1048
            pInChI[0]     = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent];
 
1049
            if ( bMobileH == TAUT_NON ) {
 
1050
                pInChI[1] = &pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent];
 
1051
            }
 
1052
        } else
 
1053
        if ( bMobileH == TAUT_NON &&
 
1054
             pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].nNumberOfAtoms &&
 
1055
             !pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].bDeleted ) {
 
1056
            /* the requested Fixed-H InChI layer does not exist; however, the Mobile-H does exist */
 
1057
            bMobileH = TAUT_YES; /* only Mobile-H is available */
 
1058
            pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent];
 
1059
        }
 
1060
        memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) );
 
1061
        memset( &nCurRemovedProtons, 0, sizeof(nCurRemovedProtons) );
 
1062
        iMobileHpStruct = 
 
1063
#if( bRELEASE_VERSION == 0 )
 
1064
#ifndef INCHI_LIBRARY
 
1065
        /* legacy: reproduce old output */
 
1066
        OldPrintCompareOneOrigInchiToRevInChI(pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH,
 
1067
                                              iComponent, num_inp, szCurHdr);
 
1068
#endif
 
1069
#endif
 
1070
        /* one component comparison result bits */
 
1071
        ret = CompareOneOrigInchiToRevInChI( pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, iComponent,
 
1072
                                             num_inp, szCurHdr, &nCurRemovedProtons, CompareInchiFlags);
 
1073
        if ( ret >= 0 ) {
 
1074
            /* no errors encountered -> accumulate removed protons from individual Mobile-H layers of components */
 
1075
            nNumRemovedProtons.nNumRemovedProtons += nCurRemovedProtons.nNumRemovedProtons;
 
1076
            for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) {
 
1077
                nNumRemovedProtons.nNumRemovedIsotopicH[i] += nCurRemovedProtons.nNumRemovedIsotopicH[i];
 
1078
            }
 
1079
            /* accumulate compare bits */
 
1080
            for ( i = 0; i < TAUT_NUM; i ++ ) {
 
1081
                pOneInput->CompareInchiFlags[0][i] |= CompareInchiFlags[i];
 
1082
            }
 
1083
        } else {
 
1084
            goto exit_function;
 
1085
        }
 
1086
    }
 
1087
    if ( iMobileH == TAUT_YES ) {
 
1088
        if ( pOneInput->nNumProtons[iInchiRec][iMobileH].pNumProtons ) {
 
1089
            ret = RI_ERR_PROGR; /* in Mobile-H case proton balances are split between compoments */
 
1090
        } else {
 
1091
            /*   num removed protons in orig. InChI      num removed protons in restored InChi */
 
1092
            if ( nNumRemovedProtons.nNumRemovedProtons != pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons ) {
 
1093
                /* restored structure InChI has less or more removed protons */
 
1094
                pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOBH_PROTONS;
 
1095
#if( bRELEASE_VERSION == 0 )
 
1096
                /* debug output only */
 
1097
                {
 
1098
                    int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons
 
1099
                                              - nNumRemovedProtons.nNumRemovedProtons;
 
1100
                    fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Proton balance (Diff: %d, RevrsRem=%d)\n",
 
1101
                        num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D',
 
1102
                        pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons,num_H_AddedByRevrs);
 
1103
                }
 
1104
#endif
 
1105
            }
 
1106
            for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) {
 
1107
                if ( nNumRemovedProtons.nNumRemovedIsotopicH[i] != pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] ) {
 
1108
                    pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOB_ISO_H;
 
1109
#if( bRELEASE_VERSION == 0 )
 
1110
                    /* debug output only */
 
1111
                    {
 
1112
                    int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i]
 
1113
                      - nNumRemovedProtons.nNumRemovedIsotopicH[i];
 
1114
                    fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Iso Xchg %dH balance (Diff: %d, RevrsRem=%d)\n",
 
1115
                        num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', i+1,
 
1116
                        pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i],num_H_AddedByRevrs);
 
1117
                    }
 
1118
#endif
 
1119
                }
 
1120
            }
 
1121
        }
 
1122
    }
 
1123
 
 
1124
exit_function:
 
1125
    return ret;
 
1126
}
 
1127
/***********************************************************************************************/
 
1128
int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM],
 
1129
                                              InpInChI *pOneInput, int bHasSomeFixedH,
 
1130
                                              long num_inp, char *szCurHdr)
 
1131
{
 
1132
    int i, k, m, n, iInChI, iMobileH, bMobileH, ifk;
 
1133
    int num_components_D, num_components_R;
 
1134
    int nNumCompHaveSeparateProtons_D, nNumCompHaveSeparateProtons_R;
 
1135
    int num_fragments_D, num_fragments_R, num_fragments_DR, num_fragments, iComponent, ret;
 
1136
    int ifInChI, ifMobileH, bfMobileH, nLink;
 
1137
    COMPONENT_REM_PROTONS nNumRemovedProtons_D;     /* removed from the disconnected layer of the Input InChI */
 
1138
    COMPONENT_REM_PROTONS nNumRemovedProtons_D_all; /* if only totals are avalable */
 
1139
    COMPONENT_REM_PROTONS nNumRemovedProtons_R; /* removed from disconnected layer of the reconstructed struct */
 
1140
    COMPONENT_REM_PROTONS nNumRemovedProtons_R_all;
 
1141
    INCHI_MODE  CompareInchiFlags[TAUT_NUM];
 
1142
    StrFromINChI *pStruct1;
 
1143
    INChI_Aux *pINChI_Aux;
 
1144
    INCHI_SORT *pINChISort1 = NULL; /* from reversed structure */
 
1145
    INCHI_SORT *pINChISort2 = NULL; /* original input InChI */
 
1146
    int        nNumNonTaut1=0, nNumNonTaut2=0;
 
1147
 
 
1148
    ret = 0;
 
1149
    memset( pOneInput->CompareInchiFlags[1], 0, sizeof(pOneInput->CompareInchiFlags[1]) );
 
1150
 
 
1151
    /* count components that are not subject to disconnection */
 
1152
    if ( !pOneInput->nNumComponents[INCHI_REC][TAUT_YES] &&
 
1153
         !pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) {
 
1154
        return 0; /* nothing to do */
 
1155
    }
 
1156
 
 
1157
    memset( &nNumRemovedProtons_D, 0, sizeof(nNumRemovedProtons_D) );
 
1158
    memset( &nNumRemovedProtons_R, 0, sizeof(nNumRemovedProtons_R) );
 
1159
    memset( &nNumRemovedProtons_D_all, 0, sizeof(nNumRemovedProtons_D_all) );
 
1160
    memset( &nNumRemovedProtons_R_all, 0, sizeof(nNumRemovedProtons_R_all) );
 
1161
    memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) );
 
1162
 
 
1163
    num_components_D = inchi_max( pOneInput->nNumComponents[INCHI_BAS][TAUT_YES],
 
1164
                                  pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] );
 
1165
    num_components_R = inchi_max( pOneInput->nNumComponents[INCHI_REC][TAUT_YES],
 
1166
                                  pOneInput->nNumComponents[INCHI_REC][TAUT_NON] );
 
1167
    /***********************************************************************************************/
 
1168
    /* InpInChI: count fragments -- disconnected components that do not match reconnected          */
 
1169
    /* Accumulate removed H and isotopic H from ALL Fixed-H disconnected components except deleted */
 
1170
    /* This segment collects info from the original InChI                                          */
 
1171
    /***********************************************************************************************/
 
1172
    /*---- Original InChI ----*/
 
1173
    num_fragments_D = 0;
 
1174
    iInChI   = INCHI_BAS;
 
1175
    iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
 
1176
    nNumCompHaveSeparateProtons_D = 0;
 
1177
 
 
1178
    /* in case of Mobile-H components here are the proton totals from the original InChI disconn. layer */
 
1179
    nNumRemovedProtons_D.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons;
 
1180
    memcpy( nNumRemovedProtons_D.nNumRemovedIsotopicH, 
 
1181
            pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH,
 
1182
            sizeof(nNumRemovedProtons_D.nNumRemovedIsotopicH) ); /* total for the disconnected layer */
 
1183
 
 
1184
    for ( k = 0; k < num_components_D; k ++ ) {
 
1185
        bMobileH = iMobileH;
 
1186
        if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) {
 
1187
            if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) {
 
1188
                bMobileH = TAUT_YES;
 
1189
            } else {
 
1190
                continue; /* component is missing ??? */
 
1191
            }
 
1192
        }
 
1193
        if ( 0 > (nLink = pOneInput->pInpInChI[iInChI][bMobileH][k].nLink) ) {
 
1194
            /* component in Disconnected layer is linked to the identical one in the Reconnected layer */
 
1195
            if ( pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) {
 
1196
                nNumCompHaveSeparateProtons_D ++;
 
1197
                nLink = -(1+nLink);
 
1198
                nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedProtons;
 
1199
                for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) {
 
1200
                    nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedIsotopicH[m];
 
1201
                }
 
1202
            }
 
1203
            continue; /* same as reconnected */
 
1204
        }
 
1205
        /* component in the reconnected layer that was disconnected */
 
1206
        nNumNonTaut2 += (bMobileH == TAUT_NON);
 
1207
        if ( pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons ) {
 
1208
            nNumCompHaveSeparateProtons_D ++;
 
1209
            nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons;
 
1210
            for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) {
 
1211
                nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m];
 
1212
            }
 
1213
        }
 
1214
        num_fragments_D ++; /* number of disconnected fragments from original reconnected structure */
 
1215
    }
 
1216
    /* in case of Mobile-H components here are the proton totals from the original InChI */
 
1217
    /*
 
1218
    nNumRemovedProtons_D_all.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons;
 
1219
    memcpy( nNumRemovedProtons_D_all.nNumRemovedIsotopicH, 
 
1220
            pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH,
 
1221
            sizeof(nNumRemovedProtons_D_all.nNumRemovedIsotopicH) );
 
1222
 
 
1223
    */
 
1224
    /****************************************************************************************************/
 
1225
    /* count fragments in reconstructed reconnected structure                                           */
 
1226
    /* accumulate removed H and isotopic H from ALL reconstructed reconnected components except deleted */
 
1227
    /* This segment collects info from the reconstructed structure InChI                                */
 
1228
    /****************************************************************************************************/
 
1229
    /*---- InChI from the reconstructed reconnected structure ----*/
 
1230
    num_fragments_R = 0;
 
1231
    iInChI   = INCHI_REC;
 
1232
    iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
 
1233
    nNumCompHaveSeparateProtons_R = 0;
 
1234
    for ( k = 0; k < num_components_R; k ++ ) {
 
1235
        bMobileH = iMobileH;
 
1236
        if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) {
 
1237
            if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) {
 
1238
                bMobileH = TAUT_YES;
 
1239
            } else {
 
1240
                continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */
 
1241
            }
 
1242
        }
 
1243
        if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) {
 
1244
            /* this reconstructed reconnected component was NOT DISCONNECTED */
 
1245
            /* same component is in the disconnected layer, it has no metal atoms or is an isolated metal atom */
 
1246
            pStruct1 = pStruct[iInChI][bMobileH]+k;
 
1247
            ifMobileH = TAUT_YES;  /* Mobile-H Aux_Info contains number removed protons */
 
1248
            ifInChI   = INCHI_BAS; /* this component cannot be reconnected */
 
1249
            ifk       = 0;         /* 0th component since it is InChI of a single component */
 
1250
            /* The statement in the following line is *WRONG*, component number mixed with bMobileH:  */
 
1251
            /* in RevInchi, when only Mobile-H is present then its only non-NULL InChI has index 0==TAUT_NON */
 
1252
            if ( bRevInchiComponentExists( pStruct1, ifInChI, ifMobileH, ifk ) ) {
 
1253
                /* count protons */
 
1254
                pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][ifMobileH];
 
1255
                if ( pINChI_Aux ) {
 
1256
                    nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons;
 
1257
                    for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) {
 
1258
                        nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m];
 
1259
                    }
 
1260
                }
 
1261
            }
 
1262
            nNumCompHaveSeparateProtons_R += bRevInchiComponentExists( pStruct1, ifInChI, ALT_TAUT(ifMobileH), ifk );
 
1263
            continue; /* same as disconnected, has no metal atoms */
 
1264
        }
 
1265
        /* this reconstructed reconnected component WAS DISCONNECTED; check its fragments */
 
1266
        /* it does not have same component in the disconnected layer */
 
1267
        pStruct1 = pStruct[iInChI][bMobileH]+k;
 
1268
        num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS];
 
1269
        ifInChI = INCHI_BAS; /* disconnected layer */
 
1270
        ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES;
 
1271
        for ( ifk = 0; ifk < num_fragments; ifk ++ ) {
 
1272
            bfMobileH = ifMobileH;
 
1273
            if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, ifk ) ) {
 
1274
                if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, ifk ) ) {
 
1275
                    bfMobileH = TAUT_YES;
 
1276
                } else {
 
1277
                    continue; /* fragment does not exist ??? */
 
1278
                }
 
1279
            }
 
1280
            nNumNonTaut1           += (bfMobileH == TAUT_NON);
 
1281
            nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON);
 
1282
            /* count protons from fragments made by metal disconnection */
 
1283
            pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES];
 
1284
            if ( pINChI_Aux ) {
 
1285
                nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons;
 
1286
                for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) {
 
1287
                    nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m];
 
1288
                }
 
1289
            }
 
1290
            num_fragments_R ++; /* number of disconnected fragments from reconstructed reconnected structure */
 
1291
        }
 
1292
    }
 
1293
    /*---------------- special treatment of the last reconstructed component -----------------*/
 
1294
    /*---------------- this may contain separate protons added by the reconstruction ---------*/
 
1295
    k = num_components_R - 1;
 
1296
    pStruct1 = pStruct[iInChI][iMobileH]+k;
 
1297
    if ( iMobileH == TAUT_YES && !bHasSomeFixedH &&
 
1298
         bInpInchiComponentDeleted( pOneInput, iInChI, iMobileH, k ) &&
 
1299
         (num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]) ) {
 
1300
 
 
1301
        ifInChI = INCHI_BAS; /* disconnected layer */
 
1302
        ifMobileH = TAUT_YES;
 
1303
        for ( ifk = 0; ifk < num_fragments; ifk ++ ) {
 
1304
            bfMobileH = ifMobileH;
 
1305
            if ( !bRevInchiComponentDeleted( pStruct1, ifInChI, bfMobileH, ifk ) ) {
 
1306
                continue; /* fragment does exist ??? Should not happen */
 
1307
            }
 
1308
            /*
 
1309
            nNumNonTaut1           += (bfMobileH == TAUT_NON);
 
1310
            nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON);
 
1311
            */
 
1312
            /* count protons from fragments made by metal disconnection */
 
1313
            pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES];
 
1314
            if ( pINChI_Aux ) {
 
1315
                nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons;
 
1316
                for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) {
 
1317
                    nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m];
 
1318
                }
 
1319
            }
 
1320
            /*num_fragments_R ++;*/ /* number of disconnected fragments from reconstructed reconnected structure */
 
1321
        }
 
1322
    }
 
1323
 
 
1324
 
 
1325
 
 
1326
    num_fragments_DR = inchi_max( num_fragments_D, num_fragments_R );
 
1327
    /* in case of correct reconstruction, num_fragments_D, num_fragments_R */
 
1328
 
 
1329
    if ( !num_fragments_DR ) {
 
1330
        return 0; /* no component was disconnected */
 
1331
    }
 
1332
    if ( num_fragments_D != num_fragments_R ) {
 
1333
        for ( i = 0; i < TAUT_NUM; i ++ ) {
 
1334
            if ( pOneInput->nNumComponents[INCHI_BAS][i] ) {
 
1335
                pOneInput->CompareInchiFlags[1][i] |= INCHIDIFF_PROBLEM;
 
1336
            }
 
1337
        }
 
1338
        return 1; /* severe error */
 
1339
    }
 
1340
 
 
1341
 
 
1342
    pINChISort1 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort1[0]));
 
1343
    pINChISort2 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort2[0]));
 
1344
    if ( !pINChISort1 || !pINChISort2 ) {
 
1345
        ret = RI_ERR_ALLOC;
 
1346
        goto exit_function;
 
1347
    }
 
1348
 
 
1349
    /* accumulate original InChI of fragments -- disconnected components that do not match reconnected */
 
1350
    iInChI   = INCHI_BAS;
 
1351
    iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
 
1352
    for ( k = n = 0; k < num_components_D; k ++ ) {
 
1353
        bMobileH = iMobileH;
 
1354
        if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) {
 
1355
            if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) {
 
1356
                bMobileH = TAUT_YES;
 
1357
            } else {
 
1358
                continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */
 
1359
            }
 
1360
        }
 
1361
        if ( 0 > pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) {
 
1362
            continue; /* same as reconnected */
 
1363
        }
 
1364
        /* the component exists in disconnected layer of the orig. InChI only: it is a fragment */
 
1365
        pINChISort2[n].pINChI[bMobileH] = pOneInput->pInpInChI[iInChI][bMobileH] + k;
 
1366
        if ( bMobileH == TAUT_NON && 
 
1367
             (bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ||
 
1368
              bInpInchiComponentDeleted( pOneInput, iInChI, TAUT_YES, k ) ) ) {
 
1369
            pINChISort2[n].pINChI[TAUT_YES] = pOneInput->pInpInChI[iInChI][TAUT_YES] + k;
 
1370
        }
 
1371
        /* the last sort key is a number of removed protons */
 
1372
        pINChISort2[n].ord_number = pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons?
 
1373
                     pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons : 0;
 
1374
        pINChISort2[n].n1 = k;  /* orig. InChI disconnected layer component number */
 
1375
        pINChISort2[n].n2 = -1; /* no fragment index */
 
1376
        n ++;     
 
1377
    }
 
1378
 
 
1379
    /* accumulate fragments from the reconstructed structure */
 
1380
    iInChI   = INCHI_REC;
 
1381
    iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
 
1382
    for ( k = n = 0; k < num_components_R; k ++ ) {
 
1383
        bMobileH = iMobileH;
 
1384
        if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) {
 
1385
            if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) {
 
1386
                bMobileH = TAUT_YES;
 
1387
            } else {
 
1388
                continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */
 
1389
            }
 
1390
        }
 
1391
        /* the reconstructed structure */
 
1392
        if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) {
 
1393
            continue; /* same as disconnected, has no metal atoms */
 
1394
        }
 
1395
        /* this reconstructed structure was disconnected */
 
1396
        pStruct1 = pStruct[iInChI][bMobileH]+k;
 
1397
        num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS];
 
1398
        ifInChI = INCHI_BAS;
 
1399
        ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES;
 
1400
        for ( i = 0; i < num_fragments; i ++ ) {
 
1401
            bfMobileH = ifMobileH;
 
1402
            if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) {
 
1403
                if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) {
 
1404
                    bfMobileH = TAUT_YES;
 
1405
                } else {
 
1406
                    continue; /* component is missing ??? */
 
1407
                }
 
1408
            }
 
1409
            pINChISort1[n].pINChI[bfMobileH] =  pStruct1->RevInChI.pINChI[ifInChI][i][bfMobileH];
 
1410
            if ( bfMobileH == TAUT_NON /*&& bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i )*/ ) {
 
1411
                pINChISort1[n].pINChI[TAUT_YES] = pStruct1->RevInChI.pINChI[ifInChI][i][TAUT_YES];
 
1412
                /* remove Fixed-H InChI if is is identical to Mobile-H */
 
1413
                /* do it exactly same way the identical components were removed from InpInChI */
 
1414
                if ( !CompareReversedINChI( pINChISort1[n].pINChI[bfMobileH],
 
1415
                                            pINChISort1[n].pINChI[TAUT_YES], NULL, NULL ) ) {
 
1416
                    pINChISort1[n].pINChI[bfMobileH] = NULL; /* remove Fixed-H layer */
 
1417
                } else {
 
1418
                    pINChISort1[n].ord_number = pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedProtons;
 
1419
                }
 
1420
            }
 
1421
 
 
1422
            pINChISort1[n].n1 = k;  /* reconstructed reconnected structure component index */
 
1423
            pINChISort1[n].n2 = i;  /* index of a fragment made out of this component */
 
1424
            n ++;
 
1425
        }
 
1426
    }
 
1427
    
 
1428
    /* sort fragment InChI before comparing them */
 
1429
    qsort( pINChISort1, num_fragments_D, sizeof(pINChISort1[0]), CompINChITaut2 );
 
1430
    qsort( pINChISort2, num_fragments_R, sizeof(pINChISort2[0]), CompINChITaut2 );
 
1431
    
 
1432
    /* compare fragments -- components present in disconnected layer only */
 
1433
    for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) {
 
1434
        INChI *pInChI1[TAUT_NUM]; /* from reversed structure */
 
1435
        INChI *pInChI2[TAUT_NUM]; /* original input InChI */
 
1436
        for ( i = 0; i < TAUT_NUM; i ++ ) {
 
1437
            pInChI1[i] = pINChISort1[iComponent].pINChI[i];
 
1438
            pInChI2[i] = pINChISort2[iComponent].pINChI[i];
 
1439
        }
 
1440
        CompareTwoPairsOfInChI( pInChI1, pInChI2, !bHasSomeFixedH, CompareInchiFlags );
 
1441
    }
 
1442
    
 
1443
    if ( /*nNumNonTaut1 && nNumNonTaut2 &&*/ bHasSomeFixedH ) {
 
1444
        if ( nNumCompHaveSeparateProtons_D || nNumCompHaveSeparateProtons_R ) {
 
1445
            /* for each component, compare number removed protons */
 
1446
            /* comparison does not make sense if Disconnected Fixed-H layer is not present */
 
1447
            for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) {
 
1448
                NUM_H   nNumRemovedIsotopicH1[NUM_H_ISOTOPES];
 
1449
                NUM_H   nNumRemovedIsotopicH2[NUM_H_ISOTOPES];
 
1450
 
 
1451
                memset( nNumRemovedIsotopicH1, 0, sizeof(nNumRemovedIsotopicH1) );
 
1452
                memset( nNumRemovedIsotopicH2, 0, sizeof(nNumRemovedIsotopicH2) );
 
1453
                /* compare removed protons */
 
1454
                if ( pINChISort1[iComponent].ord_number != pINChISort2[iComponent].ord_number ) {
 
1455
                    CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */
 
1456
                }
 
1457
                /* also compare removed isotopic atoms H */
 
1458
                k = pINChISort2[iComponent].n1; /* input InChI, OneInput */
 
1459
                if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons ) {
 
1460
                    memcpy( nNumRemovedIsotopicH2,
 
1461
                        pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH,
 
1462
                        sizeof( nNumRemovedIsotopicH2 ) );
 
1463
                }
 
1464
               /* get fragments of reconstructed structure removed protons info */
 
1465
                k = pINChISort1[iComponent].n1; /* restored component number */
 
1466
                i = pINChISort1[iComponent].n2; /* subcomponent number */
 
1467
                iInChI   = INCHI_REC;
 
1468
                iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
 
1469
                bMobileH = iMobileH;
 
1470
                if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) {
 
1471
                    if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) {
 
1472
                        bMobileH = TAUT_YES;
 
1473
                    } else {
 
1474
                       goto compare_iso_H;
 
1475
                    }
 
1476
                }
 
1477
                if ( pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) {
 
1478
                    continue;
 
1479
                    /*
 
1480
                    ret = RI_ERR_PROGR;
 
1481
                    goto exit_function;
 
1482
                    */
 
1483
                }
 
1484
                pStruct1 = pStruct[iInChI][bMobileH]+k;
 
1485
                num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS];
 
1486
                ifInChI = INCHI_BAS;
 
1487
                ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES;
 
1488
                if ( i < num_fragments ) {
 
1489
                    bfMobileH = ifMobileH;
 
1490
                    if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) {
 
1491
                        if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) {
 
1492
                            bfMobileH = TAUT_YES;
 
1493
                        } else {
 
1494
                            goto compare_iso_H;
 
1495
                        }
 
1496
                    }
 
1497
                    memcpy( nNumRemovedIsotopicH1,
 
1498
                        pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedIsotopicH,
 
1499
                        sizeof( nNumRemovedIsotopicH1 ) );
 
1500
                }
 
1501
compare_iso_H:
 
1502
                if ( memcmp( nNumRemovedIsotopicH1, nNumRemovedIsotopicH2, sizeof( nNumRemovedIsotopicH1 ) ) ) {
 
1503
                    CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H;
 
1504
                }
 
1505
            }
 
1506
        }
 
1507
    } else
 
1508
    /*if ( !nNumNonTaut1 && !nNumNonTaut2 || !bHasSomeFixedH )*/ {
 
1509
        /* compare totals for removed protons and isotopic H */
 
1510
        if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedProtons !=
 
1511
             nNumRemovedProtons_R.nNumRemovedProtons ) {
 
1512
            CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS;
 
1513
        }
 
1514
        if ( memcmp( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedIsotopicH,
 
1515
                     nNumRemovedProtons_R.nNumRemovedIsotopicH,
 
1516
                     sizeof( nNumRemovedProtons_R.nNumRemovedIsotopicH ) ) ) {
 
1517
                CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H;
 
1518
        }
 
1519
    }
 
1520
 
 
1521
    if ( !nNumNonTaut1 == !nNumNonTaut2 ) {
 
1522
        ; /* difference if(nNumNonTaut1 != nNumNonTaut2) will be caught in InChI comparison */
 
1523
    } else
 
1524
    if ( nNumNonTaut1 ) {
 
1525
        /* reconstructed has Fixed-H while the original has not: extra Fixed-H layer */
 
1526
        CompareInchiFlags[TAUT_YES] |= INCHIDIFF_WRONG_TAUT;
 
1527
    } else {
 
1528
        /* the original InChI has Fixed-H while the reconstructed one has not: missing Fixed-H layer */
 
1529
        CompareInchiFlags[TAUT_YES] |= INCHIDIFF_NO_TAUT;
 
1530
    }
 
1531
    for ( i = 0; i < TAUT_NUM; i ++ ) {
 
1532
        pOneInput->CompareInchiFlags[1][i] |= CompareInchiFlags[i];
 
1533
    }
 
1534
 
 
1535
    /* compare totals */
 
1536
    if ( nNumRemovedProtons_R.nNumRemovedProtons != nNumRemovedProtons_D.nNumRemovedProtons ) {
 
1537
        CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */
 
1538
    }
 
1539
    if ( memcmp( nNumRemovedProtons_R.nNumRemovedIsotopicH, 
 
1540
                 nNumRemovedProtons_D.nNumRemovedIsotopicH,
 
1541
                 sizeof( nNumRemovedProtons_D.nNumRemovedIsotopicH ) ) ) {
 
1542
        CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H;
 
1543
    }
 
1544
 
 
1545
exit_function:
 
1546
 
 
1547
    if ( pINChISort1 ) inchi_free( pINChISort1 );
 
1548
    if ( pINChISort2 ) inchi_free( pINChISort2 );
 
1549
 
 
1550
    return ret;
 
1551
}
 
1552
/******************************************************************************************************/
 
1553
int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM],
 
1554
                            int bMobileH, INCHI_MODE CompareInchiFlags[] )
 
1555
{
 
1556
    int iMobileH, err=0;
 
1557
    INCHI_MODE cmp;
 
1558
    for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) {
 
1559
        if ( !pInChI1[iMobileH] != !pInChI2[iMobileH] ) {
 
1560
            if ( iMobileH == TAUT_NON &&
 
1561
                 pInChI1[TAUT_YES] && pInChI1[TAUT_YES] ) {
 
1562
                CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_HLAYER;
 
1563
            } else {
 
1564
                CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_NUMBER;
 
1565
            }
 
1566
            continue;
 
1567
        }
 
1568
        if ( pInChI1[iMobileH] && pInChI2[iMobileH] ) {
 
1569
            cmp = CompareReversedINChI3( pInChI1[iMobileH], pInChI2[iMobileH], NULL, NULL, &err );
 
1570
            if ( cmp ) {
 
1571
                CompareInchiFlags[iMobileH] |= cmp;
 
1572
            }
 
1573
        }
 
1574
    }
 
1575
    return err;
 
1576
}
 
1577
/******************************************************************************************************/
 
1578
int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent,
 
1579
                                  long num_inp, char *szCurHdr,
 
1580
                                  COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[])
 
1581
{
 
1582
    int ret = pStruct->RevInChI.nRetVal, err=0;
 
1583
    INCHI_MODE cmp;
 
1584
    if ( ret == _IS_OKAY || ret == _IS_WARNING ) {
 
1585
        /* ignore bMobileH for now */
 
1586
        int i, i0, b /* created type */, b0 /* requested type*/, j, k;
 
1587
        /* pINChI[iINCHI][iComponent][bTaut] */
 
1588
        /* i0 = requested Rec/Disconnected: 1/0 */
 
1589
        /* i  = what InChI creaded out of the restored structure */
 
1590
        /* b0 = requested Mobile/Fixed-H: 1/0 */
 
1591
        /* b  = what InChI creaded out of the restored structure */
 
1592
        i = i0 = pStruct->iINCHI;
 
1593
        b = b0 = pStruct->iMobileH;
 
1594
        if ( i == INCHI_REC && !pStruct->RevInChI.num_components[i] ) {
 
1595
            i = INCHI_BAS;
 
1596
        }
 
1597
        if ( b == TAUT_NON && (!pStruct->RevInChI.pINChI[i] ||
 
1598
                               !pStruct->RevInChI.pINChI[i][0][b] ||
 
1599
                               !pStruct->RevInChI.pINChI[i][0][b]->nNumberOfAtoms ) ) {
 
1600
            b = TAUT_YES;
 
1601
        }
 
1602
        if ( pStruct->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) {
 
1603
            return 0;
 
1604
        }
 
1605
 
 
1606
        if ( pStruct->RevInChI.num_components[i] > 1 && 
 
1607
             !pStruct->RevInChI.pINChI[i][1][b]->bDeleted ||
 
1608
             pStruct->RevInChI.num_components[i] < 1 ) {
 
1609
            CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_NUMBER;
 
1610
        }
 
1611
        if ( b != b0 || b != bMobileH || b0 != bMobileH || i > i0 ) {
 
1612
            /* do not print messages about TAUT_YES instead of TAUT_NON */
 
1613
            CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_HLAYER;
 
1614
        }
 
1615
 
 
1616
        if ( pStruct->RevInChI.num_components[i] ) {
 
1617
            /* compare InChI from restored structure; '0' in [i][0][b] is the first component */
 
1618
            if ( b == TAUT_YES && pStruct->RevInChI.pINChI[i][0][b]->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) {
 
1619
                /* the 1st component is made out of proton(s) and the input component is missing or also a proton */
 
1620
                cmp = 0;
 
1621
            } else {
 
1622
                cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][b], pInChI[0], NULL, NULL, &err );
 
1623
                if ( cmp ) {
 
1624
                    CompareInchiFlags[bMobileH] |= cmp;
 
1625
                }
 
1626
            }
 
1627
            if ( b == b0 && b == TAUT_NON ) {
 
1628
                if ( pStruct->RevInChI.pINChI[i][0][TAUT_YES] &&
 
1629
                     !pStruct->RevInChI.pINChI[i][0][TAUT_YES]->bDeleted ||
 
1630
                     pInChI[1] && !pInChI[1]->bDeleted ) {
 
1631
 
 
1632
                    /* in addition to fixed-H also compare mobile-H InChI */
 
1633
                    cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][TAUT_YES], pInChI[1], NULL, NULL, &err );
 
1634
                    if ( cmp ) {
 
1635
                        CompareInchiFlags[TAUT_YES] |= cmp;
 
1636
                    }
 
1637
                }
 
1638
                /* compare removed H */
 
1639
                if ( pStruct->nNumRemovedProtonsMobHInChI != pStruct->RevInChI.pINChI_Aux[i][0][TAUT_YES]->nNumRemovedProtons ) {
 
1640
                    CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS;
 
1641
                }
 
1642
            }
 
1643
            memset( nCurRemovedProtons, 0, sizeof(*nCurRemovedProtons) );
 
1644
            for ( k = 0; k < pStruct->RevInChI.num_components[i]; k ++ ) {
 
1645
                if ( !k || pStruct->RevInChI.pINChI[i][k][TAUT_YES]->bDeleted ) {
 
1646
                    /* get removed protons from the 1st component; add othere only if they are deleted protons */
 
1647
                    nCurRemovedProtons->nNumRemovedProtons += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedProtons;
 
1648
                    for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) {
 
1649
                        nCurRemovedProtons->nNumRemovedIsotopicH[j] += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedIsotopicH[j];
 
1650
                    }
 
1651
                }
 
1652
            }
 
1653
        }
 
1654
    } else {
 
1655
        CompareInchiFlags[bMobileH] |= INCHIDIFF_STR2INCHI_ERR;
 
1656
    }
 
1657
    return err;
 
1658
}
 
1659
/*************************************************************************************/
 
1660
INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr)
 
1661
{
 
1662
    int ret = 0;
 
1663
    int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only;
 
1664
    int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only);
 
1665
    int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only);
 
1666
    
 
1667
    int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0;
 
1668
    int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0;
 
1669
    int nNumSb1 = s1? s1->nNumberOfStereoBonds   : 0;
 
1670
    int nNumSb2 = s2? s2->nNumberOfStereoBonds   : 0;
 
1671
    
 
1672
    if ( (nNumSc1 || nNumSc1) &&
 
1673
         ( nNumSc1 != nNumSc2 ||
 
1674
           memcmp( s1->nNumber,  s2->nNumber,  nNumSc1*sizeof(s1->nNumber[0] ) ) ||
 
1675
           memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) {
 
1676
 
 
1677
        num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0;
 
1678
        for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) {
 
1679
            if ( s1->nNumber[j1] ==  s2->nNumber[j2] ) {
 
1680
                if ( s1->t_parity[j1] == s2->t_parity[j2] ) {
 
1681
                    num_eq ++;
 
1682
                } else {
 
1683
                    num_dif ++;
 
1684
                }
 
1685
                j1 ++;
 
1686
                j2 ++;
 
1687
            } else
 
1688
            if ( s1->nNumber[j1] < s2->nNumber[j2] ) {
 
1689
                num_in1_only ++;
 
1690
                if ( s1->t_parity[j1] == AB_PARITY_UNDF ) {
 
1691
                    num_extra_undf ++;
 
1692
                }
 
1693
                if ( bAddSc ) {
 
1694
                    if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY )
 
1695
                        picr->sc_in1_only[picr->num_sc_in1_only ++] = j1;
 
1696
                    if ( s1->t_parity[j1] == AB_PARITY_UNDF ) {
 
1697
                        if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF )
 
1698
                            picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1;
 
1699
                    }
 
1700
                }
 
1701
                j1 ++;
 
1702
            } else {
 
1703
                num_in2_only ++;
 
1704
                if ( s2->t_parity[j2] == AB_PARITY_UNDF ) {
 
1705
                    num_miss_undf ++;
 
1706
                }
 
1707
                if ( bAddSc ) {
 
1708
                    if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY )
 
1709
                        picr->sc_in2_only[picr->num_sc_in2_only ++] = j2;
 
1710
                    if ( s2->t_parity[j2] == AB_PARITY_UNDF ) {
 
1711
                        if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF )
 
1712
                            picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1;
 
1713
                    }
 
1714
                }
 
1715
                j2 ++;
 
1716
            }
 
1717
        }
 
1718
        while ( j1 < nNumSc1 ) {
 
1719
            if ( s1->t_parity[j1] == AB_PARITY_UNDF ) {
 
1720
                num_extra_undf ++;
 
1721
            }
 
1722
            num_in1_only ++;
 
1723
            if ( bAddSc ) {
 
1724
                if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY )
 
1725
                    picr->sc_in1_only[picr->num_sc_in1_only ++] = j1;
 
1726
                if ( s1->t_parity[j1] == AB_PARITY_UNDF ) {
 
1727
                    if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF )
 
1728
                        picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1;
 
1729
                }
 
1730
            }
 
1731
            j1 ++;
 
1732
        }
 
1733
        while ( j2 < nNumSc2 ) {
 
1734
            if ( s2->t_parity[j2] == AB_PARITY_UNDF ) {
 
1735
                num_miss_undf ++;
 
1736
            }
 
1737
            num_in2_only ++;
 
1738
            if ( bAddSc ) {
 
1739
                if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY )
 
1740
                    picr->sc_in2_only[picr->num_sc_in2_only ++] = j2;
 
1741
            }
 
1742
            j2 ++;
 
1743
        }
 
1744
        if ( num_dif ) {
 
1745
            ret |= INCHIDIFF_SC_PARITY; 
 
1746
        }
 
1747
        if ( num_in1_only ) {
 
1748
            if ( num_extra_undf ) {
 
1749
                ret |= INCHIDIFF_SC_EXTRA_UNDF;
 
1750
            }
 
1751
            if ( num_in1_only != num_extra_undf ) {
 
1752
                ret |= INCHIDIFF_SC_EXTRA;
 
1753
            }
 
1754
        }
 
1755
        if ( num_in2_only ) {
 
1756
            if ( num_miss_undf ) {
 
1757
                ret |= INCHIDIFF_SC_MISS_UNDF;
 
1758
            }
 
1759
            if ( num_in2_only != num_miss_undf ) {
 
1760
                ret |= INCHIDIFF_SC_MISS;
 
1761
            }
 
1762
        }
 
1763
    }
 
1764
    if ( s1 && s2 && (s2->nCompInv2Abs != 2) && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) {
 
1765
        ret |= INCHIDIFF_SC_INV; /* 2007-07-13 DT: added (s2->nCompInv2Abs != 2) to fix bug reoprted by Yerin on 2007/02/28 */
 
1766
                                 /* Bug description: falsely reported "Stereo centers/allenes: Falsely inverted" for /S2 or /S3 */
 
1767
    }
 
1768
 
 
1769
    if ( (nNumSb1 || nNumSb2 ) &&
 
1770
         (nNumSb1 != nNumSb2 ||
 
1771
          memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) ||
 
1772
          memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) ||
 
1773
          memcmp( s1->b_parity,   s2->b_parity,   nNumSb1*sizeof(s1->b_parity[0]) ) ) ) {
 
1774
 
 
1775
        num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0;
 
1776
        for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) {
 
1777
            if ( s1->nBondAtom1[j1] ==  s2->nBondAtom1[j2] &&
 
1778
                 s1->nBondAtom2[j1] ==  s2->nBondAtom2[j2] ) {
 
1779
                if ( s1->b_parity[j1] == s2->b_parity[j2] ) {
 
1780
                    num_eq ++;
 
1781
                } else {
 
1782
                    num_dif ++;
 
1783
                }
 
1784
                j1 ++;
 
1785
                j2 ++;
 
1786
            } else
 
1787
            if ( s1->nBondAtom1[j1] <  s2->nBondAtom1[j2] ||
 
1788
                 s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] <  s2->nBondAtom2[j2]) {
 
1789
                num_in1_only ++;
 
1790
                if ( s1->b_parity[j1] == AB_PARITY_UNDF ) {
 
1791
                    num_extra_undf ++;
 
1792
                }
 
1793
                if ( bAddSb ) {
 
1794
                    if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY )
 
1795
                        picr->sb_in1_only[picr->num_sb_in1_only ++] = j1;
 
1796
                    if ( s1->b_parity[j1] == AB_PARITY_UNDF ) {
 
1797
                        if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF )
 
1798
                            picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1;
 
1799
                    }
 
1800
                }
 
1801
                j1 ++;
 
1802
            } else {
 
1803
                num_in2_only ++;
 
1804
                if ( s2->b_parity[j2] == AB_PARITY_UNDF ) {
 
1805
                    num_miss_undf ++;
 
1806
                }
 
1807
                if ( bAddSb ) {
 
1808
                    if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY )
 
1809
                        picr->sb_in2_only[picr->num_sb_in2_only ++] = j2;
 
1810
                    if ( s2->b_parity[j2] == AB_PARITY_UNDF ) {
 
1811
                        if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF )
 
1812
                            picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1;
 
1813
                    }
 
1814
                }
 
1815
                j2 ++;
 
1816
            }
 
1817
        }
 
1818
        while ( j1 < nNumSb1 ) {
 
1819
            num_in1_only ++;
 
1820
            if ( s1->b_parity[j1] == AB_PARITY_UNDF ) {
 
1821
                num_extra_undf ++;
 
1822
            }
 
1823
            if ( bAddSb ) {
 
1824
                if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY )
 
1825
                    picr->sb_in1_only[picr->num_sb_in1_only ++] = j1;
 
1826
                if ( s1->b_parity[j1] == AB_PARITY_UNDF ) {
 
1827
                    if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF )
 
1828
                        picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1;
 
1829
                }
 
1830
            }
 
1831
            j1 ++;
 
1832
        }
 
1833
        while ( j2 < nNumSb2 ) {
 
1834
            num_in2_only ++;
 
1835
            if ( s2->b_parity[j2] == AB_PARITY_UNDF ) {
 
1836
                num_miss_undf ++;
 
1837
            }
 
1838
            if ( bAddSb ) {
 
1839
                if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY )
 
1840
                    picr->sb_in2_only[picr->num_sb_in2_only ++] = j2;
 
1841
                if ( s2->b_parity[j2] == AB_PARITY_UNDF ) {
 
1842
                    if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF )
 
1843
                        picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1;
 
1844
                }
 
1845
            }
 
1846
            j2 ++;
 
1847
        }
 
1848
        if ( num_dif ) {
 
1849
            ret |= INCHIDIFF_SB_PARITY; 
 
1850
        }
 
1851
        if ( num_in1_only ) {
 
1852
            if ( num_extra_undf ) {
 
1853
                ret |= INCHIDIFF_SB_EXTRA_UNDF;
 
1854
            }
 
1855
            if ( num_in1_only != num_extra_undf ) {
 
1856
                ret |= INCHIDIFF_SB_EXTRA;
 
1857
            }
 
1858
        }
 
1859
        if ( num_in2_only ) {
 
1860
            if ( num_miss_undf ) {
 
1861
                ret |= INCHIDIFF_SB_MISS_UNDF;
 
1862
            }
 
1863
            if ( num_in2_only != num_miss_undf ) {
 
1864
                ret |= INCHIDIFF_SB_MISS;
 
1865
            }
 
1866
        }
 
1867
    }
 
1868
 
 
1869
    return ret;
 
1870
}
 
1871
/*********************************************************************************************************/
 
1872
INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */,
 
1873
                                  INChI_Aux *a1, INChI_Aux *a2, int *err )
 
1874
{
 
1875
    INCHI_MODE ret = 0;
 
1876
    INChI_Stereo *Stereo1=NULL, *Stereo2=NULL;
 
1877
    int  n1, n2, m, j, j1, j2, ret2, num_H1, num_H2;
 
1878
    ICR icr;
 
1879
    ICR *picr = &icr;
 
1880
    
 
1881
    *err = 0;
 
1882
 
 
1883
    memset( picr, 0, sizeof(*picr) );
 
1884
 
 
1885
    if ( i1 == NULL && i2 == NULL )
 
1886
        return 0;
 
1887
    if ( (i1 == NULL) ^ (i2 == NULL) ) {
 
1888
        ret |= INCHIDIFF_PROBLEM; /* one InChI exists while another doesn't */
 
1889
        goto exit_function;
 
1890
    }
 
1891
    
 
1892
    if ( i1->nErrorCode == i2->nErrorCode ) {
 
1893
        if ( i1->nErrorCode ) {
 
1894
            ret |= INCHIDIFF_PROBLEM; /* both InChI have same error codes */
 
1895
            goto exit_function;
 
1896
        }
 
1897
    } else {
 
1898
        ret |= INCHIDIFF_PROBLEM; /* at least one InChI has an error code */
 
1899
        goto exit_function;
 
1900
    }
 
1901
    
 
1902
    if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) {
 
1903
        ret |= INCHIDIFF_NUM_AT;
 
1904
        goto exit_function;
 
1905
    }
 
1906
    if ( i1->nNumberOfAtoms > 0 ) {
 
1907
        if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) {
 
1908
            ret |= INCHIDIFF_ATOMS;
 
1909
            goto exit_function;
 
1910
        }
 
1911
        /* INCHIDIFF_NON_TAUT_H,  INCHIDIFF_MORE_FH, INCHIDIFF_LESS_FH */
 
1912
        if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) {
 
1913
            ret |= INCHIDIFF_POSITION_H;
 
1914
            for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) {
 
1915
                if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) {
 
1916
                    picr->diff_pos_H_at[picr->num_diff_pos_H] = j1;
 
1917
                    picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1];
 
1918
                    picr->num_diff_pos_H ++;
 
1919
                }
 
1920
            }
 
1921
        }
 
1922
        /* fixed H */
 
1923
        if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) {
 
1924
            int bHasFixedH1 = 0, bHasFixedH2 = 0, i;
 
1925
            if ( i1->nNum_H_fixed ) {
 
1926
                for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) {
 
1927
                    if ( i1->nNum_H_fixed[i] ) {
 
1928
                        bHasFixedH1 ++;
 
1929
                    }
 
1930
                }
 
1931
            }
 
1932
            if ( i2->nNum_H_fixed ) {
 
1933
                for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) {
 
1934
                    if ( i2->nNum_H_fixed[i] ) {
 
1935
                        bHasFixedH2 ++;
 
1936
                    }
 
1937
                }
 
1938
            }
 
1939
            if ( bHasFixedH1 && !bHasFixedH2 ) {
 
1940
                for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) {
 
1941
                    if ( i1->nNum_H_fixed[i] ) {
 
1942
                        if ( j < ICR_MAX_DIFF_FIXED_H ) {
 
1943
                            picr->fixed_H_at1_more[j] = i;
 
1944
                            picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i];
 
1945
                            j ++;
 
1946
                        }
 
1947
                    }
 
1948
                }
 
1949
                picr->num_fixed_H1_more = j;
 
1950
                ret |= INCHIDIFF_MORE_FH; /* Extra Fixed-H */
 
1951
            } else
 
1952
            if ( !bHasFixedH1 && bHasFixedH2 ) {
 
1953
                for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) {
 
1954
                    if ( i2->nNum_H_fixed[i] ) {
 
1955
                        if ( j < ICR_MAX_DIFF_FIXED_H ) {
 
1956
                            picr->fixed_H_at2_more[j] = i;
 
1957
                            picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i];
 
1958
                            j ++;
 
1959
                        }
 
1960
                    }
 
1961
                }
 
1962
                picr->num_fixed_H2_more = j;
 
1963
                ret |= INCHIDIFF_LESS_FH; /* Missed Fixed-H */
 
1964
            } else
 
1965
            if ( bHasFixedH1 && bHasFixedH2 &&
 
1966
                 memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) {
 
1967
                for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) {
 
1968
                    if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) {
 
1969
                        if ( j1 < ICR_MAX_DIFF_FIXED_H ) {
 
1970
                            picr->fixed_H_at1_more[j1] = i;
 
1971
                            picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i];
 
1972
                            j1 ++;
 
1973
                        }
 
1974
                    } else
 
1975
                    if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) {
 
1976
                        if ( j2 < ICR_MAX_DIFF_FIXED_H ) {
 
1977
                            picr->fixed_H_at2_more[j2] = i;
 
1978
                            picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i];
 
1979
                            j2 ++;
 
1980
                        }
 
1981
                    }
 
1982
                }
 
1983
                ret |= (j1? INCHIDIFF_MORE_FH:0) | (j2? INCHIDIFF_LESS_FH:0);
 
1984
                picr->num_fixed_H1_more = j1;
 
1985
                picr->num_fixed_H2_more = j2;
 
1986
            }
 
1987
        }
 
1988
    }
 
1989
    /* compare formulas and H */
 
1990
    num_H1 = 0;
 
1991
    num_H2 = 0;
 
1992
    ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 );
 
1993
    picr->tot_num_H1 = num_H1;
 
1994
    picr->tot_num_H2 = num_H2;
 
1995
    if ( ret2 ) {
 
1996
        ret |= INCHIDIFF_NUM_EL;
 
1997
        goto exit_function;
 
1998
    }
 
1999
    if ( num_H1 > num_H2 ) {
 
2000
        ret |= INCHIDIFF_MORE_H;
 
2001
    }
 
2002
    if ( num_H1 < num_H2 ) {
 
2003
        ret |= INCHIDIFF_LESS_H;
 
2004
    }
 
2005
 
 
2006
    if ( i1->lenConnTable != i2->lenConnTable ) {
 
2007
        ret |= INCHIDIFF_CON_LEN;
 
2008
        goto exit_function;
 
2009
    } else
 
2010
    if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) {
 
2011
        ret |= INCHIDIFF_CON_TBL;
 
2012
        goto exit_function;
 
2013
    }
 
2014
    /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */
 
2015
    /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */
 
2016
/*
 
2017
    if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) {
 
2018
        ret |=  INCHIDIFF_TAUT_LEN; 
 
2019
    }
 
2020
*/
 
2021
    /* compare number of t-groups */
 
2022
    n1 = i1->lenTautomer? i1->nTautomer[0] : 0;
 
2023
    n2 = i2->lenTautomer? i2->nTautomer[0] : 0;
 
2024
    if ( !n1 && n2 ) {
 
2025
        ret |= INCHIDIFF_NO_TAUT;
 
2026
    } else
 
2027
    if ( n1 && !n2 ) {
 
2028
        ret |= INCHIDIFF_WRONG_TAUT;
 
2029
    } else
 
2030
    if ( n1 == 1 && n2 > 1 ) {
 
2031
        ret |= INCHIDIFF_SINGLE_TG;
 
2032
    } else
 
2033
    if ( n1 > 1 && n2 == 1 ) {
 
2034
        ret |= INCHIDIFF_MULTIPLE_TG;
 
2035
    } else
 
2036
    if ( n1 != n2 ) {
 
2037
        ret |= INCHIDIFF_NUM_TG;
 
2038
    }
 
2039
    if ( n1 || n2 ) {
 
2040
        /* number of endpoints */
 
2041
        int num1 = 0, num2 = 0, num_M1=0, num_M2=0;
 
2042
        int len, num_eq, num_in1_only, num_in2_only;
 
2043
        AT_NUMB *pe1 = (AT_NUMB *)inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) );
 
2044
        AT_NUMB *pe2 = (AT_NUMB *)inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) );
 
2045
        num_H1 = num_H2=0;
 
2046
        /* collect endpoints, H, (-) */
 
2047
        if ( !pe1 || !pe2 ) {
 
2048
            if ( pe1 ) inchi_free( pe1 );
 
2049
            if ( pe2 ) inchi_free( pe2 );
 
2050
            *err = RI_ERR_ALLOC; /* allocation error */
 
2051
            goto exit_function;
 
2052
        }
 
2053
        for ( m = 1; m < i1->lenTautomer; m += len ) {
 
2054
            len = i1->nTautomer[m ++];
 
2055
            num_H1 += i1->nTautomer[m];
 
2056
            num_M1 += i1->nTautomer[m+1];
 
2057
            for ( j = 2; j < len; j ++ ) {
 
2058
                pe1[num1 ++] = i1->nTautomer[m + j];
 
2059
            }
 
2060
        }
 
2061
        for ( m = 1; m < i2->lenTautomer; m += len ) {
 
2062
            len = i2->nTautomer[m ++];
 
2063
            num_H2 += i2->nTautomer[m];
 
2064
            num_M2 += i2->nTautomer[m+1];
 
2065
            for ( j = 2; j < len; j ++ ) {
 
2066
                pe2[num2 ++] = i2->nTautomer[m + j];
 
2067
            }
 
2068
        }
 
2069
        picr->num_taut_H1 = num_H1;
 
2070
        picr->num_taut_H2 = num_H2;
 
2071
        picr->num_taut_M1 = num_M1;
 
2072
        picr->num_taut_M2 = num_M2;
 
2073
        /* sort endpoints */
 
2074
        insertions_sort_AT_NUMB( pe1, num1 );
 
2075
        insertions_sort_AT_NUMB( pe2, num2 );
 
2076
        /* compare */
 
2077
        /*
 
2078
        if ( num1 < num2 ) {
 
2079
            ret |= INCHIDIFF_LESS_TG_ENDP;
 
2080
        } else
 
2081
        if ( num1 > num2 ) {
 
2082
            ret |= INCHIDIFF_MORE_TG_ENDP;
 
2083
        }
 
2084
        */
 
2085
        /* compare all */
 
2086
        num_eq = num_in1_only = num_in2_only = 0;
 
2087
        for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) {
 
2088
            if( pe1[j1] == pe2[j2] ) {
 
2089
                j1 ++;
 
2090
                j2 ++;
 
2091
                num_eq ++;
 
2092
            } else
 
2093
            if ( pe1[j1] < pe2[j1] ) {
 
2094
                if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) {
 
2095
                    picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1];
 
2096
                }
 
2097
                j1 ++;
 
2098
                num_in1_only ++;
 
2099
            } else {
 
2100
                if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) {
 
2101
                    picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2];
 
2102
                }
 
2103
                j2 ++;
 
2104
                num_in2_only ++;
 
2105
            }
 
2106
        }
 
2107
        while ( j1 < num1 ) {
 
2108
            if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) {
 
2109
                picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1];
 
2110
            }
 
2111
            j1 ++;
 
2112
            num_in1_only ++;
 
2113
        }
 
2114
        while ( j2 < num2 ) {
 
2115
            if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) {
 
2116
                picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2];
 
2117
            }
 
2118
            j2 ++;
 
2119
            num_in2_only ++;
 
2120
        }
 
2121
        if ( num_in1_only ) {
 
2122
            ret |= INCHIDIFF_EXTRA_TG_ENDP;
 
2123
        }
 
2124
        if ( num_in2_only ) {
 
2125
            ret |= INCHIDIFF_MISS_TG_ENDP;
 
2126
        }
 
2127
        if ( !num_in1_only && !num_in2_only && num_eq ) {
 
2128
           ; /* same t-groups endpoints */
 
2129
        } else {
 
2130
           ret |= INCHIDIFF_DIFF_TG_ENDP;
 
2131
        }
 
2132
        inchi_free( pe1 );
 
2133
        inchi_free( pe2 );
 
2134
 
 
2135
    }
 
2136
 
 
2137
    if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) &&
 
2138
         ( i1->lenTautomer != i2->lenTautomer ||
 
2139
         memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) )
 
2140
        ret |= INCHIDIFF_TG;
 
2141
 
 
2142
    if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) {
 
2143
        ret |= INCHIDIFF_NUM_ISO_AT;
 
2144
    } else
 
2145
    if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) )
 
2146
        ret |= INCHIDIFF_ISO_AT;
 
2147
    if ( i1->nTotalCharge != i2->nTotalCharge )
 
2148
        ret |= INCHIDIFF_CHARGE;
 
2149
    if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) {
 
2150
        ret |= INCHIDIFF_REM_PROT;
 
2151
    }
 
2152
    if ( a1 && (!a2 || 
 
2153
         a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] ||
 
2154
         a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] ||
 
2155
         a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) {
 
2156
        ret |= INCHIDIFF_REM_ISO_H;
 
2157
    }
 
2158
 
 
2159
/*
 
2160
    if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) {
 
2161
        if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] ||
 
2162
             memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH,
 
2163
                    sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) )
 
2164
            return 18;
 
2165
    } else
 
2166
    if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) {
 
2167
        return 19;
 
2168
    }
 
2169
*/
 
2170
    if ( i1->StereoIsotopic &&
 
2171
         i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) {
 
2172
        Stereo1 = i1->StereoIsotopic;
 
2173
    } else {
 
2174
        Stereo1 = i1->Stereo;
 
2175
    }
 
2176
    if ( i2->StereoIsotopic &&
 
2177
         i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) {
 
2178
        Stereo2 = i2->StereoIsotopic;
 
2179
    } else {
 
2180
        Stereo2 = i2->Stereo;
 
2181
    }
 
2182
    ret |= CompareReversedStereoINChI3( Stereo1, Stereo2, picr );
 
2183
 
 
2184
exit_function:
 
2185
 
 
2186
    picr->flags = ret;
 
2187
 
 
2188
    return ret;
 
2189
}
 
2190
/* message group names */
 
2191
CMP_INCHI_MSG_GROUP CompareInchiMsgsGroup[] = {
 
2192
{IDGRP_ERR,     " Error:"},
 
2193
{IDGRP_H,       " Hydrogens:"},
 
2194
{IDGRP_MOB_GRP, " Mobile-H groups:"},
 
2195
{IDGRP_ISO_AT,  " Isotopic:"},
 
2196
{IDGRP_CHARGE,  " Charge(s):"},
 
2197
{IDGRP_PROTONS, " Proton balance:"},
 
2198
{IDGRP_ISO_H,   " Exchangeable isotopic H:"},
 
2199
{IDGRP_SC,      " Stereo centers/allenes:"},
 
2200
{IDGRP_SB,      " Stereobonds/cumulenes:"},
 
2201
{IDGRP_HLAYER,  " Fixed-H layer:"},
 
2202
{IDGRP_COMP,    " Number of components:"},
 
2203
{IDGRP_CONV_ERR," Conversion encountered:"},
 
2204
{IDGRP_ZERO,    ""}
 
2205
};
 
2206
/* messages */
 
2207
CMP_INCHI_MSG  CompareInchiMsgs[] = {
 
2208
{INCHIDIFF_PROBLEM      ,IDGRP_ERR,     " Wrong result"                   }, /*0x00000001,  severe: at least one InChI does not exist */
 
2209
{INCHIDIFF_POSITION_H   ,IDGRP_H,       " Locations or number"            }, /*0x00000002,  difference in non-taut {Mobile-H} or all H {Fixed-H} location/number */
 
2210
{INCHIDIFF_MORE_FH      ,IDGRP_H,       " Fixed-H"                        }, /*0x00000004,  extra fixed H */
 
2211
{INCHIDIFF_LESS_FH      ,IDGRP_H,       " Fixed-H"                        }, /*0x00000004,  missing fixed H */
 
2212
{INCHIDIFF_MORE_H       ,IDGRP_H,       " Number"                         }, /*0x00000008,  formulas differ in number of H */
 
2213
{INCHIDIFF_LESS_H       ,IDGRP_H,       " Number"                         }, /*0x00000008,  formulas differ in number of H */
 
2214
{INCHIDIFF_NO_TAUT      ,IDGRP_MOB_GRP, " Missing"                        }, /*0x00000010,  restored structure has no taut groups while the original InChI has some */
 
2215
{INCHIDIFF_WRONG_TAUT   ,IDGRP_MOB_GRP, " Falsely present"                }, /*0x00000020,  restored has tautomerism while the original does not have it */
 
2216
{INCHIDIFF_SINGLE_TG    ,IDGRP_MOB_GRP, " One instead of multiple"        }, /*0x00000040,  restored has 1 taut. group while the original InChI has multiple tg */
 
2217
{INCHIDIFF_MULTIPLE_TG  ,IDGRP_MOB_GRP, " Multiple instead of one"        }, /*0x00000080,  restored has multiple tg while the original InChI has only one tg */
 
2218
{INCHIDIFF_EXTRA_TG_ENDP,IDGRP_MOB_GRP, " Attachment points"              }, /*0x00000100,  extra tautomeric endpoint{s} in restored structure */
 
2219
{INCHIDIFF_MISS_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points"              }, /*0x00000100,  one or more tg endpoint is not in the restored structure */
 
2220
{INCHIDIFF_DIFF_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points"              }, /*0x00000100,  lists of tg endpoints are different */
 
2221
{INCHIDIFF_NUM_TG       ,IDGRP_MOB_GRP, " Number"                         }, /*0x00000200,  different number of tautomeric groups */
 
2222
{INCHIDIFF_TG           ,IDGRP_MOB_GRP, " Do not match"                   }, /*0x00000200,  different tautomeric groups */
 
2223
{INCHIDIFF_NUM_ISO_AT   ,IDGRP_ISO_AT,  " Atoms do not match"             }, /*0x00000400,  ?severe: restored struct. has different number of isotopic atoms */
 
2224
{INCHIDIFF_ISO_AT       ,IDGRP_ISO_AT,  " Atoms do not match"             }, /*0x00000400,  ?severe: restored struct. has different locations/isotopes of isotopic atoms */
 
2225
{INCHIDIFF_REM_ISO_H    ,IDGRP_ISO_H,   " Does not match for a component" }, /*0x00000800,  isotopic H removed */
 
2226
{INCHIDIFF_MOB_ISO_H    ,IDGRP_ISO_H,   " Do not match"                   }, /*0x00001000,  different number of mobile exchangeable isotopic H */
 
2227
{INCHIDIFF_CHARGE       ,IDGRP_CHARGE,  " Do not match"                   }, /*0x00002000,  restored structure has different charge */
 
2228
{INCHIDIFF_REM_PROT     ,IDGRP_PROTONS, " Does not match for a component" }, /*0x00004000,  proton{s} removed/added from the restored structure */
 
2229
{INCHIDIFF_MOBH_PROTONS ,IDGRP_PROTONS, " Does not match"                 }, /*0x00008000,  different proton balance */
 
2230
{INCHIDIFF_SC_INV       ,IDGRP_SC,      " Falsely inverted"               }, /*0x00010000,  restores structure has different inversion stereocenter mark */
 
2231
{INCHIDIFF_SC_PARITY    ,IDGRP_SC,      " Wrong parity"                   }, /*0x00020000,  restored structure has stereoatoms or allenes with different parity */
 
2232
{INCHIDIFF_SC_EXTRA_UNDF,IDGRP_SC,      " Extra undefined"                }, /*0x00040000,  restored structure has extra undefined stereocenter{s} */
 
2233
{INCHIDIFF_SC_EXTRA     ,IDGRP_SC,      " Extra known"                    }, /*0x00080000,  restored structure has extra stereocenter{s} */
 
2234
{INCHIDIFF_SC_MISS_UNDF ,IDGRP_SC,      " Missing undefined"              }, /*0x00100000,  restored structure has not some undefined stereocenter{s} */
 
2235
{INCHIDIFF_SC_MISS      ,IDGRP_SC,      " Missing known"                  }, /*0x00200000,  restored structure has not some stereocenters that are not undefined */
 
2236
{INCHIDIFF_SB_PARITY    ,IDGRP_SB,      " Wrong parity"                   }, /*0x00400000,  restored structure has stereobonds or cumulenes with different parity */
 
2237
{INCHIDIFF_SB_EXTRA_UNDF,IDGRP_SB,      " Extra undefined"                }, /*0x00800000,  restored structure has extra undefined stereobond{s} */
 
2238
{INCHIDIFF_SB_EXTRA     ,IDGRP_SB,      " Missing known"                  }, /*0x01000000,  restored structure has extra stereobond{s} */
 
2239
{INCHIDIFF_SB_MISS_UNDF ,IDGRP_SB,      " Missing undefined"              }, /*0x02000000,  restored structure has not some undefined stereocenters */
 
2240
{INCHIDIFF_SB_MISS      ,IDGRP_SB,      " Missing known"                  }, /*0x04000000,  restored structure has not some stereobonds that are not undefined */
 
2241
{INCHIDIFF_COMP_HLAYER  ,IDGRP_HLAYER,  " Missing or extra"               }, /*0x08000000,  Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */
 
2242
{INCHIDIFF_COMP_NUMBER  ,IDGRP_COMP,    " Does not match"                 }, /*0x10000000,  wrong number of components */
 
2243
{INCHIDIFF_STR2INCHI_ERR,IDGRP_CONV_ERR," Error"                          },  /*0x20000000   Restored structure to InChI conversion error */
 
2244
{INCHIDIFF_ZERO         ,IDGRP_ZERO,    ""                                }
 
2245
};
 
2246
 
 
2247
/*************************************************************************/
 
2248
int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim )
 
2249
{
 
2250
    const char ellip[] = "...";
 
2251
    int len = strlen( szAddMsg );
 
2252
    int len_delim = (used_len && szDelim)? strlen(szDelim) : 0;
 
2253
    int len_to_copy;
 
2254
    if ( len + len_delim + used_len < tot_len ) {
 
2255
        if ( len_delim ) {
 
2256
            strcpy( szMsg+used_len, szDelim );
 
2257
            used_len += len_delim;
 
2258
        }
 
2259
        strcpy( szMsg+used_len, szAddMsg );
 
2260
        used_len += len;
 
2261
    } else
 
2262
    if ( (len_to_copy = (tot_len - used_len - len_delim - (int)sizeof(ellip))) > 10 ) {
 
2263
        if ( len_delim ) {
 
2264
            strcpy( szMsg+used_len, szDelim );
 
2265
            used_len += len_delim;
 
2266
        }
 
2267
        strncpy( szMsg+used_len, szAddMsg, len_to_copy );
 
2268
        used_len += len_to_copy;
 
2269
        strcpy( szMsg+used_len, ellip );
 
2270
        used_len += sizeof( ellip ) - 1;
 
2271
    }
 
2272
    return used_len;
 
2273
}
 
2274
/*************************************************************************/
 
2275
int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] )
 
2276
{
 
2277
    int bMobileH, k, n, len = strlen( szMsg );
 
2278
    int iPrevGrpIdx, iCurGrpIdx, bFound;
 
2279
    INCHI_MODE bit;
 
2280
    static const char *hdr = " Problems/mismatches:";
 
2281
    char szOneMsg[256];
 
2282
    if ( bits[TAUT_YES] || bits[TAUT_NON] ) {
 
2283
        if ( !strstr( szMsg, hdr ) ) {
 
2284
            len = AddOneMsg( szMsg, len, nLenMsg, hdr, NULL );
 
2285
        }
 
2286
        for ( bMobileH = TAUT_YES; 0 <= bMobileH; bMobileH -- ) {
 
2287
            if ( bits[bMobileH] ) {
 
2288
                strcpy( szOneMsg, bMobileH==TAUT_YES? " Mobile-H(" : " Fixed-H(" );
 
2289
                len = AddOneMsg( szMsg, len, nLenMsg, szOneMsg, NULL );
 
2290
            }
 
2291
            bit = 1;
 
2292
            iPrevGrpIdx = -1;
 
2293
            do {
 
2294
                if ( bit & bits[bMobileH] ) {
 
2295
                    /* search for the message */
 
2296
                    bFound = 0;
 
2297
                    for ( k = 0; CompareInchiMsgs[k].nBit != INCHIDIFF_ZERO && !bFound; k ++ ) {
 
2298
                        if ( bit & (INCHI_MODE)CompareInchiMsgs[k].nBit ) {
 
2299
                            /* message found */
 
2300
                            for ( n = 0; CompareInchiMsgsGroup[n].nGroupID != IDGRP_ZERO; n ++ ) {
 
2301
                                if ( CompareInchiMsgsGroup[n].nGroupID == CompareInchiMsgs[k].nGroupID ) {
 
2302
                                    iCurGrpIdx = n;
 
2303
                                    if ( iCurGrpIdx != iPrevGrpIdx ) {
 
2304
                                        if ( iPrevGrpIdx >= 0 ) {
 
2305
                                            len = AddOneMsg( szMsg, len, nLenMsg, ";", NULL );
 
2306
                                        }
 
2307
                                        len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgsGroup[iCurGrpIdx].szGroupName, NULL );
 
2308
                                    }
 
2309
                                    len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgs[k].szMsg, iCurGrpIdx == iPrevGrpIdx? ",":NULL );
 
2310
                                    iPrevGrpIdx = iCurGrpIdx;
 
2311
                                    bFound = 1;
 
2312
                                    break;
 
2313
                                }
 
2314
                            }
 
2315
                        }
 
2316
                    }
 
2317
                }
 
2318
                bit <<= 1;
 
2319
            } while ( bit );
 
2320
            if ( bits[bMobileH] ) {
 
2321
                len = AddOneMsg( szMsg, len, nLenMsg, ")", NULL );
 
2322
            }
 
2323
        }
 
2324
    }
 
2325
    return len;
 
2326
}
 
2327
 
 
2328
#endif