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

« back to all changes in this revision

Viewing changes to src/formats/libinchi/ichirvr2.c

  • Committer: Package Import Robot
  • Author(s): Daniel Leidert
  • Date: 2013-05-22 19:08:27 UTC
  • mfrom: (1.1.11) (7.1.9 sid)
  • Revision ID: package-import@ubuntu.com-20130522190827-72q0fnx5y2nm3bc0
Tags: 2.3.2+dfsg-1
* New upstream release.
* debian/control: Dropped DM-Upload-Allowed field.
  (Standards-Version): Bumped to 3.9.4.
* debian/copyright: Massive update.
* debian/upstream: Author name update.
* debian/get-orig-source.sh: Remove the windows-*/ directory too.
* debian/openbabel.install: Removed roundtrip manpage.
* debian/openbabel-gui.install: Fixed manpage name.
* debian/openbabel-gui.links: Removed unused file.
* debian/rules: Enable OpenMP. Disable tests on `nocheck'.
* debian/patches/gaussformat_nosym.patch: Dropped. Applied upstream.
* debian/patches/moldenformat_coordonly.patch: Ditto.
* debian/patches/obspectrophore_man.patch: Ditto.
* debian/patches/fix_ftbfs.patch: Added.
  - Fix several FTBFS issues in upstream build system.
* debian/patches/series: Adjusted.

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.04
 
5
 * September 9, 2011
 
6
 *
 
7
 * The InChI library and programs are free software developed under the
 
8
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
 
9
 * Originally developed at NIST. Modifications and additions by IUPAC 
 
10
 * and the InChI Trust.
 
11
 *
 
12
 * IUPAC/InChI-Trust Licence for the International Chemical Identifier (InChI) 
 
13
 * Software version 1.0.
 
14
 * Copyright (C) IUPAC and InChI Trust Limited
 
15
 * 
 
16
 * This library is free software; you can redistribute it and/or modify it under the 
 
17
 * terms of the IUPAC/InChI Trust Licence for the International Chemical Identifier 
 
18
 * (InChI) Software version 1.0; either version 1.0 of the License, or 
 
19
 * (at your option) any later version.
 
20
 * 
 
21
 * This library is distributed in the hope that it will be useful, 
 
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 
24
 * See the IUPAC/InChI Trust Licence for the International Chemical Identifier (InChI) 
 
25
 * Software version 1.0 for more details.
 
26
 * 
 
27
 * You should have received a copy of the IUPAC/InChI Trust Licence for the 
 
28
 * International Chemical Identifier (InChI) Software version 1.0 along with 
 
29
 * this library; if not, write to:
 
30
 * 
 
31
 * The InChI Trust
 
32
 * c/o FIZ CHEMIE Berlin
 
33
 * Franklinstrasse 11
 
34
 * 10587 Berlin
 
35
 * GERMANY
 
36
 * 
 
37
 */
 
38
 
 
39
 
 
40
#include <stdio.h>
 
41
#include <stdlib.h>
 
42
#include <string.h>
 
43
 
 
44
/*^^^ */
 
45
/*#define CHECK_WIN32_VC_HEAP*/
 
46
#include "mode.h"
 
47
 
 
48
#if ( READ_INCHI_STRING == 1 )
 
49
 
 
50
#include "ichi.h"
 
51
#include "ichitime.h"
 
52
 
 
53
#include "inpdef.h"
 
54
#include "ichimain.h"
 
55
#include "ichierr.h"
 
56
#include "incomdef.h" 
 
57
#include "ichiring.h"
 
58
#include "extr_ct.h"
 
59
#include "ichitaut.h"
 
60
#include "ichinorm.h"
 
61
#include "util.h"
 
62
 
 
63
#include "ichicomp.h"
 
64
#include "ichister.h"
 
65
 
 
66
#include "ichi_bns.h"
 
67
 
 
68
#include "strutil.h"
 
69
 
 
70
#include "ichirvrs.h"
 
71
 
 
72
/******************************************************************************************************/
 
73
void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms )
 
74
{
 
75
    int i;
 
76
    for ( i = 0; i < num_atoms; i ++ ) {
 
77
        if ( at[i].p_parity ) {
 
78
            memcpy( st[i].p_orig_at_num, at[i].p_orig_at_num, sizeof(st[0].p_orig_at_num) );
 
79
            st[i].p_parity = at[i].p_parity;
 
80
        }
 
81
        if ( at[i].sb_parity[0] ) {
 
82
            memcpy( st[i].sb_ord, at[i].sb_ord, sizeof(st[0].sb_ord) );
 
83
            memcpy( st[i].sb_parity, at[i].sb_parity, sizeof(st[0].sb_parity) );
 
84
            memcpy( st[i].sn_ord, at[i].sn_ord, sizeof(st[0].sn_ord) );
 
85
            memcpy( st[i].sn_orig_at_num, at[i].sn_orig_at_num, sizeof(st[0].sn_orig_at_num) );
 
86
        }
 
87
    }
 
88
}
 
89
void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms )
 
90
{
 
91
    int i;
 
92
    if ( !st ) {
 
93
        return;
 
94
    }
 
95
    for ( i = 0; i < num_atoms; i ++ ) {
 
96
        if ( st[i].p_parity ) {
 
97
            memcpy( at[i].p_orig_at_num, st[i].p_orig_at_num, sizeof(at[0].p_orig_at_num) );
 
98
            at[i].p_parity = st[i].p_parity;
 
99
        }
 
100
        if ( st[i].sb_parity[0] ) {
 
101
            memcpy( at[i].sb_ord, st[i].sb_ord, sizeof(st[0].sb_ord) );
 
102
            memcpy( at[i].sb_parity, st[i].sb_parity, sizeof(at[0].sb_parity) );
 
103
            memcpy( at[i].sn_ord, st[i].sn_ord, sizeof(at[0].sn_ord) );
 
104
            memcpy( at[i].sn_orig_at_num, st[i].sn_orig_at_num, sizeof(at[0].sn_orig_at_num) );
 
105
        }
 
106
    }
 
107
}
 
108
 
 
109
/******************************************************************************************************/
 
110
int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH)
 
111
{
 
112
    inp_ATOM     *at  = NULL;
 
113
    inp_ATOM_STEREO * st = NULL;
 
114
    int           num_atoms, i, jv, jn, n_vertex, n_neigh, num_H, parity;
 
115
    int           nNumDeletedH=0, iDeletedH=0, idelH1, idelH2, ret = 0, len;
 
116
    int           num_stereo_bonds, num_stereo_centers, num_stereo_bonds2, num_stereo_centers2;
 
117
    INChI_Stereo *pStereo = NULL, *pStereo2 = NULL;
 
118
    AT_NUMB       nCumulene[MAX_CUMULENE_LEN+2];
 
119
 
 
120
    num_atoms = pInChI->nNumberOfAtoms;
 
121
    if ( num_atoms <= 0 ) {
 
122
        return 0;
 
123
    }
 
124
    INCHI_HEAPCHK
 
125
    /* atoms */
 
126
    pStruct->at = at = (inp_ATOM *) inchi_calloc ( num_atoms, sizeof(pStruct->at[0]) );
 
127
    if ( !at ) {
 
128
        ret = RI_ERR_ALLOC;
 
129
        goto exit_function;
 
130
    }
 
131
    pStruct->num_atoms = num_atoms;
 
132
    /* charge */
 
133
    pStruct->charge = pInChI->nTotalCharge;
 
134
    /* elements, terminal atoms H */
 
135
    for ( i = 0; i < num_atoms; i ++ ) {
 
136
        at[i].el_number = pInChI->nAtom[i];
 
137
        if ( GetElementFormulaFromAtNum(UCINT pInChI->nAtom[i], at[i].elname ) ) {
 
138
            ret = RI_ERR_PROGR;
 
139
            goto exit_function;
 
140
        }
 
141
        at[i].orig_at_number = iAtNoOffset + i+1;
 
142
        at[i].orig_compt_at_numb = i + 1;
 
143
        at[i].component = iComponent + 1;
 
144
        num_H = pInChI->nNum_H[i];
 
145
        /* --- pInChI->nNum_H_fixed[i] was added to pInChI->nNum_H[i] ---
 
146
        if ( pInChI->nNum_H_fixed ) {
 
147
            num_H += pInChI->nNum_H_fixed[i];
 
148
        }
 
149
        */
 
150
        at[i].num_H = num_H;
 
151
    }
 
152
    INCHI_HEAPCHK
 
153
    /* connections */
 
154
    for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) {
 
155
        if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) {
 
156
            /*  vertex - neighbor connection */
 
157
            jv = at[n_vertex].valence ++;
 
158
            at[n_vertex].neighbor[jv] = n_neigh;
 
159
            at[n_vertex].bond_type[jv] = BOND_TYPE_SINGLE;
 
160
            at[n_vertex].chem_bonds_valence += at[n_vertex].bond_type[jv];
 
161
            /*  neighbor - vertex connection */
 
162
            jn = at[n_neigh].valence ++;
 
163
            at[n_neigh].neighbor[jn] = n_vertex;
 
164
            at[n_neigh].bond_type[jn] = BOND_TYPE_SINGLE;
 
165
            at[n_neigh].chem_bonds_valence += at[n_neigh].bond_type[jn];
 
166
        } else
 
167
        if ( (n_vertex = n_neigh) >= num_atoms ) {
 
168
            ret = RI_ERR_PROGR;
 
169
            goto exit_function;
 
170
        }
 
171
    }
 
172
    INCHI_HEAPCHK
 
173
    /* isotopic atoms */
 
174
    if ( pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms ) {
 
175
        for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) {
 
176
            n_vertex = pInChI->IsotopicAtom[i].nAtomNumber-1;
 
177
            at[n_vertex].iso_atw_diff = (char)pInChI->IsotopicAtom[i].nIsoDifference;
 
178
            at[n_vertex].num_iso_H[0] = (char)pInChI->IsotopicAtom[i].nNum_H;
 
179
            at[n_vertex].num_iso_H[1] = (char)pInChI->IsotopicAtom[i].nNum_D;
 
180
            at[n_vertex].num_iso_H[2] = (char)pInChI->IsotopicAtom[i].nNum_T;
 
181
        }
 
182
        pStruct->bIsotopic |= 1;
 
183
    }
 
184
    INCHI_HEAPCHK
 
185
    /* tautomeric groups */
 
186
    if ( ret = GetTgroupInfoFromInChI( &pStruct->ti, at, NULL, pInChI ) ) {
 
187
        goto exit_function;
 
188
    }
 
189
 
 
190
    /* coordinates: data from unused members: pInChI->IsotopicTGroup and InChI->nNumberOfIsotopicTGroups */
 
191
    if ( pInChI->IsotopicTGroup && !pInChI->nNumberOfIsotopicTGroups ) {
 
192
        pStruct->pXYZ = (XYZ_COORD *) pInChI->IsotopicTGroup;
 
193
        pInChI->IsotopicTGroup = NULL;
 
194
    }
 
195
    /* stereo */
 
196
    if ( pInChI->StereoIsotopic && 
 
197
         (pInChI->StereoIsotopic->nNumberOfStereoBonds +
 
198
          pInChI->StereoIsotopic->nNumberOfStereoCenters) ) {
 
199
        pStereo = pInChI->StereoIsotopic;
 
200
    } else
 
201
    if ( pInChI->Stereo && 
 
202
         (pInChI->Stereo->nNumberOfStereoBonds +
 
203
          pInChI->Stereo->nNumberOfStereoCenters) ) {
 
204
        pStereo = pInChI->Stereo;
 
205
    } else {
 
206
        pStereo = NULL;
 
207
    }
 
208
    /* stereo2: Mobile-H in addition to Fixed-H*/
 
209
    pStereo2 = NULL;
 
210
    if ( pInChIMobH && pInChIMobH->nNumberOfAtoms ) {
 
211
        if ( pInChIMobH->StereoIsotopic && 
 
212
             (pInChIMobH->StereoIsotopic->nNumberOfStereoBonds +
 
213
              pInChIMobH->StereoIsotopic->nNumberOfStereoCenters) ) {
 
214
            pStereo2 = pInChIMobH->StereoIsotopic;
 
215
        } else
 
216
        if ( pInChIMobH->Stereo && 
 
217
             (pInChIMobH->Stereo->nNumberOfStereoBonds +
 
218
              pInChIMobH->Stereo->nNumberOfStereoCenters) ) {
 
219
            pStereo2 = pInChIMobH->Stereo;
 
220
        }
 
221
    }
 
222
    INCHI_HEAPCHK
 
223
 
 
224
    num_stereo_bonds = num_stereo_bonds2 = 0;
 
225
    num_stereo_centers = num_stereo_centers2 = 0;
 
226
    /* -- have already been done in the initialization --
 
227
       iDeletedH = 0;
 
228
       nNumDeletedH = 0;
 
229
    */
 
230
    if ( pStereo || pStereo2 ) {
 
231
        /* count implicit H needed for parities and reallocate at[]; set at[n_vertex].at_type=1 for these atoms */
 
232
        int len1  = pStereo? pStereo->nNumberOfStereoCenters : 0;
 
233
        int len2 = pStereo2? pStereo2->nNumberOfStereoCenters : 0;
 
234
        int i2, diff, diff2;
 
235
        for ( i = i2 = 0; i < len1 || i2 < len2; ) {
 
236
            if ( i < len1 && i2 < len2 ) {
 
237
                diff = (int)pStereo->nNumber[i] - (int)pStereo2->nNumber[i2];
 
238
                if ( diff <= 0 ) {
 
239
                    n_vertex = pStereo->nNumber[i]-1;
 
240
                    i ++;
 
241
                    i2 += !diff;
 
242
                } else {
 
243
                    n_vertex = pStereo2->nNumber[i2]-1;
 
244
                    num_stereo_centers2 ++;
 
245
                    i2 ++;
 
246
                }
 
247
            } else
 
248
            if ( i < len1 ) {
 
249
                n_vertex = pStereo->nNumber[i]-1;
 
250
                i ++;
 
251
            } else {
 
252
                n_vertex = pStereo2->nNumber[i2]-1;
 
253
                num_stereo_centers2 ++;
 
254
                i2 ++;
 
255
            }
 
256
            /* find whether it is an allene */
 
257
            if ( at[n_vertex].valence == 2 &&
 
258
                 at[n_vertex].num_H   == 0 &&
 
259
                 bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) &&
 
260
                 at[jv = at[n_vertex].neighbor[0]].valence + at[jv].num_H == 3 &&
 
261
                 bCanAtomBeTerminalAllene(at[jv].elname, 0, 0)     &&
 
262
                 at[jn = at[n_vertex].neighbor[1]].valence + at[jn].num_H == 3 &&
 
263
                 bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) {
 
264
                /* allene */
 
265
                if ( !at[jv].at_type && at[jv].num_H ) {
 
266
                    nNumDeletedH += at[jv].num_H;
 
267
                    at[jv].at_type ++;  /* H should be added as an explicit H */
 
268
                }
 
269
                if ( !at[jn].at_type && at[jn].num_H ) {
 
270
                    nNumDeletedH += at[jn].num_H;
 
271
                    at[jn].at_type ++;  /* H should be added as an explicit H */
 
272
                }
 
273
            } else {
 
274
                /* stereogenic atom - sp3 */
 
275
                if ( !at[n_vertex].at_type && at[n_vertex].num_H ) {
 
276
                    nNumDeletedH += at[n_vertex].num_H;
 
277
                    at[n_vertex].at_type ++; /* H should be added as an explicit H */
 
278
                }
 
279
            }
 
280
        }
 
281
        INCHI_HEAPCHK
 
282
        len1  = pStereo? pStereo->nNumberOfStereoBonds : 0;
 
283
        len2 = pStereo2? pStereo2->nNumberOfStereoBonds : 0;
 
284
        for ( i = i2 = 0; i < len1 || i2 < len2; ) {
 
285
            if ( i < len1 && i2 < len2 ) {
 
286
                diff  = (int)pStereo->nBondAtom1[i] - (int)pStereo2->nBondAtom1[i2];
 
287
                diff2 = (int)pStereo->nBondAtom2[i] - (int)pStereo2->nBondAtom2[i2];
 
288
                if ( diff < 0 || diff == 0 && diff2 <= 0) {
 
289
                    n_vertex = pStereo->nBondAtom1[i]-1;
 
290
                    n_neigh  = pStereo->nBondAtom2[i]-1;
 
291
                    i ++;
 
292
                    i2 += !diff && !diff2;
 
293
                } else {
 
294
                    n_vertex = pStereo2->nBondAtom1[i2]-1;
 
295
                    n_neigh  = pStereo2->nBondAtom2[i2]-1;
 
296
                    num_stereo_bonds2 ++;
 
297
                    i2 ++;
 
298
                }
 
299
            } else
 
300
            if ( i < len1 ) {
 
301
                n_vertex = pStereo->nBondAtom1[i]-1;
 
302
                n_neigh  = pStereo->nBondAtom2[i]-1;
 
303
                i ++;
 
304
            } else {
 
305
                n_vertex = pStereo2->nBondAtom1[i2]-1;
 
306
                n_neigh  = pStereo2->nBondAtom2[i2]-1;
 
307
                num_stereo_bonds2 ++;
 
308
                i2 ++;
 
309
            }
 
310
            if ( !is_in_the_list( at[n_vertex].neighbor, (AT_NUMB)n_neigh, at[n_vertex].valence ) ) {
 
311
                /* must be a cumulene */
 
312
                if ( !bFindCumuleneChain( at, (AT_NUMB)n_vertex, (AT_NUMB)n_neigh, nCumulene, MAX_CUMULENE_LEN+1 ) ) {
 
313
                    ret = RI_ERR_SYNTAX; /* not a cumulene */
 
314
                    goto exit_function;
 
315
                }
 
316
            }
 
317
            if ( !at[n_vertex].at_type && at[n_vertex].num_H ) {
 
318
                nNumDeletedH += at[n_vertex].num_H;
 
319
                at[n_vertex].at_type ++;  /* H should be added as an explicit H */
 
320
            }
 
321
            if ( !at[n_neigh].at_type && at[n_neigh].num_H ) {
 
322
                nNumDeletedH += at[n_neigh].num_H;
 
323
                at[n_neigh].at_type ++;   /* H should be added as an explicit H */
 
324
            }
 
325
        }
 
326
        INCHI_HEAPCHK
 
327
        if ( nNumDeletedH ) {
 
328
            /* add explicit H */
 
329
            inp_ATOM *at2 = (inp_ATOM *)inchi_calloc( num_atoms + nNumDeletedH, sizeof(at2[0]) );
 
330
            if ( !at2 ) {
 
331
                ret = RI_ERR_ALLOC;
 
332
                goto exit_function;
 
333
            }
 
334
            pStruct->num_deleted_H = nNumDeletedH;
 
335
            memcpy( at2, at, num_atoms * sizeof(at2[0]) );
 
336
            inchi_free( at );
 
337
            pStruct->at =  at = at2;
 
338
            /* fill out deleted H atom info */
 
339
            for ( i = num_atoms; i < num_atoms + nNumDeletedH; i ++ ) {
 
340
                strcpy( at[i].elname, "H" );
 
341
                at[i].el_number = EL_NUMBER_H;
 
342
                at[i].orig_at_number = iAtNoOffset + i+1;
 
343
                at[i].orig_compt_at_numb = i + 1;
 
344
                at[i].component = iComponent + 1;
 
345
            }
 
346
            /* connect deleted H */
 
347
            for( i = 0; i < num_atoms; i ++ ) {
 
348
                if ( at[i].at_type == 1 ) {
 
349
                    if ( 0 > (ret = AddExplicitDeletedH( at, i, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
 
350
                        goto exit_function;
 
351
                    }
 
352
                }
 
353
            }
 
354
        }
 
355
        INCHI_HEAPCHK
 
356
    }
 
357
 
 
358
    if ( pStereo ) {
 
359
        /* mark stereo centers, they have already been connected the added explicit H, if any */
 
360
        int bInvertedParity   = (pStereo->nCompInv2Abs == -1);
 
361
        for ( i = 0; i < pStereo->nNumberOfStereoCenters; i ++ ) {
 
362
            n_vertex = pStereo->nNumber[i]-1;
 
363
            parity   = pStereo->t_parity[i];
 
364
            if ( bInvertedParity ) {
 
365
                parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity;
 
366
            }
 
367
            /* find whether it is allene */
 
368
            if ( at[n_vertex].valence == 2 &&
 
369
                 at[n_vertex].num_H   == 0 &&
 
370
                 bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) &&
 
371
                 /* allene has exactly 2 double bonds */
 
372
                 (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) &&
 
373
                 bCanAtomBeTerminalAllene(at[jv].elname, 0, 0)     &&
 
374
                 (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) &&
 
375
                 bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) {
 
376
                /* allene: add explicit H if implicit H are present */
 
377
                /* iDeletedH = current number of already added explicit H */
 
378
                /* idelH1    = index in at[] of the explicit H added to atom jv */
 
379
                if ( at[jv].num_H ) {
 
380
                    if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
 
381
                        goto exit_function;
 
382
                    }
 
383
                } else {
 
384
                    /* index of the stereo atom neighbor */
 
385
                    idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex];
 
386
                }
 
387
                if ( at[jn].num_H ) {
 
388
                    /* iDeletedH = current number of already added explicit H */
 
389
                    /* idelH2    = index of the explicit H added to atom jn */
 
390
                    if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
 
391
                        goto exit_function;
 
392
                    }
 
393
                } else {
 
394
                    idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex];
 
395
                }
 
396
                /* allene: set bond types to double */
 
397
                /*
 
398
                if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) ||
 
399
                     0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) {
 
400
                    goto exit_function;
 
401
                }
 
402
                */
 
403
                /* allene: make 0D parity */
 
404
                ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 );
 
405
                if ( ret < 0 ) {
 
406
                    goto exit_function;
 
407
                }
 
408
            } else {
 
409
                /* stereogenic sp3 atom */
 
410
                if ( at[n_vertex].num_H ) {
 
411
                    if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
 
412
                        goto exit_function;
 
413
                    }
 
414
                }
 
415
                ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity );
 
416
                if ( ret < 0 ) {
 
417
                    goto exit_function;
 
418
                }
 
419
                num_stereo_centers ++;
 
420
            }
 
421
            if ( ret < 0 ) {
 
422
                goto exit_function;
 
423
            }
 
424
        }
 
425
        INCHI_HEAPCHK
 
426
        /* mark stereobonds */
 
427
        for ( i = 0; i < pStereo->nNumberOfStereoBonds; i ++ ) {
 
428
            jv     = pStereo->nBondAtom1[i]-1;
 
429
            jn     = pStereo->nBondAtom2[i]-1;
 
430
            parity = pStereo->b_parity[i];
 
431
            if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) {
 
432
                /* must be a cumulene */
 
433
                if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) {
 
434
                    return RI_ERR_SYNTAX; /* not a cumulene */
 
435
                }
 
436
                len = MAX_CUMULENE_LEN+1;
 
437
            } else {
 
438
                /* a regular double or alt bond */
 
439
                nCumulene[0] = jv;
 
440
                nCumulene[1] = jn;
 
441
                len = 1; /* cumulene length is number of bonds, not number of atoms */
 
442
            }
 
443
            /* cumulene or double bond: add explicit H if implicit H are present */
 
444
            if ( at[jv].num_H ) {
 
445
                if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
 
446
                    goto exit_function;
 
447
                }
 
448
            } else {
 
449
                /* double bond neighbor that has the smallest canonical number; it is either 0th or 1st */
 
450
                idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]];
 
451
            }
 
452
            if ( at[jn].num_H ) {
 
453
                if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
 
454
                    goto exit_function;
 
455
                }
 
456
            } else {
 
457
                idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]];
 
458
            }
 
459
            if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) {
 
460
                goto exit_function;
 
461
            }
 
462
        }
 
463
        INCHI_HEAPCHK
 
464
    }
 
465
    /* allocate memory for Mobile-H-only stereo */
 
466
    if ( num_stereo_centers2 + num_stereo_bonds2 ) {
 
467
        if ( !(st = (inp_ATOM_STEREO *)inchi_calloc( num_atoms, sizeof(st[0])))) {
 
468
            ret = RI_ERR_ALLOC;
 
469
            goto exit_function;
 
470
        }
 
471
        CopyAt2St( at, st, num_atoms );
 
472
    }
 
473
    pStruct->st = st;
 
474
    if ( num_stereo_centers2 ) {
 
475
        /* In case of Fixed-H */
 
476
        /* mark additional Mobile-H stereo centers, they have already been connected the added explicit H, if any */
 
477
        int bInvertedParity   = (pStereo2->nCompInv2Abs == -1);
 
478
        for ( i = 0; i < pStereo2->nNumberOfStereoCenters; i ++ ) {
 
479
            n_vertex = pStereo2->nNumber[i]-1;
 
480
            parity   = pStereo2->t_parity[i];
 
481
            if ( at[n_vertex].p_parity ) {
 
482
                continue; /* the parity has already been set for Fixed-H */
 
483
            }
 
484
            if ( bInvertedParity ) {
 
485
                parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity;
 
486
            }
 
487
            /* find whether it is allene */
 
488
            if ( at[n_vertex].valence == 2 &&
 
489
                 at[n_vertex].num_H   == 0 &&
 
490
                 bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) &&
 
491
                 /* allene has exactly 2 double bonds */
 
492
                 (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) &&
 
493
                 bCanAtomBeTerminalAllene(at[jv].elname, 0, 0)     &&
 
494
                 (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) &&
 
495
                 bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) {
 
496
                /* allene: add explicit H if implicit H are present */
 
497
                /* iDeletedH = current number of already added explicit H */
 
498
                /* idelH1    = index in at[] of the explicit H added to atom jv */
 
499
                if ( at[jv].num_H ) {
 
500
                    if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
 
501
                        goto exit_function;
 
502
                    }
 
503
                } else {
 
504
                    /* index of the stereo atom neighbor */
 
505
                    idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex];
 
506
                }
 
507
                if ( at[jn].num_H ) {
 
508
                    /* iDeletedH = current number of already added explicit H */
 
509
                    /* idelH2    = index of the explicit H added to atom jn */
 
510
                    if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
 
511
                        goto exit_function;
 
512
                    }
 
513
                } else {
 
514
                    idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex];
 
515
                }
 
516
                /* allene: set bond types to double */
 
517
                /*
 
518
                if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) ||
 
519
                     0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) {
 
520
                    goto exit_function;
 
521
                }
 
522
                */
 
523
                /* allene: make 0D parity */
 
524
                ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 );
 
525
                if ( ret < 0 ) {
 
526
                    goto exit_function;
 
527
                }
 
528
            } else {
 
529
                /* stereogenic sp3 atom */
 
530
                if ( at[n_vertex].num_H ) {
 
531
                    if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
 
532
                        goto exit_function;
 
533
                    }
 
534
                }
 
535
                ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity );
 
536
                if ( ret < 0 ) {
 
537
                    goto exit_function;
 
538
                }
 
539
                num_stereo_centers ++;
 
540
            }
 
541
            if ( ret < 0 ) {
 
542
                goto exit_function;
 
543
            }
 
544
        }
 
545
    }
 
546
    if ( num_stereo_bonds2 ) {
 
547
        /* In case of Fixed-H */
 
548
        /* mark additional Mobile-H stereobonds, they have already been connected the added explicit H, if any */
 
549
        for ( i = 0; i < pStereo2->nNumberOfStereoBonds; i ++ ) {
 
550
            jv     = pStereo2->nBondAtom1[i]-1;
 
551
            jn     = pStereo2->nBondAtom2[i]-1;
 
552
            parity = pStereo2->b_parity[i];
 
553
            if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) {
 
554
                /* must be a cumulene */
 
555
                if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) {
 
556
                    return RI_ERR_SYNTAX; /* not a cumulene */
 
557
                }
 
558
                len = MAX_CUMULENE_LEN+1;
 
559
            } else {
 
560
                /* a regular double or alt bond */
 
561
                nCumulene[0] = jv;
 
562
                nCumulene[1] = jn;
 
563
                len = 1; /* cumulene length is number of bonds, not number of atoms */
 
564
            }
 
565
            /* cumulene or double bond: add explicit H if implicit H are present */
 
566
            if ( at[jv].num_H ) {
 
567
                if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
 
568
                    goto exit_function;
 
569
                }
 
570
            } else {
 
571
                /* double bond neighbor that has the smallest canonical number */
 
572
                idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]];
 
573
            }
 
574
            if ( at[jn].num_H ) {
 
575
                if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
 
576
                    goto exit_function;
 
577
                }
 
578
            } else {
 
579
                idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]];
 
580
            }
 
581
            if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) {
 
582
                goto exit_function;
 
583
            }
 
584
        }
 
585
 
 
586
    }
 
587
 
 
588
 
 
589
    ret = num_atoms;
 
590
 
 
591
exit_function:
 
592
    return ret;
 
593
}
 
594
/*************************************************************/
 
595
int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 )
 
596
{
 
597
    AT_NUMB nCumulene[MAX_CUMULENE_LEN+2];
 
598
    int j, n1, n2, k1, m2, ret, nLenCumulene = 0, bond_type;
 
599
    k1 = at[i1].sb_ord[m1];
 
600
    n1 = i1;
 
601
    nCumulene[nLenCumulene ++] = n1;
 
602
    do {
 
603
        n2 = at[n1].neighbor[k1]; /* next atom */
 
604
        nCumulene[nLenCumulene ++] = n2;
 
605
        for (m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[n2].sb_parity[m2]; m2 ++ ) {
 
606
            if ( n1 == at[n2].neighbor[(int)at[n2].sb_ord[m2]] ) {
 
607
                /* found the endatom */
 
608
                goto found;
 
609
            }
 
610
        }
 
611
        if ( at[n2].num_H || at[n2].valence != 2 || at[n2].endpoint ) {
 
612
            break; /* not a middle cumulene */
 
613
        }
 
614
        k1 = (at[n2].neighbor[0] == n1);
 
615
        n1 = n2;
 
616
    } while ( at[n1].valence == 2 && !at[n1].num_H && nLenCumulene < MAX_CUMULENE_LEN+2 &&
 
617
              bCanAtomBeMiddleAllene( at[n1].elname, at[n1].charge, at[n1].radical ) );
 
618
    return RI_ERR_SYNTAX; /* failed */
 
619
 
 
620
found:
 
621
    if ( nLenCumulene == 2 ) {
 
622
        bond_type = BOND_TYPE_STEREO; /* double bond or alternating bond */
 
623
    } else {
 
624
        bond_type = BOND_TYPE_DOUBLE; /* cumulene or allene */
 
625
    }
 
626
 
 
627
    for ( j = 1; j < nLenCumulene; j ++ ) {
 
628
        /* if bond_type = BOND_TYPE_DOUBLE then increments at->cham_bonds_valence: */
 
629
        /* at->cham_bonds_valence += BOND_TYPE_DOUBLE-BOND_TYPE_SINGLE */
 
630
        if ( 0 > (ret = set_bond_type( at, (AT_NUMB)nCumulene[j-1], (AT_NUMB)nCumulene[j], bond_type ) ) ) {
 
631
            return RI_ERR_PROGR; /* failed */
 
632
        }
 
633
    }
 
634
    return nLenCumulene;
 
635
}
 
636
/******************************************************************************************************/
 
637
int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI)
 
638
{
 
639
    INChI_Stereo *pStereo;
 
640
    inp_ATOM     *at        = pStruct->at;
 
641
    int           num_atoms = pStruct->num_atoms;
 
642
    int           i, j, num_stereo_bonds, ret; 
 
643
 
 
644
    if ( pInChI->StereoIsotopic && 
 
645
         (pInChI->StereoIsotopic->nNumberOfStereoBonds +
 
646
          pInChI->StereoIsotopic->nNumberOfStereoCenters) ) {
 
647
        pStereo = pInChI->StereoIsotopic;
 
648
    } else
 
649
    if ( pInChI->Stereo && 
 
650
         (pInChI->Stereo->nNumberOfStereoBonds +
 
651
          pInChI->Stereo->nNumberOfStereoCenters) ) {
 
652
        pStereo = pInChI->Stereo;
 
653
    } else {
 
654
        pStereo = NULL;
 
655
    }
 
656
    
 
657
    /************************ set bond types separately from stereo *******************/
 
658
    if ( pStereo ) {
 
659
        num_stereo_bonds = 0;
 
660
        for ( i = 0; i < num_atoms; i ++ ) {
 
661
            /* set BOND_TYPE_DOUBLE in allenes and cumulenes */
 
662
            /* set BOND_TYPE_STEREO in double bond stereo */
 
663
            for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) {
 
664
                num_stereo_bonds ++;
 
665
                if ( 0 > (ret = SetStereoBondTypeFor0DParity( at, i,  j ) ) ) {
 
666
                    goto exit_function;
 
667
                }
 
668
            }
 
669
        }
 
670
        if ( num_stereo_bonds ) {
 
671
            int num_bond_type_stereo;
 
672
            int num_bond_type_altern;
 
673
            AT_NUMB neigh;
 
674
            /* replace adjacent BOND_TYPE_STEREO with BOND_TYPE_ALTERN */
 
675
            for ( i = 0; i < num_atoms; i ++ ) {
 
676
                num_bond_type_stereo = 0;
 
677
                num_bond_type_altern = 0;
 
678
                for ( j = 0; j < at[i].valence; j ++ ) {
 
679
                    num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO );
 
680
                    num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN );
 
681
                }
 
682
                if ( num_bond_type_stereo + num_bond_type_altern > 1 && num_bond_type_stereo ) {
 
683
                    for ( j = 0; j < at[i].valence; j ++ ) {
 
684
                        if  ( at[i].bond_type[j] == BOND_TYPE_STEREO ) {
 
685
                            neigh = at[i].neighbor[j];
 
686
                            /* does not change at[i].chem_bond_valence in case of BOND_TYPE_ALTERN */
 
687
                            if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_ALTERN ) ) ) {
 
688
                                goto exit_function;
 
689
                            }
 
690
                        }
 
691
                    }
 
692
                }
 
693
                /* at this point only isolated stereo bonds have type BOND_TYPE_STEREO */
 
694
            }
 
695
            /* increment at[i].chem_bonds_valence if at[i] has an altern. bond */
 
696
            /* replace BOND_TYPE_STEREO with BOND_TYPE_DOUBLE and increment   */
 
697
            /* chem_bonds_valence of the adjacent atoms */
 
698
            for ( i = 0; i < num_atoms; i ++ ) {
 
699
                num_bond_type_stereo = 0;
 
700
                num_bond_type_altern = 0;
 
701
                for ( j = 0; j < at[i].valence; j ++ ) {
 
702
                    num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO );
 
703
                    num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN );
 
704
                }
 
705
                if ( !num_bond_type_stereo && num_bond_type_altern ) {
 
706
                    /* an atom has only BOND_TYPE_ALTERN => adjacent BOND_TYPE_ALTERN case */
 
707
                    at[i].chem_bonds_valence += 1;
 
708
                } else
 
709
                if ( num_bond_type_stereo == 1 ) {
 
710
                    /* isolated BOND_TYPE_STEREO => replace with BOND_TYPE_DOUBLE */
 
711
                    for ( j = 0; j < at[i].valence; j ++ ) {
 
712
                        if  ( at[i].bond_type[j] == BOND_TYPE_STEREO ) {
 
713
                            neigh = at[i].neighbor[j];
 
714
                            /* replacing BOND_TYPE_STEREO with BOND_TYPE_DOUBLE */
 
715
                            /* does not change at->chem_bonds_valence */
 
716
                            if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_DOUBLE ) ) ) {
 
717
                                goto exit_function;
 
718
                            }
 
719
                            at[i].chem_bonds_valence ++;
 
720
                            at[(int)neigh].chem_bonds_valence ++;
 
721
                        }
 
722
                    }
 
723
                } else
 
724
                if ( num_bond_type_stereo + num_bond_type_altern ) {
 
725
                    /* an atom still has both BOND_TYPE_STEREO and BOND_TYPE_ALTERN */
 
726
                    ret = RI_ERR_PROGR;
 
727
                    goto exit_function;
 
728
                }
 
729
            }
 
730
            INCHI_HEAPCHK
 
731
        }
 
732
    }
 
733
    ret = 0; /* success */
 
734
exit_function:
 
735
    return ret;
 
736
}
 
737
 
 
738
/******************************************************************************************************/
 
739
int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT  *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder )
 
740
{
 
741
    int i, j, atom_charge, left_charge, charge, ret = 0, v1, nMinorder;
 
742
    int          num_at = pStruct->num_atoms;
 
743
    inp_ATOM    *at     = pStruct->at;
 
744
    ICHICONST SRM *pSrm = pStruct->pSrm;
 
745
    BNS_VERTEX  *pv;
 
746
    BNS_EDGE    *pe;
 
747
    int          chem_bonds_valence, bond_order;
 
748
 
 
749
    atom_charge = left_charge = 0;
 
750
    for ( i = 0; i < num_at; i ++ ) {
 
751
        pv = pBNS->vert + i;
 
752
        /* bonds */
 
753
        chem_bonds_valence = 0;
 
754
        for ( j = 0; j < at[i].valence; j ++ ) {
 
755
            pe = pBNS->edge + pv->iedge[j];
 
756
            BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, NULL, &nMinorder, NULL );
 
757
            bond_order = pe->flow + nMinorder;
 
758
            if ( !bAllowZeroBondOrder && !bond_order ) {
 
759
                bond_order = 1;
 
760
            }
 
761
            chem_bonds_valence += bond_order;
 
762
            at[i].bond_type[j]  = bond_order;  /* BOND_MARK_HIGHLIGHT */
 
763
        }
 
764
        at[i].chem_bonds_valence = chem_bonds_valence;
 
765
        /* charges (both may be present resulting in zero) */
 
766
        at[i].charge = pVA[i].cInitCharge;
 
767
        if ( pVA[i].nCMinusGroupEdge ) {
 
768
            pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1;
 
769
            if ( charge = pe->flow ) {
 
770
                at[i].charge  -= charge;
 
771
                atom_charge   -= charge;
 
772
            }
 
773
        }
 
774
        if ( pVA[i].nCPlusGroupEdge ) {
 
775
            pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1;
 
776
            if ( charge = pe->cap - pe->flow ) {
 
777
                at[i].charge += charge;
 
778
                atom_charge  += charge;
 
779
            }
 
780
        }
 
781
        if ( pv->st_edge.cap > pv->st_edge.flow ) {
 
782
            at[i].radical = RADICAL_SINGLET + (pv->st_edge.cap - pv->st_edge.flow);
 
783
        }
 
784
    }
 
785
    /* find charge excess */
 
786
    for ( i = num_at; i < pBNS->num_vertices; i ++ ) {
 
787
        pv = pBNS->vert + i;
 
788
        if ( charge = pv->st_edge.cap - pv->st_edge.flow ) {
 
789
            if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
 
790
                left_charge -= charge;
 
791
            } else 
 
792
            if ( IS_BNS_VT_YVCONNECTOR(pv->type) ) {
 
793
                left_charge += charge;
 
794
            }
 
795
        }
 
796
    }
 
797
    /* tautomeric H and (-) */
 
798
    for ( i = 0; i < pBNS->num_t_groups; i ++ ) {
 
799
        /* tautomeric groups are first non-atom vertices;
 
800
           order of them is same as in pTCGroups->pTCG[] */
 
801
        int num_H       = pTCGroups->pTCG[i].tg_num_H;
 
802
        int num_Minus   = pTCGroups->pTCG[i].tg_num_Minus;
 
803
        int bMinusFirst = (pTCGroups->pTCG[i].tg_RestoreFlags & TGRF_MINUS_FIRST);
 
804
        int num_at_add;
 
805
        Vertex vMinus = NO_VERTEX;
 
806
        pv  = pBNS->vert + num_at + i;  /* t-group vertex */
 
807
        if ( !(pv->type & BNS_VERT_TYPE_TGROUP) ) {
 
808
            return RI_ERR_PROGR;
 
809
        }
 
810
        if ( pTCGroups->pTCG[i].tg_set_Minus > 0 && num_Minus > 0 ) {
 
811
            vMinus = pTCGroups->pTCG[i].tg_set_Minus-1;
 
812
            num_Minus --;
 
813
        }
 
814
 
 
815
        if ( bMinusFirst ) {
 
816
            for ( j = 0; j < pv->num_adj_edges; j ++ ) {
 
817
                pe = pBNS->edge + pv->iedge[j];
 
818
                v1 = pe->neighbor1;
 
819
                num_at_add = pe->flow;
 
820
                if ( v1 == vMinus ) {
 
821
                    if ( num_at_add ) {
 
822
                        at[v1].charge = -1;  /* no checking at[v1].charge == 0 for now ??? */
 
823
                        num_at_add --;       /* no checking  num_at_add > 0 for now ??? */
 
824
                    } else {
 
825
                        num_Minus ++;        /* error ??? */
 
826
                    }
 
827
                    vMinus = NO_VERTEX;
 
828
                }
 
829
                if ( num_at_add > 0 ) {
 
830
                    /* atom has tautomeric attachment; do not allow =N(-) */
 
831
                    if ( num_Minus && !at[v1].charge &&
 
832
                         at[v1].valence == at[v1].chem_bonds_valence ) {
 
833
                        at[v1].charge --;
 
834
                        num_at_add --;
 
835
                        num_Minus --;
 
836
                    }
 
837
                    if ( num_at_add > 0 ) {
 
838
                        at[v1].num_H += num_at_add;
 
839
                        num_H -= num_at_add;
 
840
                        num_at_add = 0;
 
841
                    }
 
842
                }
 
843
                at[v1].endpoint = i+1;
 
844
            }
 
845
            if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) {
 
846
                return RI_ERR_PROGR;
 
847
            }
 
848
        } else {
 
849
            for ( j = pv->num_adj_edges-1; 0 <= j; j -- ) {
 
850
                pe = pBNS->edge + pv->iedge[j];
 
851
                v1 = pe->neighbor1;
 
852
                num_at_add = pe->flow;
 
853
                if ( v1 == vMinus ) {
 
854
                    if ( num_at_add ) {
 
855
                        at[v1].charge = -1;  /* no checking at[v1].charge == 0 for now ??? */
 
856
                        num_at_add --;       /* no checking  num_at_add > 0 for now ??? */
 
857
                    } else {
 
858
                        num_Minus ++;        /* error ??? */
 
859
                    }
 
860
                    vMinus = NO_VERTEX;
 
861
                }
 
862
                if ( num_at_add > 0 ) {
 
863
                    /* atom has tautomeric attachment; do not allow =N(-) */
 
864
                    if ( num_Minus && !at[v1].charge &&
 
865
                         at[v1].valence == at[v1].chem_bonds_valence ) {
 
866
                        at[v1].charge --;
 
867
                        num_at_add --;
 
868
                        num_Minus --;
 
869
                    }
 
870
                    if ( num_at_add > 0 ) {
 
871
                        at[v1].num_H += num_at_add;
 
872
                        num_H -= num_at_add;
 
873
                        num_at_add = 0;
 
874
                    }
 
875
                }
 
876
                at[v1].endpoint = i+1;
 
877
            }
 
878
            if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) {
 
879
                return RI_ERR_PROGR;
 
880
            }
 
881
        }
 
882
    }
 
883
 
 
884
    return ret;
 
885
}
 
886
/******************************************************************************************************/
 
887
int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT  *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad )
 
888
{
 
889
    int nOutput = 0;
 
890
#ifndef TARGET_API_LIB
 
891
#if ( bRELEASE_VERSION == 0 )
 
892
    char s[128];
 
893
    int i, j, atom_charge, left_charge, charge, excess_charge, ret = 0;
 
894
    int v1, v2, flow, tot_st_flow, tot_st_cap, num_electrons, nNumMetalAtoms;
 
895
    int       num_at = pStruct->num_atoms;
 
896
    inp_ATOM *at     = pStruct->at;
 
897
    BNS_VERTEX  *pv;
 
898
    BNS_EDGE    *pe;
 
899
#ifdef _DEBUG
 
900
    int       bDebugOutput = 0;
 
901
    bNoRad = 1;
 
902
    bDebugOutput = 1;
 
903
#endif
 
904
    /* count electrons and metals */
 
905
    num_electrons = -pTCGroups->total_charge;
 
906
    nNumMetalAtoms = 0;
 
907
    for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) {
 
908
        num_electrons += pTCGroups->pTCG[i].tg_num_H;
 
909
    }
 
910
    for ( i = 0; i < num_at; i ++ ) {
 
911
        num_electrons += at[i].el_number + at[i].num_H;
 
912
        nNumMetalAtoms += pVA[i].cMetal;
 
913
    }
 
914
    /* create output string */
 
915
    sprintf( s, "%d:%d%sM%02dv%da%de%db%d* ",
 
916
                bNoRad, pTCGroups->iComponent+1, num_electrons%2?"O":"E", nNumMetalAtoms,
 
917
                pBNS->num_vertices, num_at, pBNS->num_edges, pBNS->num_bonds );
 
918
 
 
919
 
 
920
    tot_st_flow = tot_st_cap = 0;
 
921
    atom_charge = left_charge = 0;
 
922
    if ( pBNS->num_atoms != num_at ) {
 
923
        fprintf( stdout, "\n%sNum. atoms discrepancy: %d(BNS) vs. %d(at) ", s, pBNS->num_atoms, num_at);
 
924
        nOutput ++;
 
925
    }
 
926
    /* check edges */
 
927
#ifdef _DEBUG
 
928
    if ( bDebugOutput && bNoRad ) {
 
929
        fprintf( stderr, "\n\n------begin------------------------------------------------\n" );
 
930
        fprintf( stderr, "\n\fedge    cap       flow     v1   v2\n\n" );
 
931
        /*                  xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx    */
 
932
    }
 
933
#endif
 
934
    for ( i = 0; i < pBNS->num_edges; i ++ ) {
 
935
        pe = pBNS->edge + i;
 
936
        v1 = pe->neighbor1;
 
937
        v2 = v1 ^ pe->neighbor12;
 
938
        if ( pe->cap < pe->flow || pe->flow < 0 ) {
 
939
            fprintf( stdout, "\n%sedge %d (%d-%d) has cap=%d flow=%d  ", s, i, v1, v2, pe->cap, pe->flow );
 
940
            nOutput ++;
 
941
        }
 
942
#ifdef _DEBUG
 
943
        if ( bDebugOutput && bNoRad ) {
 
944
            /*                xxxx  xxxx/xxxx xxxx/xxxx xxxx xxxx    */
 
945
            fprintf( stderr, "%4d %4d/%-4d %4d/%-4d %4d %4d\n", i, pe->cap, pe->cap0, pe->flow, pe->flow0, v1, v2 ); 
 
946
        }
 
947
#endif
 
948
    }
 
949
 
 
950
 
 
951
    /* check vertices */
 
952
#ifdef _DEBUG
 
953
    if ( bDebugOutput && bNoRad ) {
 
954
        fprintf( stderr, "\n\fvert   st-cap   st-flow   type iedge : neigh\n\n" );
 
955
        /*                   xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx    */
 
956
    }
 
957
#endif
 
958
    for ( i = 0; i < pBNS->num_vertices; i ++ ) {
 
959
        pv = pBNS->vert + i;
 
960
#ifdef _DEBUG
 
961
        if ( bDebugOutput && bNoRad ) {
 
962
            /*                   xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx    */
 
963
            int j;
 
964
            const char *s;
 
965
            char sAtom[6];
 
966
            switch( pv->type ) {
 
967
            case BNS_VERT_TYPE_ATOM:
 
968
                sprintf( sAtom, "At %-2.2s", i < num_at? at[i].elname : "??" );
 
969
                s = sAtom;
 
970
                break;
 
971
            case BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_ENDPOINT:
 
972
                s = "Endpt";
 
973
                break;
 
974
            case BNS_VT_C_POS:
 
975
                s = "(+)  ";
 
976
                break;
 
977
            case BNS_VT_C_NEG:
 
978
                s = "(-)  ";
 
979
                break;
 
980
            case BNS_VT_C_POS_C:
 
981
                s = "(+C) ";
 
982
                break;
 
983
            case BNS_VT_C_NEG_C:
 
984
                s = "(-C) ";
 
985
                break;
 
986
            case BNS_VT_C_POS_M:
 
987
                s = "(+M) ";
 
988
                break;
 
989
            case BNS_VT_C_NEG_M:
 
990
                s = "(-M) ";
 
991
                break;
 
992
            case BNS_VT_C_POS_ALL:
 
993
                s = "(+)Sg";
 
994
                break;
 
995
            case BNS_VT_C_NEG_ALL:
 
996
                s = "(-)Sg";
 
997
                break;
 
998
            case BNS_VT_M_GROUP:
 
999
                s = "M-grp";
 
1000
                break;
 
1001
            case BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP:
 
1002
                s = "ChStr";
 
1003
                break;
 
1004
            case BNS_VERT_TYPE__AUX:
 
1005
                s = "Yconn";
 
1006
                break;
 
1007
            case BNS_VERT_TYPE_TGROUP:
 
1008
                s = "T-grp";
 
1009
                break;
 
1010
            default:
 
1011
                s = "Unkn.";
 
1012
                break;
 
1013
            }
 
1014
            fprintf( stderr,    "%4d %4d/%-4d %4d/%-4d 0x%03X %5s",
 
1015
                                i, pv->st_edge.cap, pv->st_edge.cap0, pv->st_edge.flow, pv->st_edge.flow0,
 
1016
                                pv->type, s );
 
1017
            for ( j = 0; j < pv->num_adj_edges; j ++ ) {
 
1018
                fprintf( stderr, " %2d", pv->iedge[j] );
 
1019
            }
 
1020
            fprintf( stderr, ":" );
 
1021
            for ( j = 0; j < pv->num_adj_edges; j ++ ) {
 
1022
                pe = pBNS->edge + pv->iedge[j];
 
1023
                fprintf( stderr, " %2d", pe->neighbor12 ^ i );
 
1024
            }
 
1025
            fprintf( stderr, "\n" );
 
1026
        }
 
1027
#endif
 
1028
        tot_st_flow += pv->st_edge.flow;
 
1029
        tot_st_cap  += pv->st_edge.cap;
 
1030
        if ( pv->num_adj_edges > pv->max_adj_edges ) {
 
1031
            fprintf( stdout, "\n%s%s %d type 0x%X \"%s\" num_edges=%d > max=%d  ", s,
 
1032
                i < num_at? "atom":"vertex", i,
 
1033
                pv->type, at[i].elname, pv->num_adj_edges, pv->max_adj_edges );
 
1034
            nOutput ++;
 
1035
        }
 
1036
        if ( i < num_at ) {
 
1037
            /* charge on atoms */
 
1038
            charge = pVA[i].cInitCharge;
 
1039
            if ( pVA[i].nCMinusGroupEdge ) {
 
1040
                pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1;
 
1041
                if ( pe->flow > 0 ) {
 
1042
                    charge   -= pe->flow;
 
1043
                }
 
1044
            }
 
1045
            if ( pVA[i].nCPlusGroupEdge ) {
 
1046
                pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1;
 
1047
                if ( pe->cap > pe->flow  ) {
 
1048
                    charge  += pe->cap - pe->flow;
 
1049
                }
 
1050
            }
 
1051
            if ( bNoRad && pv->st_edge.flow != pv->st_edge.cap ) {
 
1052
                fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" unexpected st_cap=%d st_flow=%d ", s,
 
1053
                    i < num_at? "atom":"vertex", i,
 
1054
                    pv->type, at[i].elname, pv->st_edge.cap, pv->st_edge.flow );
 
1055
                nOutput ++;
 
1056
            } else
 
1057
            if ( bNoRad && charge && !strcmp(at[i].elname, "C") ) {
 
1058
                /* ignore carbonyls */
 
1059
                if ( i == 0 && num_at == 2 && !strcmp(at[1].elname, "O") &&
 
1060
                     !at[0].num_H && !at[1].num_H && !pTCGroups->total_charge) {
 
1061
                    ; /* C(-)#O(+) structure */
 
1062
                } else {
 
1063
                    fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" charge=%d ", s,
 
1064
                        i < num_at? "atom":"vertex", i,
 
1065
                        pv->type, at[i].elname, charge );
 
1066
                    nOutput ++;
 
1067
                }
 
1068
            }
 
1069
            atom_charge += charge;
 
1070
        } else
 
1071
        if ( (charge = pv->st_edge.cap - pv->st_edge.flow) > 0 ) {
 
1072
            /* excess charge */
 
1073
            if ( !bNoRad && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
 
1074
                left_charge -= charge;
 
1075
            } else 
 
1076
            if ( !bNoRad && IS_BNS_VT_YVCONNECTOR(pv->type) ) {
 
1077
                left_charge += charge;
 
1078
            } else
 
1079
            if ( !bNoRad && IS_BNS_VT_M_GR(pv->type) &&
 
1080
                 0 <= (j=pTCGroups->nGroup[TCG_MeFlower3]) && 
 
1081
                 i == pTCGroups->pTCG[j].nVertexNumber ) {
 
1082
                ; /* additional "radical" on metal flower */
 
1083
            } else
 
1084
            if ( !(pv->type & BNS_VERT_TYPE_TGROUP) || bNoRad ) {
 
1085
                /* t-groups before running BFS should have st_cap > st_flow */
 
1086
                fprintf( stdout, "\n%s%s %d: type 0x%X unexpected st_cap=%d st_flow=%d ", s,
 
1087
                    i < num_at? "atom":"vertex", i,
 
1088
                    pv->type, pv->st_edge.cap, pv->st_edge.flow);
 
1089
                nOutput ++;
 
1090
            }
 
1091
        }
 
1092
        if ( pv->st_edge.cap < pv->st_edge.flow || pv->st_edge.flow < 0 ) {
 
1093
            fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_cap=%d st_flow=%d  ", s,
 
1094
                i < num_at? "atom":"vertex", i,
 
1095
                pv->type, i < num_at? at[i].elname:"", pv->st_edge.cap, pv->st_edge.flow );
 
1096
            nOutput ++;
 
1097
        }
 
1098
        /* check edge_flow vs. st_flow consistency */
 
1099
        for( j = 0, flow = 0; j < pv->num_adj_edges; j ++ ) {
 
1100
            pe = pBNS->edge + pv->iedge[j];
 
1101
            flow += pe->flow;
 
1102
        }
 
1103
        if ( flow != pv->st_edge.flow ) {
 
1104
            fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_flow=%d edge_flow=%d  ", s,
 
1105
                i < num_at? "atom":"vertex", i,
 
1106
                pv->type, i < num_at? at[i].elname:"", pv->st_edge.flow, flow );
 
1107
            nOutput ++;
 
1108
        }
 
1109
    }
 
1110
#ifdef _DEBUG
 
1111
    if ( bDebugOutput && bNoRad ) {
 
1112
        fprintf( stderr, "\n------end--------------------------------------------------\n" );
 
1113
    }
 
1114
#endif
 
1115
    /*
 
1116
    if ( num_electrons %= 2 ) {
 
1117
        fprintf( stdout, "\n%d*Odd number of electrons (%d atoms) ", bNoRad, num_at );
 
1118
        nOutput ++;
 
1119
    }
 
1120
    */
 
1121
    /* tautomeric groups charge */
 
1122
    for ( i = 0, charge = 0; i < pTCGroups->num_tgroups; i ++ ) {
 
1123
        charge -= pTCGroups->pTCG[i].tg_num_Minus;
 
1124
    }
 
1125
    /* compare */
 
1126
    if ( charge != pTCGroups->tgroup_charge ) {
 
1127
        fprintf( stdout, "\n%sCounted t-group charge=%d while %d was saved  ", s,
 
1128
               charge, pTCGroups->tgroup_charge);
 
1129
        nOutput ++;
 
1130
    }
 
1131
    /* add other charges */
 
1132
    charge += atom_charge + left_charge;
 
1133
    excess_charge = pTCGroups->total_charge - pTCGroups->added_charge - pTCGroups->tgroup_charge;
 
1134
    if ( charge != pTCGroups->total_charge && excess_charge != pTCGroups->total_charge - charge ) {
 
1135
        fprintf( stdout, "\n%sCounted total charge=%d while %d was saved; excess charge=%d  ", s,
 
1136
            charge, pTCGroups->total_charge, excess_charge );
 
1137
        nOutput ++;
 
1138
    }
 
1139
    if ( tot_st_cap != pBNS->tot_st_cap || tot_st_flow != pBNS->tot_st_flow ) {
 
1140
        fprintf( stdout, "\n%sCounted/saved total st_flow=%d/%d st_cap=%d/%d  ", s,
 
1141
            tot_st_flow, pBNS->tot_st_flow, tot_st_cap, pBNS->tot_st_cap );
 
1142
        nOutput ++;
 
1143
    }
 
1144
    if ( nOutput ) {
 
1145
        fprintf( stdout, "\n" );
 
1146
    }
 
1147
#endif
 
1148
#endif
 
1149
    return nOutput;
 
1150
}
 
1151
 
 
1152
/******************************************************************************************************/
 
1153
int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo )
 
1154
{
 
1155
    inp_ATOM  *cur_H, *cur_at = at+jv;
 
1156
    int        tot_num_iso_H = NUM_ISO_H(cur_at, 0);
 
1157
    int        num_H     = cur_at->num_H;
 
1158
    int        iso_H     = 0;
 
1159
    S_CHAR     num_iso_H[NUM_H_ISOTOPES];
 
1160
    int        i;
 
1161
 
 
1162
    if ( !at[jv].at_type ) {
 
1163
        return RI_ERR_PROGR;
 
1164
    }
 
1165
 
 
1166
    if ( at[jv].at_type > 1 ) {
 
1167
        /* explicit hydrogens have already been added; find them */
 
1168
        for ( i = 0; i < *iDeletedH; i ++ ) {
 
1169
            if ( at[num_at + i].neighbor[0] == jv ) {
 
1170
                *iH = num_at + i; /* return the first found H, it has the smallest canonical pseudo rank */
 
1171
                return 0;
 
1172
            }
 
1173
        }
 
1174
        return RI_ERR_PROGR;
 
1175
    }
 
1176
    /* add all explicit H disconnected from at[jv] in order H, 1H, D, T */
 
1177
    *iH = *iDeletedH + num_at; /* num_H includes all H, both isotopic and normal */
 
1178
    for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) {
 
1179
        num_iso_H[i] = at[jv].num_iso_H[i];
 
1180
    }
 
1181
    for ( ; num_H && (*iDeletedH) < nNumDeletedH; (*iDeletedH) ++ ) {
 
1182
        cur_H = at + num_at + (*iDeletedH); /* first available empty atom will be this explicit H */
 
1183
        cur_H->neighbor[cur_H->valence] = jv; /* connect this new atom H to the real atom */
 
1184
        cur_H->bond_type[cur_H->valence] = BOND_TYPE_SINGLE;
 
1185
        cur_H->valence ++;
 
1186
        if ( num_H > tot_num_iso_H ) {
 
1187
            num_H --;
 
1188
            if ( num_H != tot_num_iso_H ) {
 
1189
                /* may happen when Mobile-H stereo included in Fixed-H processing */
 
1190
                if ( bTwoStereo ) {
 
1191
                    continue;
 
1192
                } else {
 
1193
                    return RI_ERR_SYNTAX; /* two identical H neighbors of a stereo atom/bond */
 
1194
                }
 
1195
            }
 
1196
        } else {
 
1197
            while ( iso_H < NUM_H_ISOTOPES && !num_iso_H[iso_H] )
 
1198
                iso_H ++;
 
1199
            if ( iso_H < NUM_H_ISOTOPES ) {
 
1200
                cur_H->iso_atw_diff = iso_H + 1; /* isotopic shift + 1 */
 
1201
                num_H --;
 
1202
                tot_num_iso_H --;
 
1203
                num_iso_H[iso_H] --;
 
1204
                if ( num_iso_H[iso_H] ) {
 
1205
                    return RI_ERR_SYNTAX; /* two identical isotopic H neighbors of a stereo atom/bond */
 
1206
                }
 
1207
            } else {
 
1208
                return RI_ERR_SYNTAX; /* not enough isotopic H */
 
1209
            }
 
1210
        }
 
1211
    }
 
1212
    if ( num_H ) {
 
1213
        return RI_ERR_SYNTAX;
 
1214
    }
 
1215
    at[jv].at_type ++; /* at[jv].at_type==2 => explicit hydrogens have already been added */
 
1216
    return 0; /* success */    
 
1217
}
 
1218
/******************************************************************************************************/
 
1219
int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen )
 
1220
/* nMaxLen = number of bonds in cumulene = 3 = MAX_CUMULENE_LEN+1 */
 
1221
/* nCumulene[nMaxLen+1] will contain cumulene chain >i1=x=y=i2< in this order */
 
1222
{
 
1223
    int i, len, iat, nat;
 
1224
    nCumulene[0] = i1;
 
1225
    for ( i = 0; i < at[i1].valence; i ++ ) {
 
1226
        len = 0;
 
1227
        iat = i1; /* current */
 
1228
        nat = at[i1].neighbor[i]; /* next */
 
1229
        if ( len+1 == nMaxLen ) {
 
1230
            if ( nat == i2 ) {
 
1231
                nCumulene[++len] = nat;
 
1232
                return 1; /* success */
 
1233
            }
 
1234
            continue; /* check next at[i1] neighbor */
 
1235
        }
 
1236
        while ( at[nat].valence == 2 &&
 
1237
                at[nat].num_H   == 0 &&
 
1238
                bCanAtomBeMiddleAllene(at[nat].elname, 0, 0) ) {
 
1239
            nCumulene[++len] = nat;
 
1240
            nat = at[nat].neighbor[at[nat].neighbor[0]==iat]; /* new next */
 
1241
            if ( len+1 == nMaxLen ) {
 
1242
                if ( nat == i2 ) {
 
1243
                    nCumulene[++len] = nat;
 
1244
                    return 1; /* success */
 
1245
                }
 
1246
                break; /* check next at[i1] neighbor */
 
1247
            }
 
1248
            iat = nCumulene[len]; /* new current */
 
1249
        }
 
1250
    }
 
1251
    return 0; /* failed */
 
1252
}
 
1253
/******************************************************************************************************/
 
1254
int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType )
 
1255
{
 
1256
    AT_NUMB *p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence );
 
1257
    AT_NUMB *p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence );
 
1258
    if ( p1 && p2 ) {
 
1259
        int j1 = p1 - at[i1].neighbor;
 
1260
        int j2 = p2 - at[i2].neighbor;
 
1261
        int bTypePrev = at[i1].bond_type[j1];
 
1262
        at[i1].bond_type[j1] = bType;
 
1263
        at[i2].bond_type[j2] = bType;
 
1264
        if ( bTypePrev && bTypePrev <= BOND_TYPE_TRIPLE &&
 
1265
             bType     && bType     <= BOND_TYPE_TRIPLE ) {
 
1266
            at[i1].chem_bonds_valence += bType - bTypePrev;
 
1267
            at[i2].chem_bonds_valence += bType - bTypePrev;
 
1268
        }
 
1269
        return 0;
 
1270
    }
 
1271
    return RI_ERR_SYNTAX;
 
1272
}
 
1273
/******************************************************************************************************/
 
1274
int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len )
 
1275
{
 
1276
    AT_NUMB nCumulene[MAX_CUMULENE_LEN+2];
 
1277
    AT_NUMB *p1, *p2;
 
1278
    int     m1, m2, parity1, parity2, sb_ord_m1, sb_ord_m2, k1, k2, num_neigh1, num_neigh2;
 
1279
    /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */
 
1280
    S_CHAR  *sb_ord1, *sn_ord1, *sb_parity1;
 
1281
    S_CHAR  *sb_ord2, *sn_ord2, *sb_parity2;
 
1282
    AT_NUMB *sn_orig_at_num1;
 
1283
    AT_NUMB *sn_orig_at_num2;
 
1284
 
 
1285
 
 
1286
    if ( !bFindCumuleneChain( at, (AT_NUMB)i1, (AT_NUMB)i2, nCumulene, len ) ) {
 
1287
        return RI_ERR_SYNTAX; /* not an allene */
 
1288
    }
 
1289
    /* stereo bond neighbors: index of a stereo bond in its end-atom adjacency lists */
 
1290
    if ( (p1 = is_in_the_list( at[i1].neighbor, nCumulene[1], at[i1].valence )) &&
 
1291
         (p2 = is_in_the_list( at[i2].neighbor, nCumulene[len-1], at[i2].valence )) ) {
 
1292
        sb_ord_m1 = p1 - at[i1].neighbor; /* indes of stereobond in the atom's adjacency list */
 
1293
        sb_ord_m2 = p2 - at[i2].neighbor;
 
1294
    } else {
 
1295
        return RI_ERR_PROGR;
 
1296
    }
 
1297
    num_neigh1 = at[i1].valence + at[i1].num_H;
 
1298
    num_neigh2 = at[i2].valence + at[i2].num_H;
 
1299
 
 
1300
    if ( num_neigh1 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh1 > MAX_NUM_STEREO_BOND_NEIGH ||
 
1301
         num_neigh2 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh2 > MAX_NUM_STEREO_BOND_NEIGH ) {
 
1302
        return RI_ERR_SYNTAX;
 
1303
    }
 
1304
 
 
1305
 
 
1306
    sb_ord1    = st? st[i1].sb_ord : at[i1].sb_ord;
 
1307
    sb_ord2    = st? st[i2].sb_ord : at[i2].sb_ord;
 
1308
    sb_parity1 = st? st[i1].sb_parity : at[i1].sb_parity;
 
1309
    sb_parity2 = st? st[i2].sb_parity : at[i2].sb_parity;
 
1310
 
 
1311
    /* find the first unoccupied locations in the stereobond 0D descriptor lists; check whether the stereo has already been set */
 
1312
    for( m1 = k1 = 0; m1 < MAX_NUM_STEREO_BONDS && sb_parity1[m1] && !(k1 = sb_ord1[m1] == sb_ord_m1); m1 ++ )
 
1313
        ;
 
1314
    for( m2 = k2 = 0; m2 < MAX_NUM_STEREO_BONDS && sb_parity2[m2] && !(k2 = sb_ord2[m2] == sb_ord_m2); m2 ++ )
 
1315
        ;
 
1316
    if ( m1 == MAX_NUM_STEREO_BONDS || m2 == MAX_NUM_STEREO_BONDS ) {
 
1317
        return RI_ERR_SYNTAX;
 
1318
    }
 
1319
    if ( k1 && k2 ) {
 
1320
        return 0; /* the stereo descriptor of this bond/allene/cumulene has already been set */
 
1321
    }
 
1322
    if ( k1 || k2 ) {
 
1323
        return RI_ERR_SYNTAX; /* only half of a bond was set */
 
1324
    }
 
1325
 
 
1326
    sn_ord1    = st? st[i1].sn_ord : at[i1].sn_ord;
 
1327
    sn_ord2    = st? st[i2].sn_ord : at[i2].sn_ord;
 
1328
    sn_orig_at_num1 = st? st[i1].sn_orig_at_num : at[i1].sn_orig_at_num;
 
1329
    sn_orig_at_num2 = st? st[i2].sn_orig_at_num : at[i2].sn_orig_at_num;
 
1330
 
 
1331
    /* stereo bond neighbors connection index */
 
1332
    sb_ord1[m1] = sb_ord_m1;
 
1333
    sb_ord2[m2] = sb_ord_m2;
 
1334
    /* stereo bond end atom neighbors */
 
1335
    sn_orig_at_num1[m1] = at[idelH1].orig_at_number;
 
1336
    if ( idelH1 < num_at ) {
 
1337
        if ( p1 = is_in_the_list( at[i1].neighbor, (AT_NUMB)idelH1, at[i1].valence ) ) {
 
1338
            sn_ord1[m1] = p1 - at[i1].neighbor;
 
1339
        } else {
 
1340
            return RI_ERR_PROGR;
 
1341
        }
 
1342
    } else {
 
1343
        sn_ord1[m1] = -1;
 
1344
    }
 
1345
    
 
1346
    sn_orig_at_num2[m2] = at[idelH2].orig_at_number;
 
1347
    if ( idelH2 < num_at ) {
 
1348
        if ( p2 = is_in_the_list( at[i2].neighbor, (AT_NUMB)idelH2, at[i2].valence ) ) {
 
1349
            sn_ord2[m2] = p2 - at[i2].neighbor;
 
1350
        } else {
 
1351
            return RI_ERR_PROGR;
 
1352
        }
 
1353
    } else {
 
1354
        sn_ord2[m2] = -1;
 
1355
    }
 
1356
    if ( ATOM_PARITY_WELL_DEF(parity) ) {
 
1357
        /* special case: 2 bonds to sb atom => inverse parity because */
 
1358
        /* InChI parity refers to the lone pair as a neighbor */
 
1359
        int num_inv = (num_neigh1 == MIN_NUM_STEREO_BOND_NEIGH) + (num_neigh2 == MIN_NUM_STEREO_BOND_NEIGH);
 
1360
        if ( num_inv % 2 ) {
 
1361
            parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : AB_PARITY_EVEN;
 
1362
        }
 
1363
        parity1 = AB_PARITY_EVEN;
 
1364
        parity2 = (parity == AB_PARITY_EVEN)? AB_PARITY_EVEN : AB_PARITY_ODD;
 
1365
    } else {
 
1366
        parity1 = parity2 = parity;
 
1367
    }
 
1368
    sb_parity1[m1] = parity1;
 
1369
    sb_parity2[m2] = parity2;
 
1370
 
 
1371
    return 0;
 
1372
}
 
1373
/******************************************************************************************************/
 
1374
int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity )
 
1375
{
 
1376
    int     m1=0, m2, i, j, tot_num_neigh;
 
1377
    /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */
 
1378
    /* Given parity from InChI, the order of stereo center neighbors is: */
 
1379
    /* 1. The stereocenter itself if the total number of neighbors is 3 (not 4) */
 
1380
    /* 2. Explicit H: non-isotopic, isotopic in order ofascending  atomic mass */
 
1381
    /*         Explicit H have already been sorted in this order */
 
1382
    /* 3. Normal neighboring atoms, atom numbers (=canonical numbers from InChI - 1) in ascending order */
 
1383
    /*         Normal neighboring atoms have already been sorted in this order */
 
1384
    S_CHAR  *p_parity;
 
1385
    AT_NUMB *p_orig_at_num;
 
1386
 
 
1387
    if ( !st || !at[i1].p_parity ) {
 
1388
        m1            = 0;
 
1389
        p_parity      = st? &st[i1].p_parity : &at[i1].p_parity;
 
1390
        p_orig_at_num = st? st[i1].p_orig_at_num : at[i1].p_orig_at_num;
 
1391
 
 
1392
        tot_num_neigh = at[i1].valence + at[i1].num_H;
 
1393
        if ( tot_num_neigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) {
 
1394
            /* only 3 neighbors: the atom itself is the first neighbor */
 
1395
            p_orig_at_num[m1 ++] = at[i1].orig_at_number;
 
1396
        } else
 
1397
        if ( tot_num_neigh != MAX_NUM_STEREO_ATOM_NEIGH ) {
 
1398
            return RI_ERR_PROGR; /* wrong number of members */
 
1399
        }
 
1400
        m2 = m1 + (MAX_NUM_STEREO_ATOM_NEIGH - at[i1].valence);
 
1401
        /* stereoneighbors: deleted explicit atoms H first, in order of increasing isotopic mass */
 
1402
        if ( at[i1].num_H ) {
 
1403
            for ( j = 0; m1 < m2 && j < num_deleted_H; j ++ ) {
 
1404
                if ( at[j + num_at].neighbor[0] == i1 ) {
 
1405
                    p_orig_at_num[m1 ++] = at[j + num_at].orig_at_number;
 
1406
                }
 
1407
            }
 
1408
        }
 
1409
        if ( m1 + at[i1].valence != MAX_NUM_STEREO_ATOM_NEIGH ) {
 
1410
            return RI_ERR_PROGR; /* wrong number of members */
 
1411
        }
 
1412
        /* stereoneighbors: other than explicit H atoms */
 
1413
        for ( i = 0; i < at[i1].valence; i ++ ) {
 
1414
            m2 = at[i1].neighbor[i];
 
1415
            p_orig_at_num[m1 ++] = at[m2].orig_at_number;
 
1416
        }
 
1417
        *p_parity = parity;
 
1418
    }
 
1419
 
 
1420
    return 0;
 
1421
}
 
1422
#if ( BNS_RAD_SEARCH == 1 )
 
1423
/******************************************************************************************************/
 
1424
int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
1425
                    inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask )
 
1426
{
 
1427
    int nNumRad, ret = 0, ret2;
 
1428
    int i, j, k, num_rad_not_atom, num_moved=0, num_candidates= 0, extra_charge=0, added_charge, delta;
 
1429
    BNS_EDGE   *pEdge;
 
1430
    BNS_VERTEX *pv;
 
1431
    Vertex      v1, v2;
 
1432
    S_SHORT    *pnRad = NULL, *pnDelta = NULL;
 
1433
    CC_CAND    *pCand = NULL;
 
1434
    int         cnBits, bAtomRadRemoved = 0;
 
1435
 
 
1436
    int num_at = pStruct->num_atoms;
 
1437
    int num_deleted_H = pStruct->num_deleted_H;
 
1438
    int len_at = num_at + num_deleted_H;
 
1439
 
 
1440
    for ( i = pBNS->num_atoms, num_rad_not_atom=0; i < pBNS->num_vertices; i ++ ) {
 
1441
        num_rad_not_atom += pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow;
 
1442
    }
 
1443
    if ( !num_rad_not_atom ) {
 
1444
        goto exit_function;
 
1445
    }
 
1446
    /****************************************************/
 
1447
    /*                                                  */
 
1448
    /*    Move radicals from ChargeStruct to atoms      */
 
1449
    /*                                                  */
 
1450
    /****************************************************/
 
1451
    
 
1452
    /* allocate memory to keep track of moved radicals */
 
1453
    pnRad   = (S_SHORT *) inchi_malloc(pBNS->num_vertices * sizeof(pnRad[0]));
 
1454
    pnDelta = (S_SHORT *)inchi_calloc(pBNS->num_atoms, sizeof(pnDelta[0]));
 
1455
    if ( !pnRad || !pnDelta ) {
 
1456
        ret = RI_ERR_ALLOC;
 
1457
        goto exit_function;
 
1458
    }
 
1459
    for ( i = 0; i < pBNS->num_vertices; i ++ ) {
 
1460
        pnRad[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow;
 
1461
    }
 
1462
    while( 1 ) {
 
1463
        /* remove radicals from atoms */
 
1464
        for ( i = 0; i < pBNS->num_atoms; i ++ ) {
 
1465
            pnDelta[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow;
 
1466
            pBNS->vert[i].st_edge.cap -= pnDelta[i];
 
1467
            bAtomRadRemoved += (0 != pnDelta[i]);
 
1468
        }
 
1469
        ret = SetRadEndpoints( pBNS, pBD, RAD_SRCH_FROM_FICT );
 
1470
        if ( !ret ) {
 
1471
            break;
 
1472
        }
 
1473
        if ( ret < 0 ) {
 
1474
            goto exit_function;
 
1475
        }
 
1476
        nNumRad = ret;
 
1477
        for ( i = 0; i < nNumRad; i ++ ) {
 
1478
            pEdge = pBNS->edge + pBD->RadEdges[i];
 
1479
            v1 = pEdge->neighbor1;
 
1480
            v2 = pEdge->neighbor12 ^ v1;
 
1481
            pBNS->vert[v1].st_edge.flow -=   pEdge->flow;
 
1482
            pBNS->vert[v2].st_edge.flow -=   pEdge->flow;
 
1483
            pBNS->tot_st_flow           -= 2*pEdge->flow;
 
1484
            pEdge->flow                  = 0;
 
1485
            pEdge->forbidden |= forbidden_mask;
 
1486
            pBNS->edge_forbidden_mask |= forbidden_mask;
 
1487
        }
 
1488
        ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
1489
        if ( ret < 0 ) {
 
1490
            goto exit_function;
 
1491
        } else {
 
1492
            num_moved += ret;
 
1493
        }
 
1494
        RemoveRadEndpoints( pBNS, pBD, NULL );
 
1495
        if ( ret == 0 ) {
 
1496
            break;  /* could not move more radicals */
 
1497
        }
 
1498
        if ( bAtomRadRemoved ) {
 
1499
            /* restore radicals to atoms */
 
1500
            for ( i = 0; i < pBNS->num_atoms; i ++ ) {
 
1501
                pBNS->vert[i].st_edge.cap += pnDelta[i];
 
1502
            }
 
1503
            bAtomRadRemoved = 0;
 
1504
        }
 
1505
    }
 
1506
    if ( bAtomRadRemoved ) {
 
1507
        /* restore radicals to atoms */
 
1508
        for ( i = 0; i < pBNS->num_atoms; i ++ ) {
 
1509
            pBNS->vert[i].st_edge.cap += pnDelta[i];
 
1510
        }
 
1511
        bAtomRadRemoved = 0;
 
1512
    }
 
1513
    pBNS->edge_forbidden_mask &= ~forbidden_mask;
 
1514
 
 
1515
 
 
1516
    /****************************************************/
 
1517
    /*                                                  */
 
1518
    /*    Fix the charges                               */
 
1519
    /*                                                  */
 
1520
    /****************************************************/
 
1521
    if ( num_moved ) {
 
1522
        /* find reqired charge */
 
1523
        extra_charge = 0;
 
1524
        for ( i = pBNS->num_atoms, pv=pBNS->vert+i; i < pBNS->num_vertices; i ++, pv++ ) {
 
1525
            if ( delta = pv->st_edge.cap - pv->st_edge.flow ) {
 
1526
                if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
 
1527
                    extra_charge -= delta;
 
1528
                } else
 
1529
                if ( BNS_VERT_TYPE__AUX == pv->type ) {
 
1530
                    extra_charge += delta;
 
1531
                } else {
 
1532
                    ret = RI_ERR_PROGR;
 
1533
                    goto exit_function;
 
1534
                }
 
1535
            }
 
1536
        }
 
1537
        if ( !extra_charge ) {
 
1538
            goto exit_function;
 
1539
        }
 
1540
        /* find differences */
 
1541
        num_candidates = 0;
 
1542
        for ( i = 0; i < pBNS->num_vertices; i ++ ) {
 
1543
            pnRad[i] = (pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow) - pnRad[i];
 
1544
            if ( pnRad[i] > 0 &&  i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) {
 
1545
                num_candidates ++;
 
1546
            }
 
1547
        }
 
1548
    }
 
1549
    if ( num_candidates > 0 ) {
 
1550
        pCand = (CC_CAND *)inchi_calloc( num_candidates, sizeof(pCand[0]) );
 
1551
        if ( !pCand ) {
 
1552
            ret = RI_ERR_ALLOC;
 
1553
            goto exit_function;
 
1554
        }
 
1555
        /* create atom */
 
1556
        memcpy( at2, at, len_at*sizeof(at2[0]));
 
1557
        pStruct->at = at2;
 
1558
        ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
1559
        pStruct->at = at;
 
1560
        if ( ret2 < 0 ) {
 
1561
            ret = ret2;
 
1562
            goto exit_function;
 
1563
        }
 
1564
 
 
1565
        for ( i = 0, j = 0; i < pBNS->num_vertices; i ++ ) {
 
1566
            if ( pnRad[i] > 0 &&  i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) {
 
1567
                pCand[j].iat                  = i;
 
1568
                pCand[j].num_bonds            = at2[i].valence;
 
1569
                pCand[j].chem_valence         = at2[i].chem_bonds_valence;
 
1570
                pCand[j].cMetal               = pVA[i].cMetal;
 
1571
                pCand[j].cNumBondsToMetal     = pVA[i].cNumBondsToMetal;
 
1572
                pCand[j].cNumValenceElectrons = pVA[i].cNumValenceElectrons;
 
1573
                pCand[j].cPeriodicRowNumber   = pVA[i].cPeriodicRowNumber;
 
1574
                pCand[j].el_number            = at2[i].el_number;
 
1575
                cnBits = (pVA[i].cnListIndex > 0)? cnList[pVA[i].cnListIndex-1].bits : 0;
 
1576
                while ( cnBits > 0 ) {
 
1577
                    pCand[j].cNumChargeStates ++;
 
1578
                    cnBits >>= cn_bits_shift;
 
1579
                }
 
1580
                j ++;
 
1581
            }
 
1582
        }
 
1583
        if ( j > 1 ) {
 
1584
            qsort( pCand, j, sizeof(pCand[0]), comp_cc_cand );
 
1585
        }
 
1586
        added_charge = 0;
 
1587
        
 
1588
        for ( k = 0; k < j; k ++ ) {
 
1589
            int rest_of_charge = extra_charge - added_charge;
 
1590
            int charge_per_left_atom = (abs(rest_of_charge) + j-k - 1)/(j-k);
 
1591
            int this_atom_add_charge = rest_of_charge > 0? charge_per_left_atom : -charge_per_left_atom;
 
1592
            pVA[pCand[k].iat].cInitCharge += this_atom_add_charge;
 
1593
            added_charge                  += this_atom_add_charge;
 
1594
            if ( this_atom_add_charge ) {
 
1595
                for ( i = pBNS->num_vertices-1, pv = pBNS->vert + i; this_atom_add_charge && pBNS->num_atoms <= i; i --, pv -- ) {
 
1596
                    if ( delta = pv->st_edge.cap - pv->st_edge.flow ) {
 
1597
                        if ( this_atom_add_charge < 0 && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
 
1598
                            if ( delta + this_atom_add_charge > 0 ) {
 
1599
                                delta = -this_atom_add_charge;
 
1600
                            }
 
1601
                            pv->st_edge.cap      -= delta;
 
1602
                            pBNS->tot_st_cap     -= delta;
 
1603
                            this_atom_add_charge += delta;
 
1604
                        } else
 
1605
                        if ( this_atom_add_charge > 0 && BNS_VERT_TYPE__AUX == pv->type ) {
 
1606
                            if ( delta > this_atom_add_charge ) {
 
1607
                                delta = this_atom_add_charge;
 
1608
                            }
 
1609
                            pv->st_edge.cap      -= delta;
 
1610
                            pBNS->tot_st_cap     -= delta;
 
1611
                            this_atom_add_charge -= delta;
 
1612
                        }
 
1613
                    }  
 
1614
                }
 
1615
            }
 
1616
        }
 
1617
    }
 
1618
 
 
1619
exit_function:
 
1620
    if ( pnRad ) {
 
1621
        inchi_free( pnRad );
 
1622
    }
 
1623
    if ( pnDelta ) {
 
1624
        inchi_free( pnDelta );
 
1625
    }
 
1626
    if ( pCand ) {
 
1627
        inchi_free( pCand );
 
1628
    }
 
1629
    return ret;
 
1630
}
 
1631
#endif
 
1632
/**************************************************************************************************/
 
1633
typedef struct tagMobileHGroups {
 
1634
    AT_NUMB group_number;
 
1635
    AT_NUMB atom_number;
 
1636
    AT_NUMB atom_type_pVA;
 
1637
    S_CHAR  ineigh;
 
1638
    S_CHAR  bond_type;
 
1639
    S_CHAR  forbidden;
 
1640
   /* S_CHAR  el_type;*/
 
1641
    S_CHAR  endpoint_valence;
 
1642
    S_CHAR  num_bonds;
 
1643
    S_CHAR  bonds_valence;
 
1644
    S_CHAR  num_bonds_non_metal;
 
1645
    S_CHAR  bonds_valence_non_metal;
 
1646
 
 
1647
} MOBILE_GR;
 
1648
 
 
1649
typedef struct tagMobileGroupList {
 
1650
    AT_NUMB group_number;
 
1651
    AT_NUMB num;
 
1652
} MGROUPS;
 
1653
/**************************************************************************************************/
 
1654
int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA,
 
1655
                                    int num_atoms, int forbidden_mask )
 
1656
{
 
1657
    int i, j, k;
 
1658
    int centerpoint_type, neigh_type;
 
1659
    int num_changes;
 
1660
    int num_donors, num_acceptors, num_donor_endpoints, num_acceptor_endpoints;
 
1661
    int neigh, tg_number, num_eql_mobile_gr, num_dif_mobile_gr, bond_type, has_mobile_H, has_mobile;
 
1662
    int num_forbidden, ind_forbidden, forbidden, num_N, num_O, num_P, num_S, num_OSt;
 
1663
    int val, delta_val, delta_met, num_bonds_non_metal, bonds_valence_non_metal;
 
1664
    int num_bonds, bonds_valence;
 
1665
    int inv_forbidden_mask = ~forbidden_mask;
 
1666
    MOBILE_GR MobileGr[MAXVAL];
 
1667
    int       num_endpoints;
 
1668
    MGROUPS   MGroups[MAXVAL];
 
1669
    int       num_mgroups, num_diff_t_groups;
 
1670
    BNS_EDGE   *e, *e1, *e2, *ev, *ev1, *ev2;
 
1671
    BNS_VERTEX *pv1, *pv2;
 
1672
    num_changes = 0;
 
1673
    /* search for possible centerpoints */
 
1674
    for ( i = 0; i < num_atoms; i ++ ) {
 
1675
        
 
1676
        if ( at[i].chem_bonds_valence == at[i].valence || at[i].num_H ||
 
1677
             at[i].endpoint || at[i].charge || at[i].radical ||
 
1678
             !is_centerpoint_elem(at[i].el_number) ||
 
1679
             !(centerpoint_type = get_pVA_atom_type( pVA, at, i, 0 )) ||
 
1680
             2 > (delta_val = at[i].chem_bonds_valence - (val = get_el_valence(at[i].el_number, 0, 0))) ||
 
1681
             2 > (delta_met = (bonds_valence_non_metal = nNoMetalBondsValence(at, i)) - val )
 
1682
           ) {
 
1683
            continue;
 
1684
        }
 
1685
        
 
1686
        num_donors = num_acceptors = num_donor_endpoints = num_acceptor_endpoints = 0;
 
1687
        num_mgroups = num_endpoints = num_diff_t_groups = 0;
 
1688
        has_mobile = has_mobile_H = num_eql_mobile_gr = num_dif_mobile_gr = tg_number = 0;
 
1689
        ind_forbidden = -1;
 
1690
        num_forbidden = 0;
 
1691
        num_N = num_O = num_P = num_S = num_OSt = 0;
 
1692
        num_bonds_non_metal = nNoMetalNumBonds(at, i);
 
1693
        bonds_valence = at[i].chem_bonds_valence;
 
1694
        num_bonds     = at[i].valence;
 
1695
 
 
1696
        for ( j = 0; j < at[i].valence; j ++ ) {
 
1697
            /* collect neighbors info */
 
1698
            neigh      = at[i].neighbor[j];
 
1699
            val        = get_endpoint_valence( at[neigh].el_number );
 
1700
            forbidden  = pBNS->edge[(int)pBNS->vert[i].iedge[j]].forbidden;
 
1701
            bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK);
 
1702
            neigh_type = get_pVA_atom_type( pVA, at, neigh, bond_type);
 
1703
            if ( !forbidden && !at[neigh].endpoint ) {
 
1704
                /* save forbidden bonds */
 
1705
                if ( is_el_a_metal(at[neigh].el_number) ) {
 
1706
                    continue;
 
1707
                }
 
1708
                switch( bond_type ) {
 
1709
                case BOND_TYPE_SINGLE:
 
1710
                    if ( !at[neigh].num_H && at[neigh].charge != -1 ) {
 
1711
                        continue; /* not a donor */
 
1712
                    }
 
1713
                    break;
 
1714
                case BOND_TYPE_DOUBLE:
 
1715
                    if ( !neigh_type ) {
 
1716
                        continue;
 
1717
                    }
 
1718
                    break;
 
1719
                default:
 
1720
                    continue;
 
1721
                }
 
1722
            }
 
1723
 
 
1724
            MobileGr[num_endpoints].atom_number      = neigh;
 
1725
            MobileGr[num_endpoints].ineigh           = j;
 
1726
            MobileGr[num_endpoints].bond_type        = bond_type;
 
1727
            MobileGr[num_endpoints].group_number     = at[neigh].endpoint;
 
1728
            MobileGr[num_endpoints].endpoint_valence = val;
 
1729
            MobileGr[num_endpoints].forbidden        = forbidden;
 
1730
            MobileGr[num_endpoints].atom_type_pVA    = neigh_type;
 
1731
            MobileGr[num_endpoints].num_bonds        = at[neigh].valence;
 
1732
            MobileGr[num_endpoints].bonds_valence    = at[neigh].chem_bonds_valence;
 
1733
            MobileGr[num_endpoints].num_bonds_non_metal     = nNoMetalNumBonds(at, neigh);
 
1734
            MobileGr[num_endpoints].bonds_valence_non_metal = nNoMetalBondsValence( at, neigh );
 
1735
 
 
1736
            if ( forbidden & forbidden_mask ) {
 
1737
                num_forbidden ++;
 
1738
                ind_forbidden = num_endpoints;
 
1739
            }
 
1740
            num_O   += 0 != (neigh_type & EL_TYPE_O) && at[neigh].valence == 1; /* ignore -O- */
 
1741
            num_N   += 0 != (neigh_type & EL_TYPE_N) &&
 
1742
                            !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -N< */
 
1743
            num_S   += 0 != (neigh_type & EL_TYPE_S) && at[neigh].valence == 1; /* ignore -S- */
 
1744
            num_P   += 0 != (neigh_type & EL_TYPE_P) &&
 
1745
                            !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -P< */
 
1746
            num_OSt += 0 != (neigh_type & EL_TYPE_OSt);
 
1747
            num_acceptors += (bond_type == BOND_TYPE_DOUBLE) && (neigh_type & EL_TYPE_PT);
 
1748
            num_donors    += (bond_type == BOND_TYPE_SINGLE) && (neigh_type & EL_TYPE_PT) &&
 
1749
                             (at[neigh].num_H || at[neigh].charge==-1 || at[neigh].endpoint);
 
1750
            if ( at[neigh].endpoint ) {
 
1751
                num_acceptor_endpoints += (bond_type == BOND_TYPE_DOUBLE);
 
1752
                num_donor_endpoints    += (bond_type == BOND_TYPE_SINGLE);
 
1753
                if ( !tg_number ) {
 
1754
                    tg_number = at[neigh].endpoint;
 
1755
                    num_eql_mobile_gr = 1;
 
1756
                } else
 
1757
                if ( tg_number == at[neigh].endpoint ) {
 
1758
                    num_eql_mobile_gr ++;
 
1759
                } else {
 
1760
                    num_dif_mobile_gr ++;
 
1761
                }
 
1762
            } else
 
1763
            if ( bond_type == BOND_TYPE_SINGLE && val ) {
 
1764
                if ( at[neigh].endpoint ) {
 
1765
                    has_mobile_H |= 1; 
 
1766
                    has_mobile   |= 1; 
 
1767
                } else {
 
1768
                    has_mobile_H |= (0 != at[neigh].num_H); 
 
1769
                    has_mobile   |= (0 != at[neigh].num_H) || (at[neigh].charge == -1); 
 
1770
                }
 
1771
            }
 
1772
            num_endpoints ++;
 
1773
 
 
1774
            if ( at[neigh].endpoint || (neigh_type & EL_TYPE_PT) ) {
 
1775
                for ( k = 0; k < num_mgroups; k ++ ) {
 
1776
                    if ( MGroups[k].group_number == at[neigh].endpoint ) {
 
1777
                        MGroups[k].num ++;
 
1778
                        break;
 
1779
                    }
 
1780
                }
 
1781
                if ( k == num_mgroups ) {
 
1782
                    MGroups[k].group_number = at[neigh].endpoint;
 
1783
                    MGroups[k].num          = 1;
 
1784
                    num_mgroups ++;
 
1785
                    num_diff_t_groups += (0 != at[neigh].endpoint);
 
1786
                }
 
1787
            }
 
1788
        }
 
1789
        if ( !num_acceptors || !num_donors || /* num_acceptors > 2 ||*/
 
1790
             num_eql_mobile_gr == num_endpoints && !num_forbidden ||
 
1791
             !tg_number && !has_mobile_H ) {
 
1792
            continue; /* nothing to do */
 
1793
        }
 
1794
        
 
1795
/* case_5_1: */
 
1796
        /***************** determine the case ************************/
 
1797
        if ( 3 == num_bonds_non_metal &&
 
1798
             4 == bonds_valence_non_metal &&
 
1799
             (centerpoint_type == EL_TYPE_C) &&
 
1800
             2 == num_O && 1 == num_N+num_S && num_OSt &&
 
1801
             1 == num_forbidden && 3 == num_eql_mobile_gr  ) {
 
1802
            /******************************************************** 
 
1803
             ***         InChI Tech. Man., Table 5, case 1        *** 
 
1804
             ******************************************************** 
 
1805
                       2                  
 
1806
                      OH                OH     X  =  N, S, Se, Te
 
1807
                     /                 /       f  =  fixed bond
 
1808
                 e  /                 /        tg =  Mobile-H vertex
 
1809
              HX---C      -->    X===C   
 
1810
           ev2|| f  \\ ev1         f  \  
 
1811
              ||     \\                \ 
 
1812
              tg------O                 OH
 
1813
                       1
 
1814
            Problem:
 
1815
              XH, O, and O belong to the same Mobile-H group.
 
1816
              Fixed bond prevents the correct structure restoration:
 
1817
              H cannot migrate from X to O because HX-N bond is fixed
 
1818
            Solution:
 
1819
              Move H from X to allow XH-C bond change
 
1820
              (this unfixes the bond, see SetForbiddenEdges(...) )
 
1821
            *********************************************************/
 
1822
            int jXH = -1, jO1 = -1, jO2 = -1, n = 0;
 
1823
            for ( j = 0; j < num_endpoints; j ++ ) {
 
1824
                if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) &&
 
1825
                     (MobileGr[j].forbidden == forbidden_mask) &&
 
1826
                     MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
1827
                     jXH < 0 ) {
 
1828
                    jXH = j;
 
1829
                    n ++;
 
1830
                } else
 
1831
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O &&
 
1832
                     MobileGr[j].num_bonds_non_metal == 1 &&
 
1833
                     !MobileGr[j].forbidden ) {
 
1834
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jO1 < 0 ) {
 
1835
                        jO1 = j;
 
1836
                        n ++;
 
1837
                    } else
 
1838
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && jO2 < 0 ) {
 
1839
                        jO2 = j;
 
1840
                        n ++;
 
1841
                    }
 
1842
                }
 
1843
            }
 
1844
            if ( n != 3 ) {
 
1845
                goto case_5_2;
 
1846
            }
 
1847
            /* XH-C edge */
 
1848
            e   = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh];
 
1849
            /* C=O  edge */
 
1850
            ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh];
 
1851
            /* XH-tg edge */
 
1852
            ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1;
 
1853
 
 
1854
            if ( !ev1->flow || !ev2->flow ) {
 
1855
                goto case_5_2;
 
1856
            }
 
1857
 
 
1858
            /* do not remove forbidden edge bit */
 
1859
            e->flow ++;
 
1860
            ev1->flow --;
 
1861
            ev2->flow --;
 
1862
            pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --;
 
1863
            pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --;
 
1864
            pBNS->tot_st_flow -= 2;
 
1865
            num_changes ++;
 
1866
            continue;
 
1867
        }
 
1868
case_5_2:
 
1869
        /*********************************************************************/
 
1870
        if ( 3 == num_bonds_non_metal &&
 
1871
             5 == bonds_valence_non_metal &&
 
1872
             (centerpoint_type == EL_TYPE_N) &&
 
1873
             2 == num_O && 1 == num_N+num_S &&
 
1874
             1 == num_forbidden && 3 == num_eql_mobile_gr  ) {
 
1875
            /******************************************************** 
 
1876
             ***         InChI Tech. Man., Table 5, case 2        *** 
 
1877
             ******************************************************** 
 
1878
                                         
 
1879
                      O                 OH     X  =  N, S, Se, Te
 
1880
                     //                /       f  =  fixed bond
 
1881
                 e  //                /        tg =  Mobile-H vertex
 
1882
              HX---N      -->    X===N   
 
1883
           ev2|| f  \\ ev1         f  \\ 
 
1884
              ||     \\                \\
 
1885
              tg------O                 O
 
1886
              
 
1887
            Problem:
 
1888
              XH, O, and O belong to the same Mobile-H group.
 
1889
              Fixed bond prevents the correct structure restoration:
 
1890
              H cannot migrate from X to O because HX-N bond is fixed
 
1891
            Solution:
 
1892
              Move H from X to allow XH-N bond change
 
1893
              (this unfixes the bond, see SetForbiddenEdges(...) )
 
1894
            *********************************************************/
 
1895
            int jXH = -1, jO1 = -1, jO2 = -1, n = 0;
 
1896
            for ( j = 0; j < num_endpoints; j ++ ) {
 
1897
                if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) &&
 
1898
                     (MobileGr[j].forbidden == forbidden_mask) &&
 
1899
                     MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
1900
                     jXH < 0 ) {
 
1901
                    jXH = j;
 
1902
                    n ++;
 
1903
                } else
 
1904
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O &&
 
1905
                     MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
1906
                     MobileGr[j].num_bonds_non_metal == 1 &&
 
1907
                     !MobileGr[j].forbidden ) {
 
1908
                    if ( jO1 < 0 ) {
 
1909
                        jO1 = j;
 
1910
                        n ++;
 
1911
                    } else
 
1912
                    if ( jO2 < 0 ) {
 
1913
                        jO2 = j;
 
1914
                        n ++;
 
1915
                    }
 
1916
                }
 
1917
            }
 
1918
            if ( n != 3 ) {
 
1919
                goto case_5_4;
 
1920
            }
 
1921
            /* XH-N edge */
 
1922
            e   = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh];
 
1923
            /* N=O  edge */
 
1924
            ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh];
 
1925
            /* XH-tg edge */
 
1926
            ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1;
 
1927
 
 
1928
            if ( !ev1->flow || !ev2->flow ) {
 
1929
                goto case_5_4;
 
1930
            }
 
1931
            /* do not remove forbidden edge bit */
 
1932
            e->flow ++;
 
1933
            ev1->flow --;
 
1934
            ev2->flow --;
 
1935
            pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */
 
1936
            pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */
 
1937
            pBNS->tot_st_flow -= 2;
 
1938
            num_changes ++;
 
1939
            continue;
 
1940
        }
 
1941
case_5_4:
 
1942
        /*********************************************************************/
 
1943
        if ( 3 == num_bonds_non_metal &&
 
1944
             5 == bonds_valence_non_metal &&
 
1945
             (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) &&
 
1946
             1 == num_O+num_S && 0 < num_N && 2 == (num_N + num_P) &&
 
1947
             1 == num_forbidden && num_O+num_S+num_N == num_eql_mobile_gr  ) {
 
1948
            /******************************************************** 
 
1949
             ***         InChI Tech. Man., Table 5, case 4        *** 
 
1950
             ******************************************************** 
 
1951
                                               O  =  O, S, Se, Te
 
1952
                       X                 X     X  =  N, P, As
 
1953
                     //                //      f  =  fixed bond
 
1954
                 e  // ev2            //       tg =  Mobile-H vertex
 
1955
               O===N      -->   HO---N   
 
1956
              || f  \  ev1         f  \\ 
 
1957
              ||     \                 \\
 
1958
              tg------NH                N 
 
1959
                        
 
1960
            Problem:
 
1961
              O, NH, and possibly X belong to the same Mobile-H group.
 
1962
              Fixed bond prevents the correct structure restoration:
 
1963
              H cannot migrate from NH to O because O=N bond is fixed
 
1964
            Solution:
 
1965
              Move H from NH to O to allow O=N bond change
 
1966
              (this unfixes the bond, see fix_special_bonds(...) )
 
1967
            *********************************************************/
 
1968
            int jO = -1, jNH = -1, jX = -1, n = 0;
 
1969
            for ( j = 0; j < num_endpoints; j ++ ) {
 
1970
                if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
 
1971
                     MobileGr[j].forbidden == forbidden_mask &&
 
1972
                     MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
1973
                     MobileGr[j].num_bonds_non_metal == 1 &&
 
1974
                     jO < 0 ) {
 
1975
                    jO = j;
 
1976
                    n ++;
 
1977
                } else
 
1978
                if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) &&
 
1979
                     !MobileGr[j].forbidden ) {
 
1980
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
1981
                         (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && jNH < 0 ) {
 
1982
                        jNH = j;
 
1983
                        n ++;
 
1984
                    } else
 
1985
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jX < 0 ) {
 
1986
                        jX = j;
 
1987
                        n ++;
 
1988
                    }
 
1989
                }
 
1990
            }
 
1991
            if ( n != 3 ) {
 
1992
                goto case_5_6;
 
1993
            }
 
1994
            /* O=N edge */
 
1995
            e   = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh];
 
1996
            /* N-NH  edge */
 
1997
            ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh];
 
1998
            /* N=X edge */
 
1999
            ev2 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jX].ineigh];
 
2000
 
 
2001
            if ( !e->flow ) {
 
2002
                goto case_5_6;
 
2003
            }
 
2004
            /* do not remove forbidden edge bit */
 
2005
            e->flow --;
 
2006
            ev1->flow ++;
 
2007
            pBNS->vert[e->neighbor12 ^ i].st_edge.flow --;
 
2008
            pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --;
 
2009
            pBNS->tot_st_flow -= 2;
 
2010
            num_changes ++;
 
2011
            continue;
 
2012
        }
 
2013
case_5_6:
 
2014
        /********* InChI Tech.Man. Table 5, case 6 **************/
 
2015
        if ( 2 == delta_met && 4 == num_bonds_non_metal &&
 
2016
             5 == bonds_valence_non_metal &&
 
2017
             1 == num_forbidden && 1 < num_eql_mobile_gr &&
 
2018
             !num_dif_mobile_gr &&
 
2019
             (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) &&
 
2020
             1 <= num_N && 2 <= num_N+num_O+num_S &&
 
2021
             1 == num_acceptor_endpoints && 0 < num_donor_endpoints ) {
 
2022
            int jN = -1, njFix = 0, jFix[4], n = 0;
 
2023
                /* centerpoint is N, P, As, Sb
 
2024
 
 
2025
                  input             output
 
2026
                  -----             ------
 
2027
                     end 
 
2028
                     po- 
 
2029
                     int 
 
2030
                     2
 
2031
                     
 
2032
                  X  ZH              X  Z     Z=N,O,S,Se,Te [terminal endpoint]
 
2033
                   \ |                \ ||      
 
2034
                    \| f   f           \||      
 
2035
                 Y---N===N---       Y---N---NH---
 
2036
                       e                  f      
 
2037
                    cen  end         no bond           
 
2038
                    ter  po-         fixed  
 
2039
                    po-  int          
 
2040
                    int  1        tautomerism O==N--NH is allowed
 
2041
                    
 
2042
                  Problem: OH and =N- belong to a Mobile-H group, but
 
2043
                           forbidden edge e does not allow them to be
 
2044
                           tautomeric in the restored structure.
 
2045
 
 
2046
                  Solution:
 
2047
                    
 
2048
                  1. Decrement flow in edge e
 
2049
                  2. Decrement st_edge flow in N and N connected by e
 
2050
                  3. Fix all single order bonds to not terminal tautomeric N around N(centerpoint)
 
2051
                  4. Run BNS to establist new flow distribution
 
2052
                */
 
2053
            /* fixed bond */
 
2054
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2055
                neigh = MobileGr[j].atom_number;
 
2056
                if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2057
                     (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
 
2058
                     MobileGr[j].num_bonds_non_metal == 2     &&
 
2059
                     MobileGr[j].bonds_valence_non_metal == 3 &&
 
2060
                     at[neigh].endpoint &&
 
2061
                     !at[neigh].num_H && !at[neigh].charge &&
 
2062
                     !at[neigh].radical &&
 
2063
                     (MobileGr[j].forbidden & forbidden_mask) && jN < 0 ) {
 
2064
                    jN = j;
 
2065
                    n ++;
 
2066
                } else
 
2067
                if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2068
                     at[neigh].endpoint ) {
 
2069
                    if ( MobileGr[j].num_bonds > 1 ) {
 
2070
                        jFix[njFix ++] = j;
 
2071
                    }
 
2072
                    n ++;
 
2073
                }
 
2074
            }
 
2075
 
 
2076
            if ( jN < 0 || n < 2 || 1 + njFix == n ) {
 
2077
                goto case_5_7;  /* nothing to do */
 
2078
            }
 
2079
 
 
2080
            e   = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN].ineigh];  /* fixed edge */
 
2081
            if ( !e->flow ) {
 
2082
                goto case_5_7;
 
2083
            }
 
2084
            e->flow --;
 
2085
            pBNS->vert[i].st_edge.flow --;
 
2086
            pBNS->vert[e->neighbor12 ^ i].st_edge.flow --;
 
2087
            pBNS->tot_st_flow -= 2;
 
2088
 
 
2089
            for ( j = 0; j < njFix; j ++ ) {
 
2090
                /* edges to fix */
 
2091
                ev = pBNS->edge + pBNS->vert[i].iedge[(int)MobileGr[jFix[j]].ineigh];
 
2092
                ev->forbidden |= forbidden_mask;
 
2093
            }
 
2094
            num_changes ++;
 
2095
            continue;
 
2096
        }
 
2097
case_5_7:
 
2098
        /*********************************************************************/
 
2099
        if ( 3 == num_bonds_non_metal &&
 
2100
             4 == bonds_valence_non_metal &&
 
2101
             (centerpoint_type == EL_TYPE_S) &&
 
2102
             2 == num_O+num_S && 1 == num_OSt && 1 == num_N &&
 
2103
             1 == num_forbidden && 3 == num_eql_mobile_gr && 
 
2104
             MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
 
2105
            /******************************************************** 
 
2106
             ***         InChI Tech. Man., Table 5, case 7        *** 
 
2107
             ******************************************************** 
 
2108
                                               O  =  O, S, Se, Te
 
2109
                      OH                OH     S  =  S, Se, Te
 
2110
                     /                 /       f  =  fixed bond
 
2111
                 e  /ev2           f  /        tg =  Mobile-H vertex
 
2112
              HN---S      -->    N===S         X  =  N or non-endpoint;
 
2113
              || f  \\                \
 
2114
           ev2||     \\ev1             \ 
 
2115
              tg------O                 OH
 
2116
                                               N, O, O
 
2117
            Problem:                           =======
 
2118
              O, NH, OH belong to the same Mobile-H group.
 
2119
              Fixed bond prevents the correct structure restoration:
 
2120
              H cannot migrate from NH to O because HN-S bond is fixed
 
2121
            Solution:
 
2122
              Move H from NH to =O to allow HN=S bond change by making a 2nd terminal -OH
 
2123
              (this unfixes the bond, see fix_special_bonds(...) )
 
2124
            *********************************************************/
 
2125
            int jO = -1, jNH = -1, jOH = -1, n = 0;
 
2126
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2127
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
 
2128
                     MobileGr[j].forbidden == forbidden_mask &&
 
2129
                     MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2130
                     MobileGr[j].num_bonds_non_metal <= 2 &&
 
2131
                     jNH < 0 ) {
 
2132
                    jNH = j;
 
2133
                    n ++;
 
2134
                } else
 
2135
                if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
 
2136
                     !MobileGr[j].forbidden &&
 
2137
                     MobileGr[j].num_bonds_non_metal == 1 ) {
 
2138
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2139
                         jO < 0 ) {
 
2140
                        jO = j;
 
2141
                        n ++;
 
2142
                    } else
 
2143
                    if ( jOH < 0 ) {
 
2144
                        jOH = j;
 
2145
                        n ++;
 
2146
                    }
 
2147
                }
 
2148
            }
 
2149
            if ( n != 3 ) {
 
2150
                goto case_5_9a;
 
2151
            }
 
2152
            /* NH-S edge */
 
2153
            e   = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh];
 
2154
            /* S=O  edge */
 
2155
            ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh];
 
2156
            /* XH-tg edge */
 
2157
            ev2 = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1;
 
2158
 
 
2159
            if ( !ev1->flow || !ev2->flow ) {
 
2160
                goto case_5_9a;
 
2161
            }
 
2162
 
 
2163
            /* do not remove forbidden edge bit */
 
2164
            e->flow ++;
 
2165
            ev1->flow --;
 
2166
            ev2->flow --;
 
2167
            pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */
 
2168
            pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */
 
2169
            pBNS->tot_st_flow -= 2;
 
2170
            num_changes ++;
 
2171
            continue;
 
2172
        }
 
2173
case_5_9a:
 
2174
        /*********************************************************************/
 
2175
        if ( 3 == num_bonds_non_metal &&
 
2176
             4 == bonds_valence_non_metal &&
 
2177
             (centerpoint_type == EL_TYPE_S) &&
 
2178
             1 == num_O+num_S && !num_OSt && 1 <= num_N &&
 
2179
             1 == num_forbidden &&
 
2180
             num_O+num_S+num_N == num_eql_mobile_gr && 
 
2181
             MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
 
2182
            /******************************************************** 
 
2183
             ***         InChI Tech. Man., Table 5, case 9a       *** 
 
2184
             ******************************************************** 
 
2185
                                               O  =  O, S, Se, Te
 
2186
                      X                 X      S  =  S, Se, Te
 
2187
                     /                 /       f  =  fixed bond
 
2188
                    /                 /        tg =  Mobile-H vertex
 
2189
              HN---S      -->    N===S         X  =  N or non-endpoint;
 
2190
              ||    \\ e              \       -X is not -O(terminal)
 
2191
              ||    f\\               f\ 
 
2192
              tg------O                 OH
 
2193
                                               N, N, O  or N, O
 
2194
            Problem:                           ================
 
2195
              O, NH belong to the same Mobile-H group.
 
2196
              Fixed bond prevents the correct structure restoration:
 
2197
              H cannot migrate from NH to O because O=S bond is fixed
 
2198
            Solution:
 
2199
              Move H from NH to =O to allow O=S bond change by making a terminal -OH
 
2200
              (this unfixes the bond, see fix_special_bonds(...) )
 
2201
            *********************************************************/
 
2202
            int jO = -1, jNH = -1, jX = -1, n = 0;
 
2203
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2204
                if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
 
2205
                     MobileGr[j].forbidden == forbidden_mask &&
 
2206
                     MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2207
                     MobileGr[j].num_bonds_non_metal == 1 &&
 
2208
                     jO < 0 ) {
 
2209
                    jO = j;
 
2210
                    n ++;
 
2211
                } else
 
2212
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
 
2213
                     !MobileGr[j].forbidden &&
 
2214
                     MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2215
                     jNH < 0 ) {
 
2216
                    jNH = j;
 
2217
                    n ++;
 
2218
                } else
 
2219
                if ( jX < 0 ) {
 
2220
                    jX = j;
 
2221
                    n ++;
 
2222
                }
 
2223
            }
 
2224
            if ( jO < 0 || jNH < 0 ) {
 
2225
                goto case_5_8b_to_9b;
 
2226
            }
 
2227
 
 
2228
            e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[ind_forbidden].ineigh];
 
2229
            if ( !e->flow ) {
 
2230
                goto case_5_8b_to_9b;
 
2231
            }
 
2232
            e->flow --;
 
2233
            pBNS->vert[e->neighbor1].st_edge.flow --;
 
2234
            pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
 
2235
            pBNS->tot_st_flow -= 2;
 
2236
            num_changes ++;
 
2237
            continue;
 
2238
        }
 
2239
case_5_8b_to_9b: /* #1 */
 
2240
        /*********************************************************************/
 
2241
        if ( 3 == num_bonds_non_metal &&
 
2242
             4 == bonds_valence_non_metal &&
 
2243
             (centerpoint_type == EL_TYPE_S) &&
 
2244
             0 == num_O+num_S && 2 == num_N && 0 == num_P && !num_OSt &&
 
2245
             1 == num_forbidden &&
 
2246
             1 == num_eql_mobile_gr && 0 == num_dif_mobile_gr &&
 
2247
             1 == num_donor_endpoints && 0 == num_acceptor_endpoints &&
 
2248
             1 == num_donors          && 1 == num_acceptors &&
 
2249
             MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
 
2250
            /******************************************************** 
 
2251
             ***         InChI Tech. Man., Table 5, case 8b->9b   *** 
 
2252
             ******************************************************** 
 
2253
                             --->                    O  =  O, S, Se, Te      
 
2254
                      X                  X           S  =  S, Se, Te         
 
2255
             \      f/          \      f/            f  =  fixed bond        
 
2256
              \ ev  /  C=====Z   \     /  C-----ZH   tg =  Mobile-H vertex   
 
2257
               N===S   |     |    N===S  ||     ||   X  =  is N not an endpoint;
 
2258
             not    \  |     |         \ ||     ||  -X is not terminal -O,-S,-Se,-Te or
 
2259
             an      \ |  e  |          \||  e  ||        any N, P, As
 
2260
           endpoint   NH=====tg          N------tg
 
2261
                     is an                   f       N, N, X, fixed single
 
2262
                   endpoint                          =====================
 
2263
 
 
2264
            Problem:
 
2265
              N is not a Mobile-H endpoint, NH is a Mobile-H endpoint
 
2266
              Unfixed bond N==S prevents the correct structure restoration:
 
2267
              H can migrate from NH to N because N=S bond is not fixed
 
2268
            Solution:
 
2269
              Move H from NH to =Z to make N=S bond fixed (Table 5, case 9)
 
2270
              (this unfixes the bond, see fix_special_bonds(...) )
 
2271
            *********************************************************/
 
2272
            int jN = -1, jNH = -1, jX = -1, n = 0;
 
2273
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2274
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
 
2275
                     !(MobileGr[j].forbidden & forbidden_mask) ) {
 
2276
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2277
                         !at[MobileGr[j].atom_number].endpoint &&
 
2278
                         jN < 0 ) {
 
2279
                        jN = j;
 
2280
                        n ++;
 
2281
                    } else
 
2282
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2283
                         MobileGr[j].num_bonds == 2 &&
 
2284
                         MobileGr[j].bonds_valence == 2 &&
 
2285
                         at[MobileGr[j].atom_number].endpoint &&
 
2286
                         jNH < 0 ) {
 
2287
                        jNH = j;
 
2288
                        n ++;
 
2289
                    }
 
2290
                } else
 
2291
                if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) ||
 
2292
                       (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
 
2293
                        MobileGr[j].num_bonds > 1 ) &&
 
2294
                     (MobileGr[j].forbidden & forbidden_mask) &&
 
2295
                     MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2296
                     jX < 0 ) {
 
2297
                    jX = j;
 
2298
                    n ++;
 
2299
                }
 
2300
            }
 
2301
            if ( n != 3 ) {
 
2302
                goto case_5_8c_to_9c;
 
2303
            }
 
2304
 
 
2305
            e = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1;
 
2306
            if ( !e->flow ) {
 
2307
                goto case_5_8c_to_9c; /* should not happen ??? */
 
2308
            }
 
2309
            e->flow --;
 
2310
            pBNS->vert[e->neighbor1].st_edge.flow --;
 
2311
            pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
 
2312
            pBNS->tot_st_flow -= 2;
 
2313
            e->forbidden |= forbidden_mask;
 
2314
            num_changes ++;
 
2315
            continue;
 
2316
        }
 
2317
case_5_8c_to_9c: /* #2 */
 
2318
        /*********************************************************************/
 
2319
        if ( 3 == num_bonds_non_metal &&
 
2320
             4 == bonds_valence_non_metal &&
 
2321
             (centerpoint_type == EL_TYPE_S) &&
 
2322
             0 == num_O+num_S && 3 == num_N && 0 == num_P &&
 
2323
             1 == num_forbidden &&
 
2324
             3 == num_eql_mobile_gr && 0 == num_dif_mobile_gr &&
 
2325
             2 == num_donor_endpoints && 1 == num_acceptor_endpoints &&
 
2326
             2 == num_donors          && 1 == num_acceptors &&
 
2327
             MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
 
2328
            /******************************************************** 
 
2329
             ***         InChI Tech. Man., Table 5, case 8c->9c   *** 
 
2330
             ******************************************************** 
 
2331
                is an endpoint --->                  O  =  O, S, Se, Te      
 
2332
                      NH2(X)             NH2(X)      S  =  S, Se, Te         
 
2333
             \       / pv1  pv2 \       /            f  =  fixed bond        
 
2334
              \ 1   /  C-----ZH  \     /  C=====Z    tg =  Mobile-H vertex   
 
2335
               N===S  ||     ||   N===S   |     |    X  =  is N not an endpoint;
 
2336
            is an   \f||ev1  ||ev2     \f |     |   -X is not terminal -O,-S,-Se,-Te or
 
2337
          endpoint   \||  e  ||         \ |  e  |         any N, P, As
 
2338
                    2 N------tg          NH=====tg   C is a centerpoint of a t-group
 
2339
                     is an                   f       N, N, X, fixed single
 
2340
                   endpoint                          =====================
 
2341
 
 
2342
            Problem:
 
2343
              N is not a Mobile-H endpoint, NH is a Mobile-H endpoint
 
2344
              Unfixed bond N==S prevents the correct structure restoration:
 
2345
              H can migrate from NH to N because N=S bond is not fixed
 
2346
            Solution:
 
2347
              Move H from NH to =Z to make N=S bond fixed (Table 5, case 9)
 
2348
              (this unfixes the bond, see fix_special_bonds(...) )
 
2349
            *********************************************************/
 
2350
            int jN1 = -1, jN2 = -1, jX = -1, n = 0;
 
2351
            EdgeIndex ie, ie1, ie2;
 
2352
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2353
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
 
2354
                     !(MobileGr[j].forbidden & forbidden_mask) ) {
 
2355
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2356
                         at[MobileGr[j].atom_number].endpoint &&
 
2357
                         jN1 < 0 ) {
 
2358
                        jN1 = j;
 
2359
                        n ++;
 
2360
                    } else
 
2361
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2362
                         MobileGr[j].num_bonds == 2 &&
 
2363
                         MobileGr[j].bonds_valence == 3 &&
 
2364
                         MobileGr[j].forbidden == forbidden_mask &&
 
2365
                         at[MobileGr[j].atom_number].endpoint &&
 
2366
                         jN2 < 0 ) {
 
2367
                        jN2 = j;
 
2368
                        n ++;
 
2369
                    } else
 
2370
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2371
                         MobileGr[j].num_bonds <= 2 &&
 
2372
                         MobileGr[j].bonds_valence <= 3 &&
 
2373
                         at[MobileGr[j].atom_number].endpoint &&
 
2374
                         jX < 0 ) {
 
2375
                        jX = j;
 
2376
                        n ++;
 
2377
                    }
 
2378
                }
 
2379
            }
 
2380
            if ( n != 3 ) {
 
2381
                goto case_5_9b_to_8b;
 
2382
            }
 
2383
 
 
2384
            e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
 
2385
            if ( e->flow ) {
 
2386
                goto case_5_9b_to_8b; /* should not happen ??? */
 
2387
            }
 
2388
            pv1 = pBNS->vert + e->neighbor1;  /* must be jN2 */
 
2389
            pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12);
 
2390
            ie = e - pBNS->edge;
 
2391
            ie1 = ie2 = -1;
 
2392
            for ( j = 0; j < pv1->num_adj_edges; j ++ ) {
 
2393
                ev1 = pBNS->edge + pv1->iedge[j];
 
2394
                if ( ev1->flow && !ev1->forbidden ) {
 
2395
                    ie1 = ev1 - pBNS->edge;
 
2396
                    pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert));
 
2397
                    break;
 
2398
                }
 
2399
            }
 
2400
            for ( j = 0; j < pv2->num_adj_edges; j ++ ) {
 
2401
                ev2 = pBNS->edge + pv2->iedge[j];
 
2402
                if ( ev2->flow && !ev2->forbidden ) {
 
2403
                    ie2 = ev2 - pBNS->edge;
 
2404
                    pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert));
 
2405
                    break;
 
2406
                }
 
2407
            }
 
2408
            if ( ie1 < 0 || ie2 < 0 ) {
 
2409
                goto case_5_9b_to_8b;
 
2410
            }
 
2411
            e->flow ++;
 
2412
            e->forbidden |= forbidden_mask;
 
2413
            ev1->flow --;
 
2414
            ev2->flow --;
 
2415
            pv1->st_edge.flow --;
 
2416
            pv2->st_edge.flow --;
 
2417
            pBNS->tot_st_flow -= 2;
 
2418
            num_changes ++;
 
2419
            continue;
 
2420
        }
 
2421
case_5_9b_to_8b: /* #3 */
 
2422
        /*********************************************************************/
 
2423
        if ( 3 == num_bonds_non_metal &&
 
2424
             4 == bonds_valence_non_metal &&
 
2425
             (centerpoint_type == EL_TYPE_S) &&
 
2426
             0 == num_O+num_S && 2 == num_N && 0 == num_P &&
 
2427
             1 == num_forbidden &&
 
2428
             2 == num_eql_mobile_gr && 0 == num_dif_mobile_gr &&
 
2429
             1 == num_donor_endpoints && 1 == num_acceptor_endpoints &&
 
2430
             1 == num_donors          && 1 == num_acceptors &&
 
2431
             MobileGr[ind_forbidden].bond_type == BOND_TYPE_DOUBLE ) {
 
2432
            /******************************************************** 
 
2433
             ***         InChI Tech. Man., Table 5, case 9b->8b   *** 
 
2434
             ******************************************************** 
 
2435
                           --->                      O  =  O, S, Se, Te      
 
2436
                  X    is an              X          S  =  S, Se, Te         
 
2437
         \       /   endpoint    \       /           f  =  fixed bond        
 
2438
          \ 1ev /  C-----ZH       \     /  C=====Z   tg =  Mobile-H vertex   
 
2439
           N===S  ||     ||        N===S   |     |   X  =  is N not an endpoint;
 
2440
      is an  f  \ ||     ||          f  \  |     |  -X is not terminal -O,-S,-Se,-Te or
 
2441
   endpoint     2\||  e  ||              \ |  e  |        any N, P, As
 
2442
                  N------tg               NH=====tg
 
2443
               is an                          f      N, N, X, fixed double
 
2444
            endpoint                                 =====================
 
2445
 
 
2446
            Problem:
 
2447
              N1 and N2 are Mobile-H endpoints and belong to the same Mobile-H group.
 
2448
              Fixed bond N1==S prevents the correct structure restoration:
 
2449
              H cannot migrate ZH->N2->N1 because N1=S bond is fixed
 
2450
            Solution:
 
2451
              Move H from ZH to N2 to make N1=S bond unfixed and fix S-X bond (Table 5, case 8)
 
2452
              (see fix_special_bonds(...) for details )
 
2453
            *********************************************************/
 
2454
            int jN1 = -1, jN2 = -1, jX = -1, n = 0;
 
2455
            EdgeIndex ie, ie1, ie2;
 
2456
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2457
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) {
 
2458
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2459
                         at[MobileGr[j].atom_number].endpoint &&
 
2460
                         (MobileGr[j].forbidden == forbidden_mask) &&
 
2461
                         jN1 < 0 ) {
 
2462
                        jN1 = j;
 
2463
                        n ++;
 
2464
                    } else
 
2465
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2466
                         MobileGr[j].num_bonds == 2 &&
 
2467
                         MobileGr[j].bonds_valence == 3 &&
 
2468
                         at[MobileGr[j].atom_number].endpoint &&
 
2469
                         !(MobileGr[j].forbidden & forbidden_mask) &&
 
2470
                         jN2 < 0 ) {
 
2471
                        jN2 = j;
 
2472
                        n ++;
 
2473
                    }
 
2474
                } else
 
2475
                if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) ||
 
2476
                       (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
 
2477
                        MobileGr[j].num_bonds > 1 ) &&
 
2478
                     !(MobileGr[j].forbidden & forbidden_mask) &&
 
2479
                     MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2480
                     jX < 0 ) {
 
2481
                    jX = j;
 
2482
                    n ++;
 
2483
                }
 
2484
            }
 
2485
            if ( jN1 < 0 || jN2 < 0 ) {
 
2486
                goto case_5_9c_to_8c;
 
2487
            }
 
2488
 
 
2489
            ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh];
 
2490
            e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
 
2491
            if ( e->flow ) {
 
2492
                goto case_5_9c_to_8c; /* should not happen ??? */
 
2493
            }
 
2494
            pv1 = pBNS->vert + e->neighbor1;  /* must be jN2 */
 
2495
            pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12);
 
2496
            ie = e - pBNS->edge;
 
2497
            ie1 = ie2 = -1;
 
2498
            ev->forbidden &= inv_forbidden_mask;
 
2499
            for ( j = 0; j < pv1->num_adj_edges; j ++ ) {
 
2500
                ev1 = pBNS->edge + pv1->iedge[j];
 
2501
                if ( ev1->flow && !ev1->forbidden ) {
 
2502
                    ie1 = ev1 - pBNS->edge;
 
2503
                    pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert));
 
2504
                    break;
 
2505
                }
 
2506
            }
 
2507
            for ( j = 0; j < pv2->num_adj_edges; j ++ ) {
 
2508
                ev2 = pBNS->edge + pv2->iedge[j];
 
2509
                if ( ev2->flow && !ev2->forbidden ) {
 
2510
                    ie2 = ev2 - pBNS->edge;
 
2511
                    pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert));
 
2512
                    break;
 
2513
                }
 
2514
            }
 
2515
            if ( ie1 < 0 || ie2 < 0 ) {
 
2516
                ev->forbidden |= forbidden_mask; /* failed; restore the forbidden bit */ 
 
2517
                goto case_5_9c_to_8c;
 
2518
            }
 
2519
            e->flow ++;
 
2520
            e->forbidden |= forbidden_mask;
 
2521
            ev1->flow --;
 
2522
            ev2->flow --;
 
2523
            pv1->st_edge.flow --;
 
2524
            pv2->st_edge.flow --;
 
2525
            pBNS->tot_st_flow -= 2;
 
2526
            num_changes ++;
 
2527
            continue;
 
2528
        }
 
2529
case_5_9c_to_8c: /* #4 */
 
2530
        /*********************************************************************/
 
2531
        if ( 3 == num_bonds_non_metal &&
 
2532
             4 == bonds_valence_non_metal &&
 
2533
             (centerpoint_type == EL_TYPE_S) &&
 
2534
             0 == num_O+num_S && 3 == num_N && 0 == num_P &&
 
2535
             0 == num_forbidden &&
 
2536
             2 == num_diff_t_groups && 2 == num_mgroups && /* all neighbors belong to 2 t-groups */
 
2537
             3 == num_eql_mobile_gr + num_dif_mobile_gr && /* all 3 neighbors belong to t-groups */
 
2538
             2 == num_donor_endpoints && 1 == num_acceptor_endpoints &&
 
2539
             2 == num_donors          && 1 == num_acceptors ) {
 
2540
            /******************************************************** 
 
2541
             ***         InChI Tech. Man., Table 5, case 8c->9c   *** 
 
2542
             ******************************************************** 
 
2543
                is an endpoint --->                  O  =  O, S, Se, Te      
 
2544
                tg1   NH2(X)             NH2(X)      S  =  S, Se, Te         
 
2545
             \       / pv1  pv2 \       /            f  =  fixed bond        
 
2546
              \(1)  /  C=====Z   \     /  C-----ZH   tg =  Mobile-H vertex   
 
2547
               N===S   |     |    N===S  ||     ||   X  =  is N not an endpoint;
 
2548
            is an   \  |ev1  | ev2     \ ||     ||  -X is not terminal -O,-S,-Se,-Te or
 
2549
          endpoint   \ |  e  |          \||  e  ||        any N, P, As
 
2550
          tg1      (2)NH=====tg          N------tg   C is a centerpoint of a t-group
 
2551
                     is an                   f       N, N, X, fixed single
 
2552
                   endpoint                          =====================
 
2553
                   tg2
 
2554
            Problem:
 
2555
              N (1) and NH2 are Mobile-H group 1 endpoiints, NH (2) is a Mobile-H group 2 endpoint
 
2556
              Unfixed bonds N==S--NH(2) allows the two Mobile H groups to merge
 
2557
              hence prevents the correct structure restoration:
 
2558
              H can migrate from NH (2) to N (1) because S-NH(1) bond is not fixed
 
2559
            Solution:
 
2560
              Move H from NH(2) to =Z to make S-NH(2) bond fixed (Table 5, case 8c)
 
2561
              (this unfixes the bond, see fix_special_bonds(...) )
 
2562
            *********************************************************/
 
2563
            int jN1 = -1, jN2 = -1, jX = -1, n = 0;
 
2564
            /* find t-group that is represented by only one neighbor */
 
2565
            for ( j = 0, k = 0; j < num_mgroups; j ++ ) {
 
2566
                if ( 1 == MGroups[k].num && MGroups[k].group_number ) {
 
2567
                    k = MGroups[k].group_number;
 
2568
                    break;
 
2569
                }
 
2570
            }
 
2571
            if ( !k ) {
 
2572
                goto case_5_9c_to_9d;
 
2573
            }
 
2574
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2575
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) {
 
2576
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2577
                         at[MobileGr[j].atom_number].endpoint &&
 
2578
                         at[MobileGr[j].atom_number].endpoint != k &&
 
2579
                         jN1 < 0 ) {
 
2580
                        jN1 = j;
 
2581
                        n ++;
 
2582
                    } else
 
2583
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2584
                         MobileGr[j].num_bonds == 2 &&
 
2585
                         MobileGr[j].bonds_valence == 2 &&
 
2586
                         at[MobileGr[j].atom_number].endpoint == k &&
 
2587
                         jN2 < 0 ) {
 
2588
                        jN2 = j;
 
2589
                        n ++;
 
2590
                    } else
 
2591
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2592
                         MobileGr[j].num_bonds <= 2 &&
 
2593
                         MobileGr[j].bonds_valence <= 3 &&
 
2594
                         at[MobileGr[j].atom_number].endpoint &&
 
2595
                         at[MobileGr[j].atom_number].endpoint != k &&
 
2596
                         jX < 0 ) {
 
2597
                        jX = j;
 
2598
                        n ++;
 
2599
                    }
 
2600
                }
 
2601
            }
 
2602
            if ( n != 3 ) {
 
2603
                goto case_5_9c_to_9d;
 
2604
            }
 
2605
 
 
2606
            e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
 
2607
            if ( !e->flow ) {
 
2608
                goto case_5_9c_to_9d; /* should not happen ??? */
 
2609
            }
 
2610
            e->flow --;
 
2611
            pBNS->vert[e->neighbor1].st_edge.flow --;
 
2612
            pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
 
2613
            pBNS->tot_st_flow -= 2;
 
2614
            e->forbidden |= forbidden_mask;
 
2615
            num_changes ++;
 
2616
            continue;
 
2617
        }
 
2618
case_5_9c_to_9d: /* #6 */
 
2619
        /*********************************************************************/
 
2620
        if ( 3 == num_bonds_non_metal &&
 
2621
             4 == bonds_valence_non_metal &&
 
2622
             (centerpoint_type == EL_TYPE_S) &&
 
2623
             0 == num_O+num_S && 3 == num_N && 0 == num_P &&
 
2624
             0 == num_forbidden &&
 
2625
             3 == num_mgroups && 2 == num_diff_t_groups &&
 
2626
             2 == num_donor_endpoints && 0 == num_acceptor_endpoints &&
 
2627
             2 == num_donors          && 1 == num_acceptors ) {
 
2628
            /******************************************************** 
 
2629
             ***         InChI Tech. Man., Table 5, case 9b->8b   *** 
 
2630
             ******************************************************** 
 
2631
                 3(X)      --->         e2|          O  =  O, S, Se, Te      
 
2632
                  NH---is an              N======    S  =  S, Se, Te         
 
2633
         \       /   endpoint    \       /           f  =  fixed bond        
 
2634
          \ 1   /  C=====Z        \  f  /  C-----Z   tg =  Mobile-H vertex   
 
2635
           N===S   |     |         N===S  ||     ||  X  =  is N not an endpoint;
 
2636
     is an  ev  \  |     |           ev \ ||     || -X is not terminal -O,-S,-Se,-Te or
 
2637
   endpoint     2\ |  e1 |               \||  e1 ||       any N, P, As
 
2638
                  NH=====tg               N------tg
 
2639
               is an                          f      N, N, X, fixed double
 
2640
            endpoint                                 =====================
 
2641
 
 
2642
            Problem:
 
2643
              N1, N2, and N3 are Mobile-H endpoints and belong to the same Mobile-H group.
 
2644
              Fixed bond N1==S prevents the correct structure restoration:
 
2645
              H cannot migrate N3->N2->N1 because N1=S bond is fixed
 
2646
            Solution:
 
2647
              Move mobile H to N2 and N3 to make N1=S bond unfixed (Table 5, case 9c)
 
2648
              (see fix_special_bonds(...) for details )
 
2649
            *********************************************************/
 
2650
            int jN1 = -1, jN2 = -1, jX = -1, n = 0;
 
2651
            for ( j = 0; j < num_endpoints; j ++ ) {
 
2652
                if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) {
 
2653
                    if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
 
2654
                         !at[MobileGr[j].atom_number].endpoint &&
 
2655
                         !(MobileGr[j].forbidden & forbidden_mask) &&
 
2656
                         jN1 < 0 ) {
 
2657
                        jN1 = j;
 
2658
                        n ++;
 
2659
                    } else
 
2660
                    if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
 
2661
                         MobileGr[j].num_bonds == 2 &&
 
2662
                         MobileGr[j].bonds_valence <= 3 &&
 
2663
                         at[MobileGr[j].atom_number].endpoint &&
 
2664
                         !(MobileGr[j].forbidden & forbidden_mask) ) {
 
2665
                        if ( jN2 < 0 ) {
 
2666
                            jN2 = j;
 
2667
                            n ++;
 
2668
                        } else
 
2669
                        if ( jX < 0 ) {
 
2670
                            jX = j;
 
2671
                            n ++;
 
2672
                        }
 
2673
                    }
 
2674
                }
 
2675
            }
 
2676
            if ( n != 3 ) {
 
2677
                goto case_end;
 
2678
            }
 
2679
            ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh];
 
2680
            if ( !e->flow ) {
 
2681
                goto case_end;
 
2682
            }
 
2683
            e1 = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
 
2684
            if ( !e1->flow ) {
 
2685
                goto case_end; /* should not happen ??? */
 
2686
            }
 
2687
            e2 = pBNS->edge + pVA[MobileGr[jX].atom_number].nTautGroupEdge - 1;
 
2688
            if ( !e2->flow ) {
 
2689
                goto case_end; /* should not happen ??? */
 
2690
            }
 
2691
            /* take care of edge e1 */
 
2692
            e = e1;
 
2693
            e->flow --;
 
2694
            pBNS->vert[e->neighbor1].st_edge.flow --;
 
2695
            pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
 
2696
            pBNS->tot_st_flow -= 2;
 
2697
            e->forbidden |= forbidden_mask;
 
2698
            num_changes ++;
 
2699
            /* take care of edge e2 */
 
2700
            e = e2;
 
2701
            e->flow --;
 
2702
            pBNS->vert[e->neighbor1].st_edge.flow --;
 
2703
            pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
 
2704
            pBNS->tot_st_flow -= 2;
 
2705
            e->forbidden |= forbidden_mask;
 
2706
            num_changes ++;
 
2707
            /* take care of edge ev: do not let it change */
 
2708
            ev->forbidden |= forbidden_mask;
 
2709
            continue;
 
2710
        }
 
2711
case_end:;
 
2712
    }
 
2713
/*exit_function:*/
 
2714
    return num_changes;
 
2715
}
 
2716
/******************************************************************************************************/
 
2717
/* Replace ambiguous neutral (+)edge->flow=0, (-)edge->flow=1 with (+)edge->flow=1, (-)edge->flow=0   */
 
2718
/******************************************************************************************************/
 
2719
int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA,
 
2720
                                 ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask )
 
2721
{
 
2722
    int ret, ePlus, eMinus;
 
2723
    EDGE_LIST NewlyFixedEdges;
 
2724
    BNS_EDGE *pPlus, *pMinus;
 
2725
    int i, k1, k2, num_found, num_tot, delta, v1, v2;
 
2726
 
 
2727
    ret = 0;
 
2728
 
 
2729
    AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
 
2730
    for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) {
 
2731
        eMinus = pVA[i].nCMinusGroupEdge - 1;
 
2732
        ePlus  = pVA[i].nCPlusGroupEdge - 1;
 
2733
        num_tot += (eMinus >= 0) + (ePlus >= 0);
 
2734
        if ( eMinus >= 0 && ePlus >= 0 ) {
 
2735
            pPlus  = pBNS->edge + ePlus;
 
2736
            pMinus = pBNS->edge + eMinus;
 
2737
            if ( (k1=pMinus->flow) > 0 &&  (k2=pPlus->cap-pPlus->flow) > 0 ) {
 
2738
                num_found ++;
 
2739
            }
 
2740
        } 
 
2741
    }
 
2742
    if ( !num_found ) {
 
2743
        goto exit_function;
 
2744
    }
 
2745
    if ( ret = AllocEdgeList( &NewlyFixedEdges, num_tot + pBNS->num_bonds ) ) {
 
2746
        goto exit_function;
 
2747
    }
 
2748
 
 
2749
    for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) {
 
2750
        eMinus = pVA[i].nCMinusGroupEdge - 1;
 
2751
        ePlus  = pVA[i].nCPlusGroupEdge - 1;
 
2752
        num_tot += (eMinus >= 0) + (ePlus >= 0);
 
2753
        if ( eMinus >= 0 && ePlus >= 0 ) {
 
2754
            pPlus  = pBNS->edge + ePlus;
 
2755
            pMinus = pBNS->edge + eMinus;
 
2756
            if ( (k1=pMinus->flow) > 0 &&  (k2=pPlus->cap  - pPlus->flow) > 0 ) {
 
2757
                /* rearrange */
 
2758
                v1 = pMinus->neighbor1;
 
2759
                v2 = pMinus->neighbor12 ^ v1;
 
2760
                delta = inchi_min(k1,k2);
 
2761
                pMinus->flow -= delta;
 
2762
                pBNS->vert[v1].st_edge.flow -= delta;
 
2763
                pBNS->vert[v2].st_edge.flow -= delta;
 
2764
                pBNS->tot_st_flow -= 2*delta;
 
2765
            }
 
2766
            /* fix charges */
 
2767
            pPlus->forbidden  |= forbidden_edge_mask;
 
2768
            pMinus->forbidden |= forbidden_edge_mask;
 
2769
            if ( (ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) ||
 
2770
                 (ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 ))) {
 
2771
                goto exit_function;
 
2772
            }
 
2773
        } else
 
2774
        if ( eMinus >= 0 ) {
 
2775
            /* fix charges */
 
2776
            pMinus = pBNS->edge + eMinus;
 
2777
            pMinus->forbidden |= forbidden_edge_mask;
 
2778
            if ( ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) {
 
2779
                goto exit_function;
 
2780
            }
 
2781
        } else
 
2782
        if ( ePlus >= 0 ) {
 
2783
            /* fix charges */
 
2784
            pPlus  = pBNS->edge + ePlus;
 
2785
            pPlus->forbidden  |= forbidden_edge_mask;
 
2786
            if ( ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 )) {
 
2787
                goto exit_function;
 
2788
            }
 
2789
        }
 
2790
    }
 
2791
    for ( i = 0; i < pBNS->num_bonds; i ++ ) {
 
2792
        pBNS->edge[i].forbidden |= forbidden_edge_mask;
 
2793
        if ( ret = AddToEdgeList( &NewlyFixedEdges, i, 0 )) {
 
2794
            goto exit_function;
 
2795
        }
 
2796
    }
 
2797
    ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
2798
    RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
 
2799
    if ( ret < 0 ) {
 
2800
        goto exit_function;
 
2801
    }
 
2802
 
 
2803
exit_function:
 
2804
    AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE );
 
2805
    return ret;
 
2806
}
 
2807
/******************************************************************************************************/
 
2808
int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
2809
                                            inp_ATOM *at, inp_ATOM *at2,
 
2810
                                            VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
2811
                                            int *pnNumRunBNS, int *pnTotalDelta,
 
2812
                                            int forbidden_edge_mask)
 
2813
{
 
2814
#define FIX_BOND_ADD_ALLOC 128    
 
2815
    Vertex     vPathStart, vPathEnd;
 
2816
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
2817
    BNS_EDGE   *pe, *peZero, *peNeighMeigh = NULL, *peMeFlower;
 
2818
    BNS_VERTEX *pMeFlower = NULL, *pNeigh = NULL, *pNeighNeigh=NULL;
 
2819
    
 
2820
    int i, j, k, ret2, ret, bFixedCarbonCharges, num_changes, bSuccess;
 
2821
    int num_at = pStruct->num_atoms;
 
2822
    int num_deleted_H = pStruct->num_deleted_H;
 
2823
    int len_at = num_at + num_deleted_H;
 
2824
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
2825
    Vertex vMeFlower0, vNeigh, vNeighMeigh = NO_VERTEX;
 
2826
    
 
2827
    EDGE_LIST CarbonChargeEdges;
 
2828
    EDGE_LIST NewlyFixedEdges;
 
2829
 
 
2830
    ret = 0;
 
2831
    num_changes = 0;
 
2832
 
 
2833
    bFixedCarbonCharges = 0;
 
2834
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
 
2835
    AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
 
2836
 
 
2837
    if ( !pTCGroups->num_metal_atoms || 
 
2838
         0 > (k=pTCGroups->nGroup[TCG_MeFlower0]) ||
 
2839
         0 > (vMeFlower0 = pTCGroups->pTCG[k].nVertexNumber)) {
 
2840
        goto exit_function;
 
2841
    }
 
2842
 
 
2843
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
2844
    pStruct->at = at2;
 
2845
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
2846
    pStruct->at = at;
 
2847
    if ( ret2 < 0 ) {
 
2848
        ret = ret2;
 
2849
        goto exit_function;
 
2850
    }
 
2851
    for ( i = 0; i < num_at; i ++ ) {
 
2852
        if ( !pVA[i].cMetal || pVA[i].nMetalGroupEdge <= 0 ) {
 
2853
            continue;
 
2854
        }
 
2855
        peMeFlower = pBNS->edge + pVA[i].nMetalGroupEdge-1;
 
2856
        if ( vMeFlower0 != (peMeFlower->neighbor12 ^ i) ) {
 
2857
            ret = RI_ERR_PROGR;
 
2858
            goto exit_function;
 
2859
        }
 
2860
        pMeFlower = pBNS->vert + vMeFlower0;
 
2861
 
 
2862
        for ( j = 0; j < at2[i].valence; j ++ ) {
 
2863
            if ( !peMeFlower->flow ) {
 
2864
                break; /* cannot do anything */
 
2865
            }
 
2866
            if ( !(at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
 
2867
                /* found a zero order bond */
 
2868
                if ( !bFixedCarbonCharges ) {
 
2869
                    /* do not let carbon atoms get charged */
 
2870
                    if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
 
2871
                        goto exit_function;
 
2872
                    }
 
2873
                    bFixedCarbonCharges ++;
 
2874
                }
 
2875
                peZero = pBNS->edge + pBNS->vert[i].iedge[j];
 
2876
                if ( peZero->flow ) {
 
2877
                    ret = RI_ERR_PROGR;
 
2878
                    goto exit_function;
 
2879
                }
 
2880
                /* fix other edges */
 
2881
                for ( k = 0; k < at2[i].valence; k ++ ) {
 
2882
                    pe = pBNS->edge + pBNS->vert[i].iedge[k];
 
2883
                    if ( pe->flow == 1 && !(pe->forbidden & forbidden_edge_mask) ) {
 
2884
                        if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) {
 
2885
                            goto exit_function;
 
2886
                        }
 
2887
                        pe->forbidden |= forbidden_edge_mask;
 
2888
                    }
 
2889
                }
 
2890
                /* do not create =N(+)= in a ring or #O(+) terminal */
 
2891
                for ( k = 0; k < num_at; k ++ ) {
 
2892
                    if ( !pVA[k].cMetal && pVA[k].cNumValenceElectrons == 5 &&
 
2893
                         at2[k].valence == 2 && !at2[k].num_H && pVA[k].cMinRingSize <= 6 &&
 
2894
                         pVA[k].nCPlusGroupEdge > 0 &&
 
2895
                         (pe=pBNS->edge + pVA[k].nCPlusGroupEdge-1)->flow==1 &&
 
2896
                         !(pe->forbidden & forbidden_edge_mask)) {
 
2897
 
 
2898
                        if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) {
 
2899
                            goto exit_function;
 
2900
                        }
 
2901
                        pe->forbidden |= forbidden_edge_mask;
 
2902
                    }
 
2903
                }
 
2904
 
 
2905
                /* metal's neighbor connected by a zero-order bond */
 
2906
                pNeigh = pBNS->vert + (vNeigh = at2[i].neighbor[j]);
 
2907
                /*for ( k = 0; k < pNeigh->num_adj_edges; k ++ )*/
 
2908
                for ( k = pNeigh->num_adj_edges-1; 0 <= k; k -- )
 
2909
                {
 
2910
                    peNeighMeigh = pBNS->edge + pNeigh->iedge[k];
 
2911
                    if ( !peNeighMeigh->flow ) {
 
2912
                        continue;
 
2913
                    }
 
2914
                    vNeighMeigh = peNeighMeigh->neighbor12 ^ vNeigh;
 
2915
                    if ( vNeighMeigh != i && vNeighMeigh != vMeFlower0 ) {
 
2916
                        /* metal neighbor's neighbor connected by a not-zero-order bond */
 
2917
                        pNeighNeigh = pBNS->vert + vNeighMeigh;
 
2918
                        break; /* found */
 
2919
                    }
 
2920
                }
 
2921
                if ( k < 0 ) {
 
2922
                    continue; /* neighbor not found */
 
2923
                }
 
2924
                peZero->flow ++;
 
2925
                peZero->forbidden |= forbidden_edge_mask;
 
2926
                peMeFlower->flow --;
 
2927
                peNeighMeigh->flow --;
 
2928
                pMeFlower->st_edge.flow --;
 
2929
                pNeighNeigh->st_edge.flow --;
 
2930
                pBNS->tot_st_flow -= 2;
 
2931
                /* test */
 
2932
                bSuccess = 0;
 
2933
                ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
2934
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
2935
 
 
2936
                if ( ret == 1 && (vPathEnd == vMeFlower0 && vPathStart == vNeighMeigh ||
 
2937
                                  vPathEnd == vNeighMeigh && vPathStart == vMeFlower0) && abs(nDeltaCharge) <= 2 ) {
 
2938
                    ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
2939
                    if ( ret > 0 ) {
 
2940
                        (*pnNumRunBNS) ++;
 
2941
                        *pnTotalDelta += ret;
 
2942
                        num_changes ++;
 
2943
                        bSuccess = ret;
 
2944
                    }
 
2945
                    if ( ret = AddToEdgeList( &NewlyFixedEdges, peZero - pBNS->edge, FIX_BOND_ADD_ALLOC )) {
 
2946
                        goto exit_function;
 
2947
                    }
 
2948
                } else {
 
2949
                    peZero->flow --;
 
2950
                    peZero->forbidden &= inv_forbidden_edge_mask;
 
2951
                    peMeFlower->flow ++;
 
2952
                    peNeighMeigh->flow ++;
 
2953
                    pMeFlower->st_edge.flow ++;
 
2954
                    pNeighNeigh->st_edge.flow ++;
 
2955
                    pBNS->tot_st_flow += 2;
 
2956
                }
 
2957
                RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
 
2958
                NewlyFixedEdges.num_edges = 0;
 
2959
                if ( bSuccess ) {
 
2960
                    /* update at2[] */
 
2961
                    memcpy( at2, at, len_at*sizeof(at2[0]));
 
2962
                    pStruct->at = at2;
 
2963
                    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
2964
                    pStruct->at = at;
 
2965
                    if ( ret2 < 0 ) {
 
2966
                        ret = ret2;
 
2967
                        goto exit_function;
 
2968
                    }
 
2969
                }
 
2970
            }
 
2971
        }
 
2972
    }
 
2973
    ret = num_changes;
 
2974
 
 
2975
exit_function:
 
2976
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
2977
    RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
 
2978
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
2979
    AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE );
 
2980
    return ret;
 
2981
}
 
2982
/***********************************************************************
 
2983
        NH2                NH2
 
2984
           \                  \
 
2985
            C==S(+)-   =>      C(+)-S-   where NH2 are not tautomeric
 
2986
           /                  /
 
2987
        NH2                NH2
 
2988
************************************************************************/
 
2989
int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
2990
                    inp_ATOM *at, inp_ATOM *at2,
 
2991
                    VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
2992
                    int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
2993
{
 
2994
    int i, j, k, ret, ret2, cur_success;
 
2995
    int delta;
 
2996
    EdgeIndex        ePlusS, ePlusC, eMinusC, e;
 
2997
    BNS_VERTEX *pvS, *pvC, *pv1, *pv2;
 
2998
    BNS_EDGE   *pePlusS, *pePlusC, *pe1, *pe2, *peCN[3], *peSC, *pe;
 
2999
    Vertex     vC, vN;
 
3000
    
 
3001
    int num_at = pStruct->num_atoms;
 
3002
    int num_deleted_H = pStruct->num_deleted_H;
 
3003
    int len_at = num_at + num_deleted_H;
 
3004
    
 
3005
    Vertex     vPathStart, vPathEnd, v1, v2;
 
3006
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
3007
 
 
3008
    EDGE_LIST AllChargeEdges;
 
3009
    
 
3010
    ret = 0;
 
3011
    cur_success = 0;
 
3012
 
 
3013
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );
 
3014
 
 
3015
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
3016
    pStruct->at = at2;
 
3017
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
3018
    pStruct->at = at;
 
3019
    if ( ret2 < 0 ) {
 
3020
        ret = ret2;
 
3021
        goto exit_function;
 
3022
    }
 
3023
    /* find (NH2)C=S(+) */
 
3024
    for ( i = 0; i < num_at; i ++ ) {
 
3025
        if ( !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 6 &&
 
3026
             at2[i].valence == 2 && 
 
3027
             (pvS = pBNS->vert+i)->st_edge.cap == pvS->st_edge.flow &&
 
3028
             0 <= (ePlusS = pVA[i].nCPlusGroupEdge-1) && !(pePlusS=pBNS->edge+ePlusS)->flow && /* S(+) */
 
3029
             (pe1=pBNS->edge + pvS->iedge[0])->flow + 
 
3030
             (pe2=pBNS->edge + pvS->iedge[1])->flow == 1 /* -S(+)= */ &&
 
3031
             pVA[vC = (peSC=pe1->flow? pe1 : pe2)->neighbor12 ^ i].cNumValenceElectrons == 4 &&
 
3032
             at2[vC].valence == 3 &&
 
3033
             0 <= (ePlusC=pVA[vC].nCPlusGroupEdge-1) && (pePlusC=pBNS->edge+ePlusC)->flow &&
 
3034
             !(0 <= (eMinusC=pVA[vC].nCMinusGroupEdge-1) && pBNS->edge[eMinusC].flow ) ) {
 
3035
            /* found >C=S(+)- */
 
3036
            pvC = pBNS->vert + vC;
 
3037
            for ( j = k = 0; j < at[vC].valence; j ++ ) {
 
3038
                if ( peSC != (peCN[k] = pBNS->edge + pvC->iedge[j]) && !peCN[k]->flow ) {
 
3039
                    k ++; /* a single bond from C */
 
3040
                }
 
3041
            }
 
3042
            if ( k != 2 ) {
 
3043
                continue;
 
3044
            }
 
3045
            for ( j = 0; j < k; j ++ ) {
 
3046
                vN = peCN[j]->neighbor12 ^ vC;
 
3047
                if ( pVA[vN].cNumValenceElectrons != 5 ||
 
3048
                     pBNS->vert[vN].st_edge.cap != pBNS->vert[vN].st_edge.flow ||
 
3049
                     at2[vN].num_H != 2 ||
 
3050
                     at2[vN].endpoint || (pStruct->endpoint && pStruct->endpoint[vN]) ) {
 
3051
                    break; /* does not fit the pattern */
 
3052
                }
 
3053
            }
 
3054
            if ( j != k ) {
 
3055
                continue;
 
3056
            }
 
3057
            /* fix all charges */
 
3058
            if ( !AllChargeEdges.num_edges ) {
 
3059
                for ( j = 0; j < num_at; j ++ ) {
 
3060
                    if ( 0 <= (e = pVA[j].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden &&
 
3061
                         (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) {
 
3062
                        goto exit_function;
 
3063
                    }
 
3064
                    if ( 0 <= (e = pVA[j].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden &&
 
3065
                         (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) {
 
3066
                        goto exit_function;
 
3067
                    }
 
3068
                }
 
3069
            }
 
3070
            SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
3071
            pePlusS->forbidden &= ~forbidden_edge_mask;
 
3072
            pe   = pePlusC;
 
3073
            if ( !pe->flow )
 
3074
                continue;
 
3075
            delta = 1;
 
3076
            pv1 = pBNS->vert + (v1 = pe->neighbor1);
 
3077
            pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1);
 
3078
 
 
3079
            pe->flow -= delta;
 
3080
            pv1->st_edge.flow -= delta;
 
3081
            pv2->st_edge.flow -= delta;
 
3082
            pBNS->tot_st_flow -= 2*delta;
 
3083
 
 
3084
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3085
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3086
 
 
3087
            if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
3088
                              vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) {
 
3089
                /* Remover (+)charge from S => nDeltaCharge == -1 */
 
3090
                /* Flow change on pe (+)charge edge (atom S) is not known to RunBnsTestOnce()) */
 
3091
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3092
                if ( ret > 0 ) {
 
3093
                    (*pnNumRunBNS) ++;
 
3094
                    cur_success ++;
 
3095
                }
 
3096
            } else {
 
3097
                pe->flow += delta;
 
3098
                pv1->st_edge.flow += delta;
 
3099
                pv2->st_edge.flow += delta;
 
3100
                pBNS->tot_st_flow += 2*delta;
 
3101
            }
 
3102
            RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
3103
        }
 
3104
    }
 
3105
exit_function:
 
3106
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
 
3107
    return ret;
 
3108
}
 
3109
/******************************************************************************************************/
 
3110
int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
3111
                                            inp_ATOM *at, inp_ATOM *at2,
 
3112
                                            VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
3113
                                            int *pnNumRunBNS, int *pnTotalDelta,
 
3114
                                            int forbidden_edge_mask, int forbidden_stereo_edge_mask)
 
3115
    /********* Avoid charge separation on heteroatoms ******************/
 
3116
{
 
3117
    int i, j, k, ret, ret2, num_pos, num_neg, num_min=0, bFixedCarbonCharges;
 
3118
    int vPlusSuper;  /* (+)super vertex */
 
3119
    int ePlusSuper;  /* edge from vPlusSuper to (+/-) */
 
3120
    int vPlusMinus;  /* (+/-) vertex */
 
3121
    int nDeltaPlus1, nDeltaMinus1, delta;
 
3122
    BNS_VERTEX *pvPlusSuper, *pvPlusMinus;
 
3123
    BNS_EDGE   *pEdge;
 
3124
    
 
3125
    int num_at = pStruct->num_atoms;
 
3126
    int num_deleted_H = pStruct->num_deleted_H;
 
3127
    int len_at = num_at + num_deleted_H;
 
3128
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
3129
    
 
3130
    Vertex     vPathStart, vPathEnd;
 
3131
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
3132
 
 
3133
    EDGE_LIST FixedLargeRingStereoEdges, CarbonChargeEdges;
 
3134
    
 
3135
    ret = 0;
 
3136
 
 
3137
    AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR );
 
3138
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
 
3139
    bFixedCarbonCharges = 0;
 
3140
 
 
3141
    if ( forbidden_stereo_edge_mask ) {
 
3142
        for ( i = 0; i < num_at; i ++ ) {
 
3143
            for ( j = 0; j < at2[i].valence; j ++ ) {
 
3144
                if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) {
 
3145
                    int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q,
 
3146
                                                             pStruct->pbfsq->nAtomLevel,
 
3147
                                                             pStruct->pbfsq->cSource, 99 /* max ring size */ );
 
3148
                    if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, 64 ))) {
 
3149
                        goto exit_function;
 
3150
                    }
 
3151
                }
 
3152
            }
 
3153
        }
 
3154
        if ( !FixedLargeRingStereoEdges.num_edges ) {
 
3155
            goto exit_function;
 
3156
        } else {
 
3157
            /* allow stereobonds in rings change */
 
3158
            RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask );
 
3159
        }
 
3160
    }
 
3161
 
 
3162
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
3163
    pStruct->at = at2;
 
3164
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
3165
    pStruct->at = at;
 
3166
    if ( ret2 < 0 ) {
 
3167
        ret = ret2;
 
3168
        goto exit_function;
 
3169
    }
 
3170
    /* count charges */
 
3171
    num_pos = num_neg = 0;
 
3172
    for ( i = 0; i < num_at; i ++ ) {
 
3173
        if ( !pVA[i].cMetal && !at2[i].radical ) {
 
3174
            num_pos += ( at2[i].charge > 0 );
 
3175
            num_neg += ( at2[i].charge < 0 );
 
3176
        }
 
3177
    }
 
3178
    num_min = inchi_min( num_pos, num_neg );
 
3179
 
 
3180
 
 
3181
    if ( num_min && 
 
3182
         (k          = pTCGroups->nGroup[TCG_Plus]) >= 0 &&
 
3183
         (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
 
3184
         (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at &&
 
3185
         !(pEdge=pBNS->edge + ePlusSuper)->forbidden ) {
 
3186
 
 
3187
        vPlusMinus = pEdge->neighbor12 ^ vPlusSuper;
 
3188
        pvPlusSuper = pBNS->vert + vPlusSuper;
 
3189
        pvPlusMinus = pBNS->vert + vPlusMinus;
 
3190
        num_min     = inchi_min( num_min, pEdge->flow );
 
3191
        nDeltaPlus1  = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
 
3192
        nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow;
 
3193
        if ( num_min && (!nDeltaPlus1 && !nDeltaMinus1 ) ) {
 
3194
            if ( !bFixedCarbonCharges ) { /* 02-02-2006 */
 
3195
                /* do not let carbon atoms get charged */
 
3196
                if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
 
3197
                    goto exit_function;
 
3198
                }
 
3199
                bFixedCarbonCharges ++;
 
3200
            }
 
3201
            delta = 1;
 
3202
            pEdge->forbidden          |= forbidden_edge_mask;
 
3203
            pBNS->edge_forbidden_mask |= forbidden_edge_mask;
 
3204
            for ( i = 0; i < num_min; i += delta ) {
 
3205
                /* cancel 1 pair of charges at a time   */
 
3206
                /* an attempt to cancel all at once may */
 
3207
                /* convert a pair of N(IV)(+) into a pair of N(V) neutral with total charge reduced by 2 */
 
3208
                pEdge->flow               -= delta;
 
3209
                pvPlusSuper->st_edge.flow -= delta;
 
3210
                pvPlusMinus->st_edge.flow -= delta;
 
3211
                pBNS->tot_st_flow         -= 2*delta;
 
3212
                /* test for charhe cancellation */
 
3213
                ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3214
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3215
                if ( ret < 0 ) {
 
3216
                    goto exit_function;
 
3217
                }
 
3218
                if ( ret == 1 && (vPathEnd == vPlusSuper && vPathStart == vPlusMinus ||
 
3219
                                  vPathEnd == vPlusMinus && vPathStart == vPlusSuper) && nDeltaCharge < 0 ) {
 
3220
                    ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3221
                    (*pnNumRunBNS) ++;
 
3222
                    if ( ret < 0 ) {
 
3223
                        goto exit_function;
 
3224
                    } else
 
3225
                    if ( ret == 1 ) {
 
3226
                        *pnTotalDelta += ret;
 
3227
                    } else {
 
3228
                        ret = RI_ERR_PROGR;
 
3229
                        goto exit_function;
 
3230
                    }
 
3231
                } else {
 
3232
                    pEdge->flow               += delta;
 
3233
                    pvPlusSuper->st_edge.flow += delta;
 
3234
                    pvPlusMinus->st_edge.flow += delta;
 
3235
                    pBNS->tot_st_flow         += 2*delta;
 
3236
                    break;
 
3237
                }
 
3238
            }
 
3239
            num_min -= i; /* how many pairs of charges left */
 
3240
            pEdge->forbidden &= inv_forbidden_edge_mask;
 
3241
        }
 
3242
        nDeltaPlus1  = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
 
3243
        nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow;
 
3244
        if ( num_min > 1 && (!nDeltaPlus1 && !nDeltaMinus1 ) ) {
 
3245
            delta = 2;
 
3246
            pEdge->forbidden          |= forbidden_edge_mask;
 
3247
            pBNS->edge_forbidden_mask |= forbidden_edge_mask;
 
3248
            for ( i = 0; i < num_min; i += delta ) {
 
3249
                /* cancel 2 pairs of opposite charges at a time   */
 
3250
                /* 1. test cancellation of a pair of (+) charges */
 
3251
                pvPlusSuper->st_edge.cap += delta;
 
3252
                pBNS->tot_st_cap         += delta;
 
3253
                ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3254
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3255
                if ( ret < 0 ) {
 
3256
                    goto exit_function;
 
3257
                }
 
3258
                pvPlusSuper->st_edge.cap -= delta;
 
3259
                pBNS->tot_st_cap         -= delta;
 
3260
                if ( ret != 1 || (vPathEnd != vPlusSuper || vPathStart != vPlusSuper) || nDeltaCharge >= 0 ) {
 
3261
                    break;
 
3262
                }
 
3263
                /* 2. test cancellation of a pair of (-) charges */
 
3264
                pvPlusMinus->st_edge.cap += delta;
 
3265
                pBNS->tot_st_cap         += delta;
 
3266
                ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3267
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3268
                if ( ret < 0 ) {
 
3269
                    goto exit_function;
 
3270
                }
 
3271
                pvPlusMinus->st_edge.cap -= delta;
 
3272
                pBNS->tot_st_cap         -= delta;
 
3273
                if ( ret != 1 || (vPathEnd != vPlusMinus || vPathStart != vPlusMinus) || nDeltaCharge >= 0 ) {
 
3274
                    break;
 
3275
                }
 
3276
                /* 3. Actually cancel the pair of charges */
 
3277
                pEdge->flow               -= delta;
 
3278
                pvPlusSuper->st_edge.flow -= delta;
 
3279
                pvPlusMinus->st_edge.flow -= delta;
 
3280
                pBNS->tot_st_flow         -= 2*delta;
 
3281
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3282
                (*pnNumRunBNS) ++;
 
3283
                if ( ret < 0 ) {
 
3284
                    goto exit_function;
 
3285
                } else
 
3286
                if ( ret == 2 ) {
 
3287
                    *pnTotalDelta += ret;
 
3288
                } else {
 
3289
                    ret = RI_ERR_PROGR;
 
3290
                    goto exit_function;
 
3291
                }
 
3292
            }
 
3293
            num_min -= i; /* how many pairs of charges left */
 
3294
            pEdge->forbidden &= inv_forbidden_edge_mask;
 
3295
        }
 
3296
    }
 
3297
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
3298
    pStruct->at = at;
 
3299
exit_function:
 
3300
    if ( bFixedCarbonCharges ) {
 
3301
        RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, forbidden_edge_mask ); 
 
3302
        AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
3303
    }
 
3304
    if ( forbidden_stereo_edge_mask && FixedLargeRingStereoEdges.num_edges ) {
 
3305
        SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask );
 
3306
    }
 
3307
    AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE );
 
3308
 
 
3309
    return ret < 0? ret : num_min;
 
3310
}
 
3311
#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 )
 
3312
/********************** not used *************************************************************************/
 
3313
int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
3314
                                       inp_ATOM *at, inp_ATOM *at2,
 
3315
                                       VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
3316
                                       int *pnNumRunBNS, int *pnTotalDelta,
 
3317
                                       int forbidden_edge_mask)
 
3318
    /********* Avoid charge separation on heteroatoms ******************/
 
3319
{
 
3320
    int i, k, ret, ret2, num_pos, num_neg, num_min;
 
3321
    int vPlusSuper, vMinusSuper;  /* (+), (-) super vertices */
 
3322
    int ePlusSuper, eMinusSuper;  /* edges from vPlusSuper or vMinusSuper to (+/-) */
 
3323
    int vPlMn;  /* (+/-) vertex */
 
3324
    int vPlusHeteroat, vMinusHeteroat; /* (+), (-) heteroatom vertices */
 
3325
    int ePlusHeteroat, eMinusHeteroat; /* edges from (+) or (-) heteroatom vertex to super (+) or (-) */
 
3326
    int vPlusCarbons,  vMinusCarbons; /* (+), (-) carbons vertices */
 
3327
    int ePlusCarbons,  eMinusCarbons; /* edges from (+), (-) carbons vertices to super (+) or (-) */
 
3328
    int vPlusMetals,   vMinusMetals; /* (+), (-) carbons vertices */
 
3329
    int ePlusMetals,   eMinusMetals; /* edges from (+), (-) carbons vertices to super (+) or (-) */
 
3330
    int eMinusHeteroToSuper;         /* edge (-)vHetero-[eMinusHeteroat]-Y-[eMinusHeteroToSuper]-(-)vPlusSuper */
 
3331
    int v1, v2;
 
3332
    int nDeltaPlus1, nDeltaMinus1, delta;
 
3333
    BNS_VERTEX *pvPlusSuper, *pvMinusSuper, *pvPlMn;
 
3334
    BNS_VERTEX *pvPlusHeteroat, *pvMinusHeteroat, *pvPlusCarbons, *pvMinusCarbons;
 
3335
    BNS_VERTEX *pvPlusMetals,  *pvMinusMetals;
 
3336
    BNS_EDGE   *pEdgePlusHeteroat, *pEdgeMinusHeteroat, *pEdgeMinusHeteroToSuper;
 
3337
    BNS_EDGE   *pEdgePlusCarbons, *pEdgeMinusCarbons, *pEdgePlusMetals, *pEdgeMinusMetals;
 
3338
    BNS_EDGE   *pEdgePlusSuper, *pEdgeMinusSuper;
 
3339
    
 
3340
    int num_at = pStruct->num_atoms;
 
3341
    int num_deleted_H = pStruct->num_deleted_H;
 
3342
    int len_at = num_at + num_deleted_H;
 
3343
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
3344
    
 
3345
    Vertex     vPathStart, vPathEnd;
 
3346
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
3347
 
 
3348
 
 
3349
    ret = 0;
 
3350
 
 
3351
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
3352
    pStruct->at = at2;
 
3353
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
3354
    pStruct->at = at;
 
3355
    if ( ret2 < 0 ) {
 
3356
        ret = ret2;
 
3357
        goto exit_function;
 
3358
    }
 
3359
    /* (+) */
 
3360
    pEdgePlusSuper = NULL;
 
3361
    pvPlusSuper    = NULL;
 
3362
    if ( (k          = pTCGroups->nGroup[TCG_Plus]) >= 0 &&
 
3363
         (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
 
3364
         (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3365
         pEdgePlusSuper = pBNS->edge + ePlusSuper;
 
3366
         pvPlusSuper    = pBNS->vert + vPlusSuper;
 
3367
    }
 
3368
    pEdgePlusCarbons = NULL;
 
3369
    pvPlusCarbons    = NULL;
 
3370
    if ( (k            = pTCGroups->nGroup[TCG_Plus_C0] )  > 0 &&
 
3371
         (ePlusCarbons = pTCGroups->pTCG[k].nForwardEdge)  > 0 &&
 
3372
         (vPlusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3373
        pEdgePlusCarbons = pBNS->edge + ePlusCarbons;
 
3374
        pvPlusCarbons    = pBNS->vert + vPlusCarbons;
 
3375
    }
 
3376
    pEdgePlusHeteroat = NULL;
 
3377
    pvPlusHeteroat    = NULL;
 
3378
    if ( (k             = pTCGroups->nGroup[TCG_Plus0] )  > 0 &&
 
3379
         (ePlusHeteroat = pTCGroups->pTCG[k].nForwardEdge)  > 0 &&
 
3380
         (vPlusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3381
        pEdgePlusHeteroat = pBNS->edge + ePlusHeteroat;
 
3382
        pvPlusHeteroat     = pBNS->vert + vPlusHeteroat;
 
3383
    }
 
3384
    pEdgePlusMetals = NULL;
 
3385
    pvPlusMetals    = NULL;
 
3386
    if ( (k             = pTCGroups->nGroup[TCG_Plus_M0] )  > 0 &&
 
3387
         (ePlusMetals = pTCGroups->pTCG[k].nForwardEdge)  > 0 &&
 
3388
         (vPlusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3389
        pEdgePlusMetals = pBNS->edge + ePlusMetals;
 
3390
        pvPlusMetals    = pBNS->vert + vPlusMetals;
 
3391
    }
 
3392
    /* (-) */
 
3393
    pEdgeMinusSuper = NULL;
 
3394
    pvMinusSuper    = NULL;
 
3395
    if ( (k          = pTCGroups->nGroup[TCG_Minus]) >= 0 &&
 
3396
         (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
 
3397
         (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3398
         pEdgeMinusSuper = pBNS->edge + eMinusSuper;
 
3399
         pvMinusSuper    = pBNS->vert + vMinusSuper;
 
3400
    }
 
3401
    pEdgeMinusCarbons = NULL;
 
3402
    pvMinusCarbons    = NULL;
 
3403
    if ( (k            = pTCGroups->nGroup[TCG_Minus_C0] )  > 0 &&
 
3404
         (eMinusCarbons = pTCGroups->pTCG[k].nForwardEdge)  > 0 &&
 
3405
         (vMinusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3406
        pEdgeMinusCarbons = pBNS->edge + eMinusCarbons;
 
3407
        pvMinusCarbons    = pBNS->vert + vMinusCarbons;
 
3408
    }
 
3409
    pEdgeMinusHeteroat = NULL;
 
3410
    pvMinusHeteroat    = NULL;
 
3411
    pEdgeMinusHeteroToSuper = NULL;
 
3412
    if ( (k             = pTCGroups->nGroup[TCG_Minus0] )  > 0 &&
 
3413
         (eMinusHeteroat = pTCGroups->pTCG[k].nForwardEdge)  > 0 &&
 
3414
         (vMinusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3415
        BNS_VERTEX *pvYMinusHetero;
 
3416
        BNS_EDGE   *pe;
 
3417
        int         vYMinusHetero;
 
3418
        pEdgeMinusHeteroat = pBNS->edge + eMinusHeteroat;
 
3419
        pvMinusHeteroat    = pBNS->vert + vMinusHeteroat;
 
3420
        /* next edge toward (-)super */
 
3421
        if ( pvMinusSuper ) {
 
3422
            vYMinusHetero = pEdgeMinusHeteroat->neighbor12 ^ vMinusHeteroat;
 
3423
            pvYMinusHetero = pBNS->vert + vYMinusHetero;
 
3424
            for ( i = 0; i < pvYMinusHetero->num_adj_edges; i ++ ) {
 
3425
                pe = pBNS->edge + pvYMinusHetero->iedge[i];
 
3426
                if ( (pe->neighbor12 ^ vYMinusHetero) == vMinusSuper ) {
 
3427
                    pEdgeMinusHeteroToSuper = pe;
 
3428
                    eMinusHeteroToSuper     = pe - pBNS->edge;
 
3429
                    break;
 
3430
                }
 
3431
            }
 
3432
        }
 
3433
    }
 
3434
    pEdgeMinusMetals = NULL;
 
3435
    pvMinusMetals    = NULL;
 
3436
    if ( (k             = pTCGroups->nGroup[TCG_Minus_M0] )  > 0 &&
 
3437
         (eMinusMetals = pTCGroups->pTCG[k].nForwardEdge)  > 0 &&
 
3438
         (vMinusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
 
3439
        pEdgeMinusMetals = pBNS->edge + eMinusMetals;
 
3440
        pvMinusMetals    = pBNS->vert + vMinusMetals;
 
3441
    }
 
3442
    /* (+/-) */
 
3443
    pvPlMn = NULL;
 
3444
    if ( pEdgePlusSuper ) {
 
3445
        vPlMn = pEdgePlusSuper->neighbor12 ^ vPlusSuper;
 
3446
        pvPlMn = pBNS->vert + vPlMn;
 
3447
    } else
 
3448
    if ( pEdgeMinusSuper ) {
 
3449
        vPlMn = pEdgeMinusSuper->neighbor12 ^ vMinusSuper;
 
3450
        pvPlMn = pBNS->vert + vPlMn;
 
3451
    }
 
3452
    num_pos = num_neg = 0;
 
3453
    /***************************************************************/
 
3454
    /* Positive Charges                                            */
 
3455
    /***************************************************************/
 
3456
    if ( pEdgePlusHeteroat && pEdgePlusMetals ) {
 
3457
        /* count charges */
 
3458
        for ( i = 0; i < num_at; i ++ ) {
 
3459
            if ( !at2[i].radical &&
 
3460
                  at2[i].charge > 0 && 
 
3461
                  (k = pVA[i].nCPlusGroupEdge-1) >= 0 ) {
 
3462
                v1 = pBNS->edge[k].neighbor1;
 
3463
                v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12;
 
3464
                if ( v1 == vPlusHeteroat || v2 == vPlusHeteroat ) {
 
3465
                    num_pos ++;
 
3466
                }
 
3467
            }
 
3468
        }
 
3469
        /* attempt to move (+) from heteroatoms to metal atoms */
 
3470
        num_min = inchi_min( num_pos, pEdgePlusHeteroat->flow );
 
3471
 
 
3472
        nDeltaPlus1  = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
 
3473
        nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow;
 
3474
        if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) {
 
3475
            if ( pEdgePlusSuper ) {
 
3476
                pEdgePlusSuper->forbidden |= forbidden_edge_mask;
 
3477
            }
 
3478
            if ( pEdgeMinusSuper ) {
 
3479
                pEdgeMinusSuper->forbidden |= forbidden_edge_mask;
 
3480
            }
 
3481
            if ( pEdgePlusCarbons ) {
 
3482
                pEdgePlusCarbons->forbidden |= forbidden_edge_mask;
 
3483
            }
 
3484
            if ( pEdgeMinusCarbons ) {
 
3485
                pEdgeMinusCarbons->forbidden |= forbidden_edge_mask;
 
3486
            }
 
3487
            if ( pEdgePlusHeteroat ) {
 
3488
                pEdgePlusHeteroat->forbidden |= forbidden_edge_mask;
 
3489
            }
 
3490
            if ( pEdgeMinusHeteroat ) {
 
3491
                pEdgeMinusHeteroat->forbidden |= forbidden_edge_mask;
 
3492
            }
 
3493
            delta = 1;
 
3494
            for ( i = 0; i < num_min; i += delta ) {
 
3495
                v1 = pEdgePlusHeteroat->neighbor1;
 
3496
                v2 = pEdgePlusHeteroat->neighbor12 ^ v1;
 
3497
                pEdgePlusHeteroat->flow     -= delta;
 
3498
                pBNS->vert[v1].st_edge.flow -= delta;
 
3499
                pBNS->vert[v2].st_edge.flow -= delta;
 
3500
                pBNS->tot_st_flow           -= 2*delta;
 
3501
                /* test for charhe cancellation */
 
3502
                ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3503
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3504
                if ( ret < 0 ) {
 
3505
                    goto exit_function;
 
3506
                }
 
3507
                if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
3508
                                  vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
 
3509
                    ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3510
                    (*pnNumRunBNS) ++;
 
3511
                    if ( ret < 0 ) {
 
3512
                        goto exit_function;
 
3513
                    } else
 
3514
                    if ( ret == 1 ) {
 
3515
                        *pnTotalDelta += ret;
 
3516
                    } else {
 
3517
                        ret = RI_ERR_PROGR;
 
3518
                        goto exit_function;
 
3519
                    }
 
3520
                } else {
 
3521
                    pEdgePlusHeteroat->flow     += delta;
 
3522
                    pBNS->vert[v1].st_edge.flow += delta;
 
3523
                    pBNS->vert[v2].st_edge.flow += delta;
 
3524
                    pBNS->tot_st_flow           += 2*delta;
 
3525
                    break;
 
3526
                }
 
3527
            }
 
3528
            if ( pEdgePlusSuper ) {
 
3529
                pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask;
 
3530
            }
 
3531
            if ( pEdgeMinusSuper ) {
 
3532
                pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask;
 
3533
            }
 
3534
            if ( pEdgePlusCarbons ) {
 
3535
                pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask;
 
3536
            }
 
3537
            if ( pEdgeMinusCarbons ) {
 
3538
                pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask;
 
3539
            }
 
3540
            if ( pEdgePlusHeteroat ) {
 
3541
                pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask;
 
3542
            }
 
3543
            if ( pEdgeMinusHeteroat ) {
 
3544
                pEdgeMinusHeteroat->forbidden &= inv_forbidden_edge_mask;
 
3545
            }
 
3546
        }
 
3547
    }
 
3548
    /***************************************************************/
 
3549
    /* Negative Charges                                            */
 
3550
    /***************************************************************/
 
3551
    if ( pEdgeMinusHeteroToSuper && pEdgeMinusMetals ) {
 
3552
        /* count charges */
 
3553
        for ( i = 0; i < num_at; i ++ ) {
 
3554
            if ( !at2[i].radical &&
 
3555
                  at2[i].charge < 0 && 
 
3556
                  (k = pVA[i].nCMinusGroupEdge-1) >= 0 ) {
 
3557
                v1 = pBNS->edge[k].neighbor1;
 
3558
                v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12;
 
3559
                if ( v1 == vMinusHeteroat || v2 == vMinusHeteroat ) {
 
3560
                    num_neg ++;
 
3561
                }
 
3562
            }
 
3563
        }
 
3564
        if ( num_neg ) {
 
3565
            /* attempt to move (+) from heteroatoms to metal atoms */
 
3566
            num_min = inchi_min( num_neg, pEdgeMinusHeteroToSuper->flow );
 
3567
        }
 
3568
 
 
3569
        nDeltaPlus1  = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
 
3570
        nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow;
 
3571
        if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) {
 
3572
            if ( pEdgePlusSuper ) {
 
3573
                pEdgePlusSuper->forbidden |= forbidden_edge_mask;
 
3574
            }
 
3575
            if ( pEdgeMinusSuper ) {
 
3576
                pEdgeMinusSuper->forbidden |= forbidden_edge_mask;
 
3577
            }
 
3578
            if ( pEdgePlusCarbons ) {
 
3579
                pEdgePlusCarbons->forbidden |= forbidden_edge_mask;
 
3580
            }
 
3581
            if ( pEdgeMinusCarbons ) {
 
3582
                pEdgeMinusCarbons->forbidden |= forbidden_edge_mask;
 
3583
            }
 
3584
            if ( pEdgePlusHeteroat ) {
 
3585
                pEdgePlusHeteroat->forbidden |= forbidden_edge_mask;
 
3586
            }
 
3587
            if ( pEdgeMinusHeteroToSuper ) {
 
3588
                pEdgeMinusHeteroToSuper->forbidden |= forbidden_edge_mask;
 
3589
            }
 
3590
            delta = 1;
 
3591
            for ( i = 0; i < num_min; i += delta ) {
 
3592
                v1 = pEdgeMinusHeteroToSuper->neighbor1;
 
3593
                v2 = pEdgeMinusHeteroToSuper->neighbor12 ^ v1;
 
3594
                pEdgeMinusHeteroToSuper->flow -= delta;
 
3595
                pBNS->vert[v1].st_edge.flow   -= delta;
 
3596
                pBNS->vert[v2].st_edge.flow   -= delta;
 
3597
                pBNS->tot_st_flow             -= 2*delta;
 
3598
                /* test for charhe cancellation */
 
3599
                ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3600
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3601
                if ( ret < 0 ) {
 
3602
                    goto exit_function;
 
3603
                }
 
3604
                if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
3605
                                  vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
 
3606
                    ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3607
                    (*pnNumRunBNS) ++;
 
3608
                    if ( ret < 0 ) {
 
3609
                        goto exit_function;
 
3610
                    } else
 
3611
                    if ( ret == 1 ) {
 
3612
                        *pnTotalDelta += ret;
 
3613
                    } else {
 
3614
                        ret = RI_ERR_PROGR;
 
3615
                        goto exit_function;
 
3616
                    }
 
3617
                } else {
 
3618
                    pEdgeMinusHeteroToSuper->flow += delta;
 
3619
                    pBNS->vert[v1].st_edge.flow   += delta;
 
3620
                    pBNS->vert[v2].st_edge.flow   += delta;
 
3621
                    pBNS->tot_st_flow             += 2*delta;
 
3622
                    break;
 
3623
                }
 
3624
            }
 
3625
            if ( pEdgePlusSuper ) {
 
3626
                pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask;
 
3627
            }
 
3628
            if ( pEdgeMinusSuper ) {
 
3629
                pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask;
 
3630
            }
 
3631
            if ( pEdgePlusCarbons ) {
 
3632
                pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask;
 
3633
            }
 
3634
            if ( pEdgeMinusCarbons ) {
 
3635
                pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask;
 
3636
            }
 
3637
            if ( pEdgePlusHeteroat ) {
 
3638
                pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask;
 
3639
            }
 
3640
            if ( pEdgeMinusHeteroToSuper ) {
 
3641
                pEdgeMinusHeteroToSuper->forbidden &= inv_forbidden_edge_mask;
 
3642
            }
 
3643
        }
 
3644
    }
 
3645
exit_function:
 
3646
    return ret;
 
3647
}
 
3648
#endif
 
3649
/******************************************************************************************************/
 
3650
int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
3651
                     inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
3652
                     int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
3653
{
 
3654
    Vertex     vPathStart, vPathEnd;
 
3655
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
3656
    BNS_EDGE  *pe;
 
3657
    
 
3658
    int i, j, ret2, ret;
 
3659
    int num_at = pStruct->num_atoms;
 
3660
    int num_deleted_H = pStruct->num_deleted_H;
 
3661
    int len_at = num_at + num_deleted_H;
 
3662
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
3663
    Vertex v1, v2;
 
3664
 
 
3665
    EDGE_LIST CarbonChargeEdges;
 
3666
    
 
3667
    ret = 0;
 
3668
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
 
3669
 
 
3670
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
3671
    pStruct->at = at2;
 
3672
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
3673
    pStruct->at = at;
 
3674
    if ( ret2 < 0 ) {
 
3675
        ret = ret2;
 
3676
        goto exit_function;
 
3677
    }
 
3678
 
 
3679
    for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
 
3680
        if ( at2[i].valence == 1 &&
 
3681
             at2[i].num_H   == 0 &&
 
3682
             at2[i].chem_bonds_valence == 2 &&
 
3683
             at2[i].charge == -1 &&
 
3684
             at2[i].radical == 0 &&
 
3685
             pVA[i].cNumValenceElectrons == 5 &&  /* terminal N(-)=, P, As, Sb, Bi */
 
3686
             pVA[i].nCMinusGroupEdge > 0 &&
 
3687
             pVA[i].nTautGroupEdge == 0 &&
 
3688
             at2[j=at2[i].neighbor[0]].valence == 2 &&
 
3689
             at2[j].num_H == 0 &&
 
3690
             at2[j].chem_bonds_valence == 4 &&
 
3691
             at2[j].charge == 0 &&
 
3692
             at2[j].radical == 0 &&
 
3693
             pVA[j].cNumValenceElectrons == 4 && /* C or Si or Ge or Sn or Pb */
 
3694
             pVA[i].cnListIndex > 0 &&
 
3695
             cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
 
3696
            /* found N(-)=C= */
 
3697
            pe = pBNS->edge + (pVA[i].nCMinusGroupEdge-1); /* N#N(+) triple bond edge */
 
3698
            
 
3699
            if ( !pe->flow ) {
 
3700
                continue; /* wrong atom ??? Strange... */
 
3701
            }
 
3702
            v1 = pe->neighbor1;
 
3703
            v2 = pe->neighbor12 ^ v1;
 
3704
            pe->flow --;
 
3705
            pBNS->vert[v1].st_edge.flow --;
 
3706
            pBNS->vert[v2].st_edge.flow --;
 
3707
            pBNS->tot_st_flow -= 2;
 
3708
            pe->forbidden |= forbidden_edge_mask;
 
3709
 
 
3710
            /* do not let carbon atoms get charged */
 
3711
            if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
 
3712
                goto exit_function;
 
3713
            }
 
3714
            
 
3715
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3716
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3717
 
 
3718
            if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
3719
                              vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) {
 
3720
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3721
                (*pnNumRunBNS) ++;
 
3722
                *pnTotalDelta += ret;
 
3723
            } else {
 
3724
                pe->flow ++;
 
3725
                pBNS->vert[v1].st_edge.flow ++;
 
3726
                pBNS->vert[v2].st_edge.flow ++;
 
3727
                pBNS->tot_st_flow += 2;
 
3728
            }
 
3729
            RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
3730
            
 
3731
            pe->forbidden &= inv_forbidden_edge_mask; /* unmask the edges */
 
3732
        }
 
3733
    }
 
3734
 
 
3735
exit_function:
 
3736
 
 
3737
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
3738
    return ret;
 
3739
}
 
3740
/******************************************************************************************************/
 
3741
int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
3742
                     inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
3743
                     int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
3744
{
 
3745
#define INC_EDGE_LIST 16
 
3746
    Vertex     vPathStart, vPathEnd;
 
3747
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success;
 
3748
    BNS_EDGE  *pe;
 
3749
    Vertex    v1, v2;
 
3750
    
 
3751
    int i, j, ret2, ret, bIsCarbon;
 
3752
    int num_at = pStruct->num_atoms;
 
3753
    int num_deleted_H = pStruct->num_deleted_H;
 
3754
    int len_at = num_at + num_deleted_H;
 
3755
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
3756
    EdgeIndex eNMinusEdge, eNPlusEdge, eNPlusEdge1, eN34Edge;
 
3757
    EdgeIndex eNFlowerEdge1;
 
3758
 
 
3759
    EDGE_LIST CarbonChargeEdges, AllChargeEdges, IsoCyanoCarbonChargeEdges;
 
3760
 
 
3761
    ret = 0;
 
3762
    num_failed = num_success = 0;
 
3763
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* carbon charge edges */
 
3764
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );    /* heteroatom charge edges */
 
3765
    AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_CLEAR );   /* C in C(+)#N(+) charge edges */
 
3766
 
 
3767
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
3768
    pStruct->at = at2;
 
3769
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
3770
    pStruct->at = at;
 
3771
    if ( ret2 < 0 ) {
 
3772
        ret = ret2;
 
3773
        goto exit_function;
 
3774
    }
 
3775
    /* 1st attempt: take care of C(+)#N(+)-  => C(-)#N(+)- and remove 2 negative charges */
 
3776
    /* This would produce nDeltaCharge = 2 */
 
3777
    AllocEdgeList( &CarbonChargeEdges, 2*num_at );
 
3778
    for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
 
3779
        /* accumulate edges for subsequent fixing them */
 
3780
        bIsCarbon = (pVA[i].cNumValenceElectrons == 4 && pVA[i].cPeriodicRowNumber == 1);
 
3781
        eNFlowerEdge1 = NO_VERTEX;
 
3782
        if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 &&
 
3783
             !pBNS->edge[eNMinusEdge].forbidden ) {
 
3784
            if ( bIsCarbon ) {
 
3785
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
3786
                    goto exit_function;
 
3787
                }
 
3788
            } else
 
3789
            if ( !pVA[i].cMetal && !at2[i].endpoint && at2[i].charge != -1 ) {
 
3790
                if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
3791
                    goto exit_function;
 
3792
                }
 
3793
            }
 
3794
        }
 
3795
        if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 &&
 
3796
             !pBNS->edge[eNPlusEdge].forbidden ) {
 
3797
            if ( bIsCarbon ) {
 
3798
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
 
3799
                    goto exit_function;
 
3800
                }
 
3801
            } else
 
3802
            if ( !pVA[i].cMetal && !at2[i].endpoint  ) {
 
3803
                if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
 
3804
                    goto exit_function;
 
3805
                }
 
3806
                if ( pVA[i].cNumValenceElectrons == 5 &&
 
3807
                     NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) &&
 
3808
                     !pBNS->edge[eNFlowerEdge1].flow ) {
 
3809
                    if ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) {
 
3810
                        goto exit_function;
 
3811
                    }
 
3812
                }
 
3813
            }
 
3814
        }
 
3815
        if ( bIsCarbon &&
 
3816
             0 <= eNMinusEdge &&
 
3817
             0 <= eNPlusEdge &&
 
3818
             at2[i].valence == 1 &&
 
3819
             at2[i].num_H   == 0 &&
 
3820
             at2[i].radical   == 0 &&
 
3821
             !pBNS->edge[eNMinusEdge].forbidden &&
 
3822
             pBNS->edge[eNMinusEdge].flow == 0 &&
 
3823
             !pBNS->edge[eNPlusEdge].forbidden &&
 
3824
             pBNS->edge[eNPlusEdge].flow == 0 &&   /* found terminal C(+) */
 
3825
             
 
3826
             at2[j=at2[i].neighbor[0]].valence == 2 &&
 
3827
             at2[j].num_H == 0 &&
 
3828
             at2[j].radical == 0 &&
 
3829
             pVA[j].cNumValenceElectrons == 5 && 
 
3830
             (eNPlusEdge1  = pVA[j].nCPlusGroupEdge - 1)>= 0 &&
 
3831
             pBNS->edge[eNPlusEdge].flow == 0  ) {     /* -N(+)- */
 
3832
 
 
3833
#ifdef NEVER /* I have not found a good reason to do this yet */
 
3834
            /* fix (+) charge on -N(+)- as much as C charges are fixed */
 
3835
            if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
 
3836
                goto exit_function;
 
3837
            }
 
3838
            /* fix floer edge to prevent N(V) ??? */
 
3839
            if ( NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 )) &&
 
3840
                 !pBNS->edge[eNFlowerEdge1].flow ) {
 
3841
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) {
 
3842
                    goto exit_function;
 
3843
                }
 
3844
            }
 
3845
#endif
 
3846
            /*
 
3847
               Carbon(+)         Carbon(-) 
 
3848
               ChargeStruct:     ChargeStruct:
 
3849
                                 
 
3850
                         5(+C)             5(+C)
 
3851
                        /                 //    
 
3852
               6(-C)  4          6(-C)  4
 
3853
                   \ //             \\ /        
 
3854
                    3                 3         
 
3855
                    |                 |         
 
3856
                    2                 2         
 
3857
                    ||                || 
 
3858
                   -C1-              -C1-       
 
3859
                    |                 |
 
3860
 
 
3861
             3-6 is (-) Charge Edge; 4-5 is (+) Charge Edge
 
3862
 
 
3863
             To convert the left pattern to the right one:
 
3864
 
 
3865
             We need to release these charge edges and decrement
 
3866
             edge 3-4 flow to change charge from (+) to (-)
 
3867
            
 
3868
            */
 
3869
 
 
3870
            /* find vertices 4 and 5 */
 
3871
            v1 = pBNS->edge[eNPlusEdge].neighbor1; /* one of two vertices incident with edge 4-5 */
 
3872
            v2 = pBNS->edge[eNPlusEdge].neighbor12 ^ v1;
 
3873
            if ( IS_BNS_VT_C_GR(pBNS->vert[v1].type) ) {
 
3874
                /* v1 is 5(+C) */
 
3875
                Vertex tmp = v1;
 
3876
                v1 = v2;
 
3877
                v2 = tmp;
 
3878
            }
 
3879
            /* v1 should be 4, v2 - 5(+C) */
 
3880
            if ( !IS_BNS_VT_CHRG_STRUCT(pBNS->vert[v1].type) || pBNS->vert[v1].num_adj_edges != 2 ) {
 
3881
                continue; /* mismatch */
 
3882
            }
 
3883
            /* find edge 3-4 */
 
3884
            eN34Edge = pBNS->vert[v1].iedge[pBNS->vert[v1].iedge[0] == eNPlusEdge];
 
3885
            if ( pBNS->edge[eN34Edge].forbidden || !pBNS->edge[eN34Edge].flow ) {
 
3886
                continue;
 
3887
            }
 
3888
            /* save 3 edges: 6-3, 4-5, and 3-4 in this order */
 
3889
            if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
3890
                goto exit_function;
 
3891
            }
 
3892
            if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
 
3893
                goto exit_function;
 
3894
            }
 
3895
            if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eN34Edge, INC_EDGE_LIST ) ) {
 
3896
                goto exit_function;
 
3897
            }
 
3898
        }
 
3899
    }
 
3900
    /* 1st attempt: move (-) charges from heteroatoms to C(+) */
 
3901
    SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
3902
    SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
3903
    RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask );
 
3904
    for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) {
 
3905
        eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i];
 
3906
        eNPlusEdge  = IsoCyanoCarbonChargeEdges.pnEdges[i+1];
 
3907
        eN34Edge    = IsoCyanoCarbonChargeEdges.pnEdges[i+2];
 
3908
        
 
3909
        pe = pBNS->edge + eN34Edge;
 
3910
        pe->forbidden |= forbidden_edge_mask;
 
3911
        if ( !pe->flow ) {
 
3912
            continue; /* already done */
 
3913
        }
 
3914
 
 
3915
        v1 = pe->neighbor1;
 
3916
        v2 = pe->neighbor12 ^ v1;
 
3917
        pe->flow --;
 
3918
        pBNS->vert[v1].st_edge.flow --;
 
3919
        pBNS->vert[v2].st_edge.flow --;
 
3920
        pBNS->tot_st_flow -= 2;
 
3921
        
 
3922
        ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3923
                              &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3924
 
 
3925
        if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
3926
                          vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= -2 ) {
 
3927
            ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3928
            (*pnNumRunBNS) ++;
 
3929
            *pnTotalDelta += ret;
 
3930
            num_success ++;
 
3931
        } else {
 
3932
            pe->flow ++;
 
3933
            pBNS->vert[v1].st_edge.flow ++;
 
3934
            pBNS->vert[v2].st_edge.flow ++;
 
3935
            pBNS->tot_st_flow += 2;
 
3936
            pe->forbidden &= inv_forbidden_edge_mask;
 
3937
            num_failed ++;
 
3938
        }
 
3939
    }
 
3940
    if ( num_failed ) {
 
3941
        /* relax conditions: allow all heteroatoms to change charge */
 
3942
        RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
3943
        for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) {
 
3944
            eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i];
 
3945
            eNPlusEdge  = IsoCyanoCarbonChargeEdges.pnEdges[i+1];
 
3946
            eN34Edge    = IsoCyanoCarbonChargeEdges.pnEdges[i+2];
 
3947
            
 
3948
            pe = pBNS->edge + eN34Edge;
 
3949
            pe->forbidden |= forbidden_edge_mask;
 
3950
            if ( !pe->flow ) {
 
3951
                continue; /* already done */
 
3952
            }
 
3953
 
 
3954
            v1 = pe->neighbor1;
 
3955
            v2 = pe->neighbor12 ^ v1;
 
3956
            pe->flow --;
 
3957
            pBNS->vert[v1].st_edge.flow --;
 
3958
            pBNS->vert[v2].st_edge.flow --;
 
3959
            pBNS->tot_st_flow -= 2;
 
3960
            
 
3961
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
3962
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
3963
 
 
3964
            if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
3965
                              vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 2 ) {
 
3966
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
3967
                (*pnNumRunBNS) ++;
 
3968
                *pnTotalDelta += ret;
 
3969
                num_success ++;
 
3970
            } else {
 
3971
                pe->flow ++;
 
3972
                pBNS->vert[v1].st_edge.flow ++;
 
3973
                pBNS->vert[v2].st_edge.flow ++;
 
3974
                pBNS->tot_st_flow += 2;
 
3975
                pe->forbidden &= inv_forbidden_edge_mask; /* let it change if it wants */
 
3976
                num_failed ++;
 
3977
            }
 
3978
        }
 
3979
    }
 
3980
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
3981
    RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
3982
    RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask );
 
3983
 
 
3984
 
 
3985
exit_function:
 
3986
 
 
3987
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
3988
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
 
3989
    AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_FREE );
 
3990
    return ret;
 
3991
#undef INC_EDGE_LIST
 
3992
}
 
3993
 
 
3994
/******************************************************************************************************/
 
3995
int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
3996
                     inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
3997
                     int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
3998
{
 
3999
#define INC_EDGE_LIST 16
 
4000
    Vertex     vPathStart, vPathEnd;
 
4001
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
4002
    int        num_failed, num_success, n, nDeltaChargeMax, nMetalCharge;
 
4003
    BNS_EDGE  *pe;
 
4004
    Vertex    v1, v2;
 
4005
    
 
4006
    int i, j, k, ret2, ret;
 
4007
    int num_at = pStruct->num_atoms;
 
4008
    int num_deleted_H = pStruct->num_deleted_H;
 
4009
    int len_at = num_at + num_deleted_H;
 
4010
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
4011
    EdgeIndex e, eNMinusEdge, eNMinusEdge1, eNMinusEdge2, eNPlusEdge2;
 
4012
 
 
4013
    EDGE_LIST AllChargeEdges;
 
4014
 
 
4015
    ret = 0;
 
4016
    num_failed = num_success = 0;
 
4017
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );
 
4018
 
 
4019
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
4020
    pStruct->at = at2;
 
4021
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
4022
    pStruct->at = at;
 
4023
    if ( ret2 < 0 ) {
 
4024
        ret = ret2;
 
4025
        goto exit_function;
 
4026
    }
 
4027
    /* attepmt #1 N#N(+)-N => N(-)=N(+)=N */
 
4028
    for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
 
4029
        if ( at2[i].valence == 1 &&
 
4030
             at2[i].num_H   == 0 &&
 
4031
             at2[i].radical == 0 &&
 
4032
             pVA[i].cNumValenceElectrons == 6 &&  /* terminal -O */
 
4033
             (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 1 &&
 
4034
             !pBNS->edge[eNMinusEdge].forbidden && /* terminal O(-) */
 
4035
             
 
4036
             at2[j=at2[i].neighbor[0]].valence == 2 &&
 
4037
             at2[j].num_H == 0 &&
 
4038
             at2[j].radical == 0 &&
 
4039
             pVA[j].cNumValenceElectrons == 5 &&
 
4040
             (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge1].flow == 1 &&
 
4041
             !pBNS->edge[eNMinusEdge1].forbidden &&
 
4042
             
 
4043
             pVA[k=at2[j].neighbor[at2[j].neighbor[0]==i]].cMetal &&
 
4044
 
 
4045
             (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 &&
 
4046
             !pBNS->edge[eNMinusEdge2].forbidden &&
 
4047
             (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 &&
 
4048
             !pBNS->edge[eNPlusEdge2].forbidden  ) {
 
4049
 
 
4050
            /* found M(q)-N(-)-O(-); convert to M(q-2)-N=O */
 
4051
 
 
4052
            /* find all charge edges to fix */
 
4053
            if ( 0 == AllChargeEdges.num_edges ) {
 
4054
                for ( n = 0; n < num_at; n ++ ) {
 
4055
                    if ( (e = pVA[n].nCMinusGroupEdge - 1)>= 0 &&
 
4056
                         !pBNS->edge[e].forbidden ) {
 
4057
                        if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) {
 
4058
                            goto exit_function;
 
4059
                        }
 
4060
                    }
 
4061
                    if ( (e = pVA[n].nCPlusGroupEdge - 1)>= 0 &&
 
4062
                         !pBNS->edge[e].forbidden ) {
 
4063
                        if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) {
 
4064
                            goto exit_function;
 
4065
                        }
 
4066
                        if ( pVA[n].cNumValenceElectrons == 6 &&
 
4067
                             NO_VERTEX != (e = GetChargeFlowerUpperEdge( pBNS, pVA, e )) &&
 
4068
                             pBNS->edge[e].flow == 0 ) {
 
4069
                            if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) {
 
4070
                                goto exit_function;
 
4071
                            }
 
4072
                        }
 
4073
                    }
 
4074
                }
 
4075
            }
 
4076
 
 
4077
            nMetalCharge = (pBNS->edge[eNPlusEdge2].cap - pBNS->edge[eNPlusEdge2].flow)
 
4078
                           - pBNS->edge[eNMinusEdge2].flow;
 
4079
            if ( nMetalCharge == 0 ) {
 
4080
                /* change on O is invisible; charge from N(-) goes, charge comes to Metal */
 
4081
                nDeltaChargeMax = 0;
 
4082
            } else
 
4083
            if ( nMetalCharge == 2 ) {
 
4084
                /* charges on Metal and N disappear */
 
4085
                nDeltaChargeMax = -2;
 
4086
            } else {
 
4087
                /* charge from N disappears */
 
4088
                nDeltaChargeMax = -1;
 
4089
            }
 
4090
 
 
4091
            SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
4092
            pBNS->edge[eNMinusEdge1].forbidden &= inv_forbidden_edge_mask;
 
4093
            pBNS->edge[eNMinusEdge2].forbidden &= inv_forbidden_edge_mask;
 
4094
            pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask;
 
4095
 
 
4096
            pe = pBNS->edge + eNMinusEdge; /* must be already fixed as a charge edge */
 
4097
 
 
4098
            v1 = pe->neighbor1;
 
4099
            v2 = pe->neighbor12 ^ v1;
 
4100
            pe->flow --;
 
4101
            pBNS->vert[v1].st_edge.flow --;
 
4102
            pBNS->vert[v2].st_edge.flow --;
 
4103
            pBNS->tot_st_flow -= 2;
 
4104
 
 
4105
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
4106
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
4107
 
 
4108
            if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
4109
                              vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == nDeltaChargeMax*/ ) {
 
4110
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
4111
                (*pnNumRunBNS) ++;
 
4112
                *pnTotalDelta += ret;
 
4113
                num_success ++;
 
4114
            } else {
 
4115
                pe->flow ++;
 
4116
                pBNS->vert[v1].st_edge.flow ++;
 
4117
                pBNS->vert[v2].st_edge.flow ++;
 
4118
                pBNS->tot_st_flow += 2;
 
4119
                num_failed ++;
 
4120
            }
 
4121
            RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
4122
        }
 
4123
    }
 
4124
    ret = num_success;
 
4125
 
 
4126
exit_function:
 
4127
 
 
4128
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
 
4129
 
 
4130
    return ret;
 
4131
#undef INC_EDGE_LIST
 
4132
}
 
4133
/******************************************************************************************************/
 
4134
int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
4135
                     inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
4136
                     int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
4137
{
 
4138
#define INC_EDGE_LIST 16
 
4139
    Vertex     vPathStart, vPathEnd;
 
4140
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success, n, nDeltaChargeMax;
 
4141
    BNS_EDGE  *pe;
 
4142
    Vertex    v1, v2;
 
4143
    
 
4144
    int i, j, k, ret2, ret;
 
4145
    int num_at = pStruct->num_atoms;
 
4146
    int num_deleted_H = pStruct->num_deleted_H;
 
4147
    int len_at = num_at + num_deleted_H;
 
4148
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
4149
    EdgeIndex eNMinusEdge, eNPlusEdge, eNMinusEdge1, eNPlusEdge1, eNMinusEdge2, eNPlusEdge2;
 
4150
    EdgeIndex eNFlowerEdge1, eNFlowerEdge2;
 
4151
 
 
4152
    EDGE_LIST CarbonChargeEdges, AllChargeEdges, NNNChargeEdges, CurNNNChargeEdges, AllNNNTermAtoms, AllNIIIChargeEdges;
 
4153
 
 
4154
    ret = 0;
 
4155
    num_failed = num_success = 0;
 
4156
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
 
4157
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );
 
4158
    AllocEdgeList( &NNNChargeEdges, EDGE_LIST_CLEAR );
 
4159
    AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_CLEAR );
 
4160
    AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_CLEAR );
 
4161
    AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_CLEAR );
 
4162
 
 
4163
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
4164
    pStruct->at = at2;
 
4165
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
4166
    pStruct->at = at;
 
4167
    if ( ret2 < 0 ) {
 
4168
        ret = ret2;
 
4169
        goto exit_function;
 
4170
    }
 
4171
    /* attepmt #1 N#N(+)-N => N(-)=N(+)=N: naive approach; expecting tp move (-) from some other atom */
 
4172
    for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
 
4173
        if ( at2[i].valence == 1 &&
 
4174
             at2[i].num_H   == 0 &&
 
4175
             at2[i].chem_bonds_valence == 3 &&
 
4176
             at2[i].charge ==  0 &&
 
4177
             at2[i].radical == 0 &&
 
4178
             pVA[i].cNumValenceElectrons == 5 &&  /* terminal N# */
 
4179
             (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 0 &&
 
4180
             !pBNS->edge[eNMinusEdge].forbidden &&
 
4181
             at2[j=at2[i].neighbor[0]].valence == 2 &&
 
4182
             at2[j].num_H == 0 &&
 
4183
             at2[j].chem_bonds_valence == 4 &&
 
4184
             at2[j].charge == 1 &&
 
4185
             at2[j].radical == 0 &&
 
4186
             pVA[j].cNumValenceElectrons == 5 &&
 
4187
             (eNPlusEdge = pVA[j].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge].flow == 0 &&
 
4188
             !pBNS->edge[eNPlusEdge].forbidden &&
 
4189
             at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 &&
 
4190
             at2[k].num_H == 0 &&
 
4191
             at2[k].chem_bonds_valence == 3 &&
 
4192
             pVA[k].cNumValenceElectrons == 5 &&
 
4193
             (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge2].flow == 1 &&
 
4194
             !pBNS->edge[eNPlusEdge2].forbidden &&
 
4195
             pVA[i].cnListIndex > 0 &&
 
4196
             cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
 
4197
            /* found N#N(+)-N~ where the last N (at2[k]) may be charged */
 
4198
            pe = pBNS->edge + pBNS->vert[i].iedge[0]; /* N#N(+) triple bond edge */
 
4199
            
 
4200
            v1 = pe->neighbor1;
 
4201
            v2 = pe->neighbor12 ^ v1;
 
4202
            pe->flow --;
 
4203
            pBNS->vert[v1].st_edge.flow --;
 
4204
            pBNS->vert[v2].st_edge.flow --;
 
4205
            pBNS->tot_st_flow -= 2;
 
4206
 
 
4207
            pe->forbidden                     |= forbidden_edge_mask;
 
4208
            pBNS->edge[eNPlusEdge].forbidden  |= forbidden_edge_mask;
 
4209
            pBNS->edge[eNPlusEdge2].forbidden |= forbidden_edge_mask;
 
4210
            
 
4211
            
 
4212
            if ( !CarbonChargeEdges.num_edges ) {
 
4213
                /* do not let carbon atoms get charged */
 
4214
                AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); 
 
4215
                if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
 
4216
                    goto exit_function;
 
4217
                }
 
4218
            } else {
 
4219
                SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
4220
            }
 
4221
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
4222
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
4223
 
 
4224
            if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
4225
                              vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 0 ) {
 
4226
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
4227
                (*pnNumRunBNS) ++;
 
4228
                *pnTotalDelta += ret;
 
4229
                num_success ++;
 
4230
                /* fix charges on N(-)=N(+)=N- */
 
4231
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4232
                    goto exit_function;
 
4233
                }
 
4234
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
 
4235
                    goto exit_function;
 
4236
                }
 
4237
            } else {
 
4238
                pe->flow ++;
 
4239
                pBNS->vert[v1].st_edge.flow ++;
 
4240
                pBNS->vert[v2].st_edge.flow ++;
 
4241
                pBNS->tot_st_flow += 2;
 
4242
                num_failed ++;
 
4243
            }
 
4244
            RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
4245
 
 
4246
            pe->forbidden                     &= inv_forbidden_edge_mask;
 
4247
            pBNS->edge[eNPlusEdge].forbidden  &= inv_forbidden_edge_mask;
 
4248
            pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask;
 
4249
        }
 
4250
    }
 
4251
 
 
4252
    /* 2nd attempt: take care of N#N(+)-N=-...=N-N(-) */
 
4253
    /* This would produce nDeltaCharge >= 2 */
 
4254
    
 
4255
    AllChargeEdges.num_edges = 0;
 
4256
    AllNNNTermAtoms.num_edges = 0;
 
4257
    NNNChargeEdges.num_edges = 0;
 
4258
    AllNIIIChargeEdges.num_edges = 0;
 
4259
 
 
4260
    for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
 
4261
        if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 &&
 
4262
             !pBNS->edge[eNMinusEdge].forbidden ) {
 
4263
            if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4264
                goto exit_function;
 
4265
            }
 
4266
        } else {
 
4267
            eNMinusEdge = -1;
 
4268
        }
 
4269
        if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 &&
 
4270
             !pBNS->edge[eNPlusEdge].forbidden ) {
 
4271
            if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
 
4272
                goto exit_function;
 
4273
            }
 
4274
            if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && at2[i].chem_bonds_valence == 3) {
 
4275
                if ( ret = AddToEdgeList( &AllNIIIChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
 
4276
                    goto exit_function;
 
4277
                }
 
4278
            }
 
4279
            /* N flower edge */
 
4280
            if ( pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 &&
 
4281
                 NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) &&
 
4282
                 pBNS->edge[eNFlowerEdge1].flow == 0 &&
 
4283
                 ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) {
 
4284
                goto exit_function;
 
4285
            }
 
4286
        } else {
 
4287
            eNPlusEdge = -1;
 
4288
        }
 
4289
 
 
4290
        if ( 0 <= eNMinusEdge &&
 
4291
             at2[i].valence == 1 &&
 
4292
             at2[i].num_H   == 0 &&
 
4293
             at2[i].radical   == 0 &&
 
4294
             pVA[i].cNumValenceElectrons == 5 &&  /* terminal N# */
 
4295
             
 
4296
             at2[j=at2[i].neighbor[0]].valence == 2 &&
 
4297
             at2[j].num_H == 0 &&
 
4298
             at2[j].radical == 0 &&
 
4299
             pVA[j].cNumValenceElectrons == 5 && 
 
4300
             (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 &&
 
4301
             (eNPlusEdge1  = pVA[j].nCPlusGroupEdge - 1)>= 0 &&
 
4302
             !pBNS->edge[eNMinusEdge1].forbidden &&
 
4303
             !pBNS->edge[eNPlusEdge1].forbidden &&
 
4304
 
 
4305
             at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 &&
 
4306
             at2[k].num_H == 0 &&
 
4307
             at2[k].radical == 0 &&
 
4308
             pVA[k].cNumValenceElectrons == 5 &&
 
4309
             (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 &&
 
4310
             (eNPlusEdge2  = pVA[k].nCPlusGroupEdge - 1)>= 0 &&
 
4311
             !pBNS->edge[eNMinusEdge2].forbidden &&
 
4312
             !pBNS->edge[eNPlusEdge2].forbidden &&
 
4313
 
 
4314
             pVA[i].cnListIndex > 0 &&
 
4315
             cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
 
4316
            /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */
 
4317
            
 
4318
            /* 1. N(-)=N(+)=N- */
 
4319
            if ( pBNS->edge[eNMinusEdge].flow  == 1 &&  /* N(-) */
 
4320
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4321
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4322
                continue; /* already good */
 
4323
            }
 
4324
            /* accumulate terminal atoms of all other NNN */
 
4325
            if ( ret = AddToEdgeList( &AllNNNTermAtoms, i, INC_EDGE_LIST ) ) {
 
4326
                goto exit_function;
 
4327
            }
 
4328
            /* 2. N#N(+)-N= */
 
4329
            if ( pBNS->edge[eNMinusEdge].flow  == 0 && /* N */
 
4330
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4331
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4332
                /* unfix (-) edge on terminal N# */
 
4333
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4334
                    goto exit_function;
 
4335
                }
 
4336
                continue;
 
4337
            }
 
4338
            /* 3. N(-)=N-N= */
 
4339
            if ( pBNS->edge[eNMinusEdge].flow  == 1 && /* N(-) */
 
4340
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */
 
4341
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4342
                /* unfix (+) edge on middle N */
 
4343
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
 
4344
                    goto exit_function;
 
4345
                }
 
4346
                continue;
 
4347
            }
 
4348
            /* 4. N#N(+)-N(-)- */
 
4349
            if ( pBNS->edge[eNMinusEdge].flow  == 0 &&  /* N */
 
4350
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4351
                 pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1    /* N(-) */ ) {
 
4352
                /* unfix (-) edge on the 1st and 3rd N */
 
4353
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4354
                    goto exit_function;
 
4355
                }
 
4356
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
 
4357
                    goto exit_function;
 
4358
                }
 
4359
                continue;
 
4360
            }
 
4361
            /* 5. N#N(+)-N(+)# */
 
4362
            if ( pBNS->edge[eNMinusEdge].flow  == 0 &&  /* N */
 
4363
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4364
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0    /* N(+) */ ) {
 
4365
                /* unfix (-) edge on the 1st and (+) edge on the 3rd N */
 
4366
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4367
                    goto exit_function;
 
4368
                }
 
4369
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
 
4370
                    goto exit_function;
 
4371
                }
 
4372
                continue;
 
4373
            }
 
4374
        }
 
4375
    }
 
4376
    /* try to fix each NNN */
 
4377
    for ( n = AllNNNTermAtoms.num_edges-1; 0 <= n; n -- ) {
 
4378
        i = AllNNNTermAtoms.pnEdges[n];
 
4379
        eNMinusEdge = pVA[i].nCMinusGroupEdge - 1;
 
4380
        /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/
 
4381
        j=at2[i].neighbor[0];
 
4382
        eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1;
 
4383
        eNPlusEdge1  = pVA[j].nCPlusGroupEdge - 1;
 
4384
        k=at2[j].neighbor[at2[j].neighbor[0]==i];
 
4385
        eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1;
 
4386
        eNPlusEdge2  = pVA[k].nCPlusGroupEdge - 1;
 
4387
        /*SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );*/
 
4388
        /* 1. N(-)=N(+)=N- */
 
4389
        if ( pBNS->edge[eNMinusEdge].flow  == 1 &&  /* N(-) */
 
4390
             pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4391
             pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4392
 
 
4393
            RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge );
 
4394
            RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 );
 
4395
            RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 );
 
4396
            RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 );
 
4397
            RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 );
 
4398
 
 
4399
            pe = NULL;
 
4400
        } else   /* 2. N#N(+)-N= */
 
4401
        if ( pBNS->edge[eNMinusEdge].flow  == 0 &&  /* N */
 
4402
             pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4403
             pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4404
            /* decrement triple bond on terminal N# */
 
4405
            pe = pBNS->edge + pBNS->vert[i].iedge[0];
 
4406
        } else
 
4407
        /* 3. N(-)=N-N= */
 
4408
        if ( pBNS->edge[eNMinusEdge].flow  == 1 &&  /* N(-) */
 
4409
             pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */
 
4410
             pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4411
            /* decrement flow on (+) charge edge of the middle =N- */
 
4412
            pe = pBNS->edge + eNPlusEdge1;
 
4413
        } else
 
4414
        /* 4. N#N(+)-N(-)- */
 
4415
        if ( pBNS->edge[eNMinusEdge].flow  == 0 &&  /* N */
 
4416
             pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4417
             pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1    /* N(-) */ ) {
 
4418
            /* decrement triple bond on terminal N# */
 
4419
            pe = pBNS->edge + pBNS->vert[i].iedge[0];
 
4420
        } else
 
4421
        /* 5. N#N(+)-N(+)# */
 
4422
        if ( pBNS->edge[eNMinusEdge].flow  == 0 &&  /* N */
 
4423
             pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4424
             pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0    /* N(+) */ ) {
 
4425
            /* decrement triple bond on terminal N# */
 
4426
            pe = pBNS->edge + pBNS->vert[i].iedge[0];
 
4427
        } else {
 
4428
            pe = NULL; /* unknown case */
 
4429
        }
 
4430
        if ( pe ) {
 
4431
            SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
4432
            RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask );
 
4433
            
 
4434
            v1 = pe->neighbor1;
 
4435
            v2 = pe->neighbor12 ^ v1;
 
4436
            pe->flow --;
 
4437
            pBNS->vert[v1].st_edge.flow --;
 
4438
            pBNS->vert[v2].st_edge.flow --;
 
4439
            pBNS->tot_st_flow -= 2;
 
4440
            
 
4441
            pe->forbidden                    |= forbidden_edge_mask;
 
4442
            
 
4443
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
4444
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
4445
 
 
4446
            if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
4447
                              vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge <= 2*/ ) {
 
4448
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
4449
                (*pnNumRunBNS) ++;
 
4450
                *pnTotalDelta += ret;
 
4451
                num_success ++;
 
4452
                /* fix charges on N(-)=N(+)=N- */
 
4453
                RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge );
 
4454
                RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 );
 
4455
                RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 );
 
4456
                RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 );
 
4457
                RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 );
 
4458
            } else {
 
4459
                pe->flow ++;
 
4460
                pBNS->vert[v1].st_edge.flow ++;
 
4461
                pBNS->vert[v2].st_edge.flow ++;
 
4462
                pBNS->tot_st_flow += 2;
 
4463
                num_failed ++;
 
4464
            }
 
4465
            pe->forbidden &= inv_forbidden_edge_mask;
 
4466
            RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
 
4467
        }
 
4468
    }
 
4469
    
 
4470
    /* 3rd attempt */
 
4471
 
 
4472
    /*
 
4473
    AllChargeEdges.num_edges = 0;
 
4474
    AllNNNTermAtoms.num_edges = 0;
 
4475
    NNNChargeEdges.num_edges = 0;
 
4476
    */
 
4477
    for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
 
4478
 
 
4479
        eNMinusEdge = pVA[i].nCMinusGroupEdge - 1;
 
4480
        /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/
 
4481
 
 
4482
        if ( 0 <= eNMinusEdge &&
 
4483
             at2[i].valence == 1 &&
 
4484
             at2[i].num_H   == 0 &&
 
4485
             at2[i].radical   == 0 &&
 
4486
             pVA[i].cNumValenceElectrons == 5 &&  /* terminal N# */
 
4487
             
 
4488
             at2[j=at2[i].neighbor[0]].valence == 2 &&
 
4489
             at2[j].num_H == 0 &&
 
4490
             at2[j].radical == 0 &&
 
4491
             pVA[j].cNumValenceElectrons == 5 && 
 
4492
             (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 &&
 
4493
             (eNPlusEdge1  = pVA[j].nCPlusGroupEdge - 1)>= 0 &&
 
4494
             !pBNS->edge[eNMinusEdge1].forbidden &&
 
4495
             !pBNS->edge[eNPlusEdge1].forbidden &&
 
4496
 
 
4497
             at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 &&
 
4498
             at2[k].num_H == 0 &&
 
4499
             at2[k].radical == 0 &&
 
4500
             pVA[k].cNumValenceElectrons == 5 &&
 
4501
             (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 &&
 
4502
             (eNPlusEdge2  = pVA[k].nCPlusGroupEdge - 1)>= 0 &&
 
4503
             !pBNS->edge[eNMinusEdge2].forbidden &&
 
4504
             !pBNS->edge[eNPlusEdge2].forbidden &&
 
4505
 
 
4506
             pVA[i].cnListIndex > 0 &&
 
4507
             cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
 
4508
 
 
4509
            /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */
 
4510
            NNNChargeEdges.num_edges = 0;
 
4511
 
 
4512
            eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 );
 
4513
            eNFlowerEdge2 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge2 );
 
4514
 
 
4515
            /* 1. N(-)=N(+)=N- */
 
4516
            if ( pBNS->edge[eNMinusEdge].flow  == 1 &&  /* N(-) */
 
4517
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4518
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4519
                /* fix charges on N(-)=N(+)=N- */
 
4520
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4521
                    goto exit_function;
 
4522
                }
 
4523
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
 
4524
                    goto exit_function;
 
4525
                }
 
4526
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
 
4527
                    goto exit_function;
 
4528
                }
 
4529
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
 
4530
                    goto exit_function;
 
4531
                }
 
4532
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
 
4533
                    goto exit_function;
 
4534
                }
 
4535
                continue; /* already good */
 
4536
            }
 
4537
            /* 2. N#N(+)-N= */
 
4538
            if ( pBNS->edge[eNMinusEdge].flow  == 0 && /* N */
 
4539
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4540
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4541
                /* unfix (-) edge on terminal N# */
 
4542
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
 
4543
                    goto exit_function;
 
4544
                }
 
4545
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
 
4546
                    goto exit_function;
 
4547
                }
 
4548
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
 
4549
                    goto exit_function;
 
4550
                }
 
4551
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
 
4552
                    goto exit_function;
 
4553
                }
 
4554
                pe = pBNS->edge + pBNS->vert[i].iedge[0];
 
4555
                nDeltaChargeMax = 0;
 
4556
                nDeltaChargeMax = (num_failed && !num_success && pStruct->nNumRemovedProtonsMobHInChI > 0)? 2 : 0;
 
4557
            } else
 
4558
            /* 3. N(-)=N-N= */
 
4559
            if ( pBNS->edge[eNMinusEdge].flow  == 1 && /* N(-) */
 
4560
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */
 
4561
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1    /* N */ ) {
 
4562
                /* unfix (+) edge on middle N */
 
4563
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4564
                    goto exit_function;
 
4565
                }
 
4566
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
 
4567
                    goto exit_function;
 
4568
                }
 
4569
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
 
4570
                    goto exit_function;
 
4571
                }
 
4572
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
 
4573
                    goto exit_function;
 
4574
                }
 
4575
                if ( NO_VERTEX != eNFlowerEdge1 &&
 
4576
                     ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) {
 
4577
                    goto exit_function;
 
4578
                }
 
4579
                /* decrement flow on (+) charge edge of the middle =N- */
 
4580
                pe = pBNS->edge + eNPlusEdge1;
 
4581
                nDeltaChargeMax = 2;
 
4582
            } else
 
4583
            /* 4. N#N(+)-N(-)- */
 
4584
            if ( pBNS->edge[eNMinusEdge].flow  == 0 &&  /* N */
 
4585
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4586
                 pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1    /* N(-) */ ) {
 
4587
                /* unfix (-) edge on the 1st and 3rd N */
 
4588
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
 
4589
                    goto exit_function;
 
4590
                }
 
4591
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
 
4592
                    goto exit_function;
 
4593
                }
 
4594
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
 
4595
                    goto exit_function;
 
4596
                }
 
4597
                /* decrement triple bond on terminal N# */
 
4598
                pe = pBNS->edge + pBNS->vert[i].iedge[0];
 
4599
                nDeltaChargeMax = 0;
 
4600
            } else
 
4601
            /* 5. N#N(+)-N(+)# */
 
4602
            if ( pBNS->edge[eNMinusEdge].flow  == 0 &&  /* N */
 
4603
                 pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
 
4604
                 pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0    /* N(+) */ ) {
 
4605
                /* unfix (-) edge on the 1st and (+) edge on the 3rd N */
 
4606
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
 
4607
                    goto exit_function;
 
4608
                }
 
4609
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
 
4610
                    goto exit_function;
 
4611
                }
 
4612
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
 
4613
                    goto exit_function;
 
4614
                }
 
4615
                /* decrement triple bond on terminal N# */
 
4616
                pe = pBNS->edge + pBNS->vert[i].iedge[0];
 
4617
                nDeltaChargeMax = 0;
 
4618
            } else {
 
4619
                continue;
 
4620
            }
 
4621
 
 
4622
            if ( NO_VERTEX != eNFlowerEdge1 && !pBNS->edge[eNFlowerEdge1].flow ) {
 
4623
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) {
 
4624
                    goto exit_function;
 
4625
                }
 
4626
            }
 
4627
            if ( NO_VERTEX != eNFlowerEdge2 && !pBNS->edge[eNFlowerEdge2].flow ) {
 
4628
                if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge2, INC_EDGE_LIST ) ) {
 
4629
                    goto exit_function;
 
4630
                }
 
4631
            }
 
4632
 
 
4633
            v1 = pe->neighbor1;
 
4634
            v2 = pe->neighbor12 ^ v1;
 
4635
            pe->flow --;
 
4636
            pBNS->vert[v1].st_edge.flow --;
 
4637
            pBNS->vert[v2].st_edge.flow --;
 
4638
            pBNS->tot_st_flow -= 2;
 
4639
 
 
4640
            pe->forbidden                    |= forbidden_edge_mask;
 
4641
            
 
4642
            if ( !CarbonChargeEdges.num_edges ) {
 
4643
                /* do not let carbon atoms get charged */
 
4644
                AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); 
 
4645
                if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
 
4646
                    goto exit_function;
 
4647
                }
 
4648
            } else {
 
4649
                SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
4650
            }
 
4651
            SetForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask );
 
4652
 
 
4653
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
4654
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
4655
 
 
4656
            if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
4657
                              vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= nDeltaChargeMax ) {
 
4658
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
4659
                (*pnNumRunBNS) ++;
 
4660
                *pnTotalDelta += ret;
 
4661
                num_success ++;
 
4662
                /* fix charges on N(-)=N(+)=N- */
 
4663
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
 
4664
                    goto exit_function;
 
4665
                }
 
4666
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
 
4667
                    goto exit_function;
 
4668
                }
 
4669
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
 
4670
                    goto exit_function;
 
4671
                }
 
4672
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
 
4673
                    goto exit_function;
 
4674
                }
 
4675
                if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
 
4676
                    goto exit_function;
 
4677
                }
 
4678
            } else {
 
4679
                pe->flow ++;
 
4680
                pBNS->vert[v1].st_edge.flow ++;
 
4681
                pBNS->vert[v2].st_edge.flow ++;
 
4682
                pBNS->tot_st_flow += 2;
 
4683
                num_failed ++;
 
4684
            }
 
4685
            RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
4686
            RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask );
 
4687
            pe->forbidden &= inv_forbidden_edge_mask;
 
4688
            /*pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask;*/ /* BC: array index out of range */
 
4689
        }
 
4690
    }
 
4691
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
4692
 
 
4693
 
 
4694
exit_function:
 
4695
 
 
4696
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
4697
    AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
 
4698
    AllocEdgeList( &NNNChargeEdges, EDGE_LIST_FREE );
 
4699
    AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_FREE );
 
4700
    AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_FREE );
 
4701
    AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_FREE );
 
4702
    return ret;
 
4703
#undef INC_EDGE_LIST
 
4704
}
 
4705
/******************************************************************************************************/
 
4706
int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
4707
                     inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
4708
                     int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
4709
{
 
4710
    int i, j, k, bForbiddenCarbonCharges, ret2, ret;
 
4711
    int num_at = pStruct->num_atoms;
 
4712
    int num_deleted_H = pStruct->num_deleted_H;
 
4713
    int len_at = num_at + num_deleted_H;
 
4714
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
4715
    EDGE_LIST CarbonChargeEdges;
 
4716
 
 
4717
    ret = 0;
 
4718
    bForbiddenCarbonCharges = 0;
 
4719
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
 
4720
        
 
4721
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
4722
    pStruct->at = at2;
 
4723
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
4724
    if ( ret2 < 0 ) {
 
4725
        ret = ret2;
 
4726
        goto exit_function;
 
4727
    }
 
4728
 
 
4729
    /* forbid creation of other N(V) atoms */
 
4730
    /* fix single bonds to metals */
 
4731
    for ( i = 0; i < num_at; i ++ ) {
 
4732
        if ( pVA[i].cNumValenceElectrons == 5 && 
 
4733
             0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) &&
 
4734
             1 == pBNS->edge[k].flow) {
 
4735
            pBNS->edge[k].forbidden |= forbidden_edge_mask;
 
4736
        } else
 
4737
        if ( pVA[i].cMetal ) {
 
4738
            for ( j = 0; j < at2[i].valence; j ++ ) {
 
4739
                if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
 
4740
                    pBNS->edge[pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask;
 
4741
                }
 
4742
            }
 
4743
        }
 
4744
    }
 
4745
 
 
4746
    /*------------------------------------------------------------------------------
 
4747
                 (+)  single line => flow = 0                            (+)-(Y)=(+)super
 
4748
            01  //    double line => flow = 1                fix-> 01   // <-- fix
 
4749
         1 --- 0                                                 1 === 0    
 
4750
          \\ //         edge eij connects vertices i < j:         \   /  02 
 
4751
        12  2   02 <--- edge number: e02 connects vertices v0   12  2(..) <- double 'radical'  
 
4752
            |                        v0 and v2                      |       
 
4753
           =N=      vertex N has number i                          =N=      
 
4754
            |                                                       |       
 
4755
    --------------------------------------------------------------------------------*/
 
4756
    for ( i = 0; i < num_at; i ++ ) {
 
4757
        if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 &&
 
4758
             at2[i].chem_bonds_valence == 5 && !at2[i].charge && !at2[i].radical &&
 
4759
             !(at2[i].endpoint || pStruct->endpoint && pStruct->endpoint[i]) && pVA[i].cnListIndex > 0 &&
 
4760
             cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN &&
 
4761
             pVA[i].nCPlusGroupEdge > 0 ) {
 
4762
            
 
4763
            Vertex v, v0 = NO_VERTEX, v1 = NO_VERTEX, v2 = NO_VERTEX;
 
4764
            EdgeIndex iePlus, ie, ie12 = NO_VERTEX, ie02, ie01;
 
4765
            BNS_VERTEX *pv0, *pv1, *pv2 = NULL;
 
4766
            BNS_EDGE   *pePlus, *pe, *pe12 = NULL, *pe02 = NULL, *pe01 = NULL;
 
4767
            Vertex     vPathStart, vPathEnd;
 
4768
            int        nPathLen;
 
4769
            int        nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
4770
            
 
4771
            iePlus = pVA[i].nCPlusGroupEdge - 1;
 
4772
            pePlus = pBNS->edge + iePlus;
 
4773
 
 
4774
            v0 = IS_BNS_VT_C_GR( pBNS->vert[pePlus->neighbor1].type )?
 
4775
                 (pePlus->neighbor1 ^ pePlus->neighbor12) : pePlus->neighbor1;
 
4776
            pv0 = pBNS->vert + v0;
 
4777
            for ( j = 0; j < pv0->num_adj_edges; j ++ ) {
 
4778
                ie = pv0->iedge[j];
 
4779
                if ( ie == iePlus ) {
 
4780
                    continue;
 
4781
                }
 
4782
                pe = pBNS->edge + ie;
 
4783
                if ( pe->flow == 1 && v2 == NO_VERTEX ) {
 
4784
                    /* 0 - 2, edge 02 */
 
4785
                    v2  = pe->neighbor12 ^ v0;
 
4786
                    pv2 = pBNS->vert + v2;
 
4787
                    ie02 = ie;
 
4788
                    pe02 = pe;
 
4789
                } else
 
4790
                if ( pe->flow == 0 && v1 == NO_VERTEX ) {
 
4791
                    /* 0 - 1, edge 01 */
 
4792
                    v1  = pe->neighbor12 ^ v0;
 
4793
                    pv1 = pBNS->vert + v2;
 
4794
                    ie01 = ie;
 
4795
                    pe01 = pe;
 
4796
                } else {
 
4797
                    ret = RI_ERR_PROGR;
 
4798
                    goto exit_function;
 
4799
                }
 
4800
            }
 
4801
            if ( v1 == NO_VERTEX || v2 == NO_VERTEX ) {
 
4802
                ret = RI_ERR_PROGR;
 
4803
                goto exit_function;
 
4804
            }
 
4805
            for ( j = 0; j < pv2->num_adj_edges; j ++ ) {
 
4806
                ie = pv2->iedge[j];
 
4807
                pe = pBNS->edge + ie;
 
4808
                v  = pe->neighbor12 ^ v2;
 
4809
                if ( v == v0 || v == i ) {
 
4810
                    continue;
 
4811
                } else
 
4812
                if ( v == v1 && pe->flow == 1 ) {
 
4813
                    /* 1 - 2, edge 12 */
 
4814
                    ie12 = ie;
 
4815
                    pe12 = pe;
 
4816
                } else {
 
4817
                    ret = RI_ERR_PROGR;
 
4818
                    goto exit_function;
 
4819
                }
 
4820
            }
 
4821
            if ( ie12 == NO_VERTEX ) {
 
4822
                ret = RI_ERR_PROGR;
 
4823
                goto exit_function;
 
4824
            }
 
4825
            /* rearrange cap and flow, forbid 2 edges */
 
4826
            pe01->flow = 1;
 
4827
            pe12->flow = 0;
 
4828
            pe02->flow = 0;
 
4829
            pv2->st_edge.flow -= 2;
 
4830
            pBNS->tot_st_flow -= 2;
 
4831
            pePlus->forbidden |= forbidden_edge_mask;
 
4832
            pe01->forbidden   |= forbidden_edge_mask;
 
4833
            
 
4834
            if ( !bForbiddenCarbonCharges ) {
 
4835
                if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
 
4836
                    goto exit_function;
 
4837
                }
 
4838
                bForbiddenCarbonCharges = 1;
 
4839
            }
 
4840
 
 
4841
 
 
4842
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
4843
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
4844
            if ( ret == 1 && vPathEnd == v2 && vPathStart == v2 && nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) {
 
4845
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
4846
            } else {
 
4847
                pe01->flow = 0;
 
4848
                pe12->flow = 1;
 
4849
                pe02->flow = 1;
 
4850
                pv2->st_edge.flow += 2;
 
4851
                pBNS->tot_st_flow += 2;
 
4852
            }
 
4853
            pePlus->forbidden &= inv_forbidden_edge_mask;
 
4854
            pe01->forbidden   &= inv_forbidden_edge_mask;
 
4855
 
 
4856
            if ( ret < 0 ) {
 
4857
                goto exit_function;
 
4858
            } else
 
4859
            if ( ret ) {
 
4860
                memcpy( at2, at, len_at*sizeof(at2[0]));
 
4861
                pStruct->at = at2;
 
4862
                ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
4863
                if ( ret2 < 0 ) {
 
4864
                    ret = ret2;
 
4865
                    goto exit_function;
 
4866
                }
 
4867
            }
 
4868
        }
 
4869
    }
 
4870
exit_function:
 
4871
    /* allow creation of other N(V) atoms */
 
4872
    for ( i = 0; i < num_at; i ++ ) {
 
4873
        if ( pVA[i].cNumValenceElectrons == 5 && 
 
4874
             0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) &&
 
4875
             1 == pBNS->edge[k].flow && (pBNS->edge[k].forbidden & forbidden_edge_mask) ) {
 
4876
            pBNS->edge[k].forbidden &= inv_forbidden_edge_mask;
 
4877
        } else
 
4878
        if ( pVA[i].cMetal ) {
 
4879
            for ( j = 0; j < at2[i].valence; j ++ ) {
 
4880
                if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
 
4881
                    pBNS->edge[pBNS->vert[i].iedge[j]].forbidden &= inv_forbidden_edge_mask;
 
4882
                }
 
4883
            }
 
4884
        }
 
4885
    }
 
4886
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
4887
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
4888
    return ret;
 
4889
}
 
4890
/******************************************************************************************************/
 
4891
int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
4892
                     inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
4893
                     int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
4894
{
 
4895
    int i, j, k, neigh, bForbiddenCarbonCharges, nFlowerEdge, delta, ret2, ret;
 
4896
    int num_at = pStruct->num_atoms;
 
4897
    int num_deleted_H = pStruct->num_deleted_H;
 
4898
    int len_at = num_at + num_deleted_H;
 
4899
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
4900
    EDGE_LIST CarbonChargeEdges, FlowerEdgesList;
 
4901
 
 
4902
    ret = 0;
 
4903
    bForbiddenCarbonCharges = 0;
 
4904
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
 
4905
    AllocEdgeList( &FlowerEdgesList, EDGE_LIST_CLEAR );
 
4906
        
 
4907
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
4908
    pStruct->at = at2;
 
4909
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
4910
    if ( ret2 < 0 ) {
 
4911
        ret = ret2;
 
4912
        goto exit_function;
 
4913
    }
 
4914
 
 
4915
    /* forbid creation of other S(IV) atoms */
 
4916
    /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */
 
4917
    for ( i = 0; i < num_at; i ++ ) {
 
4918
        if ( (pVA[i].cNumValenceElectrons == 5 /* N(IV)*/ || pVA[i].cNumValenceElectrons == 6 /* S(VI)*/) && 
 
4919
             0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) &&
 
4920
             !pBNS->edge[k].forbidden &&
 
4921
             6 == pVA[i].cNumValenceElectrons + pBNS->edge[k].flow ) {
 
4922
 
 
4923
            pBNS->edge[k].forbidden |= forbidden_edge_mask;
 
4924
            if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) {
 
4925
                goto exit_function;
 
4926
            }
 
4927
        } else
 
4928
        if ( pVA[i].cMetal ) {
 
4929
            for ( j = 0; j < at2[i].valence; j ++ ) {
 
4930
                if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
 
4931
 
 
4932
                    pBNS->edge[k=pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask;
 
4933
                    if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) {
 
4934
                        goto exit_function;
 
4935
                    }
 
4936
                }
 
4937
            }
 
4938
        } else
 
4939
        /* fix bonds to neighbors of S(IV) if they are not O,S,Se,Te with 2 or more bonds */
 
4940
        /* exactly same if(..) as below */
 
4941
        if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 &&
 
4942
             at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical &&
 
4943
             !at2[i].endpoint && pVA[i].cnListIndex > 0 &&
 
4944
             cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN &&
 
4945
             0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) &&
 
4946
             pBNS->edge[nFlowerEdge].flow > 0 ) {
 
4947
 
 
4948
            for ( j = 0; j < at2[i].valence; j ++ ) {
 
4949
                neigh = at2[i].neighbor[j];
 
4950
                if ( pVA[neigh].cNumValenceElectrons != 6 && at2[neigh].valence > 1 ) {
 
4951
                    k = pBNS->vert[i].iedge[j];
 
4952
                    if ( !pBNS->edge[k].forbidden ) {
 
4953
                        if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) {
 
4954
                            goto exit_function;
 
4955
                        }
 
4956
                        pBNS->edge[k].forbidden |= forbidden_edge_mask;
 
4957
                    }
 
4958
                }
 
4959
            }
 
4960
        }
 
4961
    }
 
4962
    /*------------------------------------------------------------------------------
 
4963
                        example: struct #301,
 
4964
      |          |               disconnected porphyrin with four -SO3(-)
 
4965
     -S-   =>   =S=
 
4966
      |          |
 
4967
    
 
4968
    -------------------------------------------------------------------------------*/
 
4969
 
 
4970
    /*-------------------------------------------------------------------------------
 
4971
     found:                                       super(+)=(Y)        super(+)=(Y)   
 
4972
                                                              \                   \           
 
4973
             (+)  single line => flow = 0                     (+)                 (+)
 
4974
        01  //    double line => flow = 1         fix-> 01   //             01   //             
 
4975
     1 === 0      triple line => flow = 2          (.)1 --- 0(.)  --->    1 --- 0               
 
4976
      \   /       edge eij connects vertices i<j:      \   /  02  run      \\ //  02            
 
4977
    12  2   02 <--- edge number: e02 connects        12  2        BFS    12  2                        
 
4978
       |||              vertices v0 and v2              |||                  |                  
 
4979
       -S-      vertex S has number i                   -S-                 =S=                 
 
4980
       / \                                              / \                 / \                 
 
4981
    --------------------------------------------------------------------------------*/
 
4982
    for ( i = 0; i < num_at; i ++ ) {
 
4983
        if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 &&
 
4984
             at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical &&
 
4985
             !at2[i].endpoint && pVA[i].cnListIndex > 0 &&
 
4986
             cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN &&
 
4987
             /* 01 is nFlowerEdge */
 
4988
             0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) &&
 
4989
             pBNS->edge[nFlowerEdge].flow > 0 ) {
 
4990
            
 
4991
            Vertex     v1 = NO_VERTEX, v2 = NO_VERTEX;
 
4992
            BNS_VERTEX *pv1, *pv2;
 
4993
            BNS_EDGE   *pe;
 
4994
            Vertex     vPathStart, vPathEnd;
 
4995
            int        nPathLen;
 
4996
            int        nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
4997
 
 
4998
            if ( !bForbiddenCarbonCharges ) {
 
4999
                if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
 
5000
                    goto exit_function;
 
5001
                }
 
5002
                bForbiddenCarbonCharges = 1;
 
5003
            }
 
5004
 
 
5005
            delta = 1;
 
5006
            pe = pBNS->edge + nFlowerEdge;                 /* edge  01 */
 
5007
            pv1 = pBNS->vert + (v1 = pe->neighbor1);       /* vertex 0 */
 
5008
            pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */
 
5009
 
 
5010
            pe->forbidden     |= forbidden_edge_mask;
 
5011
            pe->flow          -= delta;
 
5012
            pv1->st_edge.flow -= delta;
 
5013
            pv2->st_edge.flow -= delta;
 
5014
            pBNS->tot_st_flow -= 2*delta;
 
5015
 
 
5016
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
5017
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
5018
            if ( ret == 1 && 
 
5019
                 (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) &&
 
5020
                 nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) {
 
5021
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
5022
            } else {
 
5023
                pe->forbidden     &= inv_forbidden_edge_mask;
 
5024
                pe->flow          += delta;
 
5025
                pv1->st_edge.flow += delta;
 
5026
                pv2->st_edge.flow += delta;
 
5027
                pBNS->tot_st_flow += 2*delta;
 
5028
            }
 
5029
            if ( ret < 0 ) {
 
5030
                goto exit_function;
 
5031
            } else
 
5032
            if ( ret ) {
 
5033
                memcpy( at2, at, len_at*sizeof(at2[0]));
 
5034
                pStruct->at = at2;
 
5035
                ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
5036
                if ( ret2 < 0 ) {
 
5037
                    ret = ret2;
 
5038
                    goto exit_function;
 
5039
                }
 
5040
                /* store the fixed edge to unfix it upon exit */
 
5041
                if ( ret = AddToEdgeList( &FlowerEdgesList, nFlowerEdge, 64 )) {
 
5042
                    goto exit_function;
 
5043
                }
 
5044
            }
 
5045
        }
 
5046
    }
 
5047
exit_function:
 
5048
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
5049
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
5050
    RemoveForbiddenEdgeMask( pBNS, &FlowerEdgesList, forbidden_edge_mask );
 
5051
    AllocEdgeList( &FlowerEdgesList, EDGE_LIST_FREE );
 
5052
    return ret;
 
5053
}
 
5054
/******************************************************************************************************
 
5055
  
 
5056
 
 
5057
  =N(+)=O       =N-O(-)
 
5058
            =>
 
5059
   M(q)         M(q+2)
 
5060
 
 
5061
*******************************************************************************************************/
 
5062
int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
5063
                     inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
5064
                     int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
5065
{
 
5066
    int i, j, k, n, bForbiddenCarbonCharges, delta, ret2, ret, num_NO, num_M;
 
5067
    int num_at = pStruct->num_atoms;
 
5068
    int num_deleted_H = pStruct->num_deleted_H;
 
5069
    int len_at = num_at + num_deleted_H;
 
5070
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
5071
    EDGE_LIST CarbonChargeEdges, NO_ChargeEdgeList, NO_EdgeList;
 
5072
 
 
5073
    Vertex     v1, v2;
 
5074
    BNS_VERTEX *pv1, *pv2;
 
5075
    BNS_EDGE   *pe;
 
5076
    Vertex     vPathStart, vPathEnd;
 
5077
    int        nPathLen;
 
5078
    int        nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
5079
 
 
5080
 
 
5081
    if ( !pTCGroups->num_metal_atoms )
 
5082
        return 0;
 
5083
 
 
5084
    ret = 0;
 
5085
    bForbiddenCarbonCharges = 0;
 
5086
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* all charges */
 
5087
    AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_CLEAR ); /* charges to be changed */
 
5088
    AllocEdgeList( &NO_EdgeList, EDGE_LIST_CLEAR );       /* N(+)=O edges */
 
5089
        
 
5090
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
5091
    pStruct->at = at2;
 
5092
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
5093
    if ( ret2 < 0 ) {
 
5094
        ret = ret2;
 
5095
        goto exit_function;
 
5096
    }
 
5097
    num_NO = num_M = 0;
 
5098
    /* forbid creation of other S(IV) atoms */
 
5099
    /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */
 
5100
    for ( i = 0; i < num_at; i ++ ) {
 
5101
        if ( !pVA[i].cMetal ) {
 
5102
            if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
 
5103
                if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) {
 
5104
                    goto exit_function;
 
5105
                }
 
5106
            }
 
5107
            if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
 
5108
                if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) {
 
5109
                    goto exit_function;
 
5110
                }
 
5111
            }
 
5112
        } else {
 
5113
            num_M ++;
 
5114
        }
 
5115
        /*
 
5116
        if ( pVA[i].cMetal ) {
 
5117
            if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
 
5118
                if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) {
 
5119
                    goto exit_function;
 
5120
                }
 
5121
            }
 
5122
            if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
 
5123
                if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) {
 
5124
                    goto exit_function;
 
5125
                }
 
5126
            }
 
5127
        } else
 
5128
        */
 
5129
        if ( !pVA[i].cMetal &&
 
5130
             pVA[i].cNumValenceElectrons == 6 &&
 
5131
             at2[i].charge == 0 && !at2[i].num_H &&
 
5132
             1 == at2[i].valence && 2 == at2[i].chem_bonds_valence &&
 
5133
             pVA[j=at2[i].neighbor[0]].cNumValenceElectrons == 5 &&
 
5134
             at2[j].charge == 1 && !at2[j].num_H &&
 
5135
             2 == at2[j].valence && 4 == at2[j].chem_bonds_valence ) {
 
5136
            /* found =N(+)=O */
 
5137
            if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden /* O */ &&
 
5138
                 (n = pVA[j].nCPlusGroupEdge -1) >= 0 && !pBNS->edge[j].forbidden /* N */ ) {
 
5139
                if ( (ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) ||
 
5140
                     (ret = AddToEdgeList( &NO_ChargeEdgeList, n, 64 ) ) ) {
 
5141
                    goto exit_function;
 
5142
                }
 
5143
                k = pBNS->vert[i].iedge[0];  /* N(+)=O bond */
 
5144
                if ( !pBNS->edge[k].forbidden ) { 
 
5145
                    if ( ret = AddToEdgeList( &NO_EdgeList, k, 64 ) ) {
 
5146
                        goto exit_function;
 
5147
                    }
 
5148
                    num_NO ++;
 
5149
                }
 
5150
            }
 
5151
        }
 
5152
    }
 
5153
    if ( num_M && num_NO ) {
 
5154
        SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
5155
        SetForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask );
 
5156
        RemoveForbiddenEdgeMask( pBNS, &NO_ChargeEdgeList, forbidden_edge_mask  );
 
5157
        /* now only N(+), O(-) and metal charges are allowed to change */
 
5158
        for ( i = 0; i < NO_EdgeList.num_edges; i ++ ) {
 
5159
            k = NO_EdgeList.pnEdges[i];
 
5160
            delta = 1;
 
5161
            pe = pBNS->edge + k;                 /* edge  N(+)=O */
 
5162
            pv1 = pBNS->vert + (v1 = pe->neighbor1);       /* vertex 0 */
 
5163
            pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */
 
5164
 
 
5165
            pe->flow          -= delta;
 
5166
            pv1->st_edge.flow -= delta;
 
5167
            pv2->st_edge.flow -= delta;
 
5168
            pBNS->tot_st_flow -= 2*delta;
 
5169
 
 
5170
            ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
5171
                                  &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
5172
            if ( ret == 1 && 
 
5173
                 (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) &&
 
5174
                 nDeltaCharge == 0 ) {
 
5175
                ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
5176
            } else {
 
5177
                pe->forbidden     &= inv_forbidden_edge_mask;
 
5178
                pe->flow          += delta;
 
5179
                pv1->st_edge.flow += delta;
 
5180
                pv2->st_edge.flow += delta;
 
5181
                pBNS->tot_st_flow += 2*delta;
 
5182
            }
 
5183
            if ( ret < 0 ) {
 
5184
                goto exit_function;
 
5185
            }
 
5186
        }
 
5187
    }
 
5188
exit_function:
 
5189
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
 
5190
    RemoveForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask );
 
5191
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
 
5192
    AllocEdgeList( &NO_EdgeList, EDGE_LIST_FREE );
 
5193
    AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_FREE );
 
5194
    return ret;
 
5195
}
 
5196
/******************************************************************************************************/
 
5197
int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
5198
                              inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
5199
                              int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
5200
{
 
5201
    int ret2, ret;
 
5202
    int num_at = pStruct->num_atoms;
 
5203
    int num_deleted_H = pStruct->num_deleted_H;
 
5204
    int len_at = num_at + num_deleted_H;
 
5205
    int nNumFixedEdges, nNumAdjEdges;
 
5206
    
 
5207
    ret = 0;
 
5208
 
 
5209
    if ( pTCGroups->num_tgroups ) {
 
5210
        memcpy( at2, at, len_at*sizeof(at2[0]));
 
5211
        pStruct->at = at2;
 
5212
        ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
5213
        pStruct->at = at;
 
5214
        if ( ret2 < 0 ) {
 
5215
            ret = ret2;
 
5216
            goto exit_function;
 
5217
        }
 
5218
#if ( FIND_RING_SYSTEMS == 1 )
 
5219
        ret2 = MarkRingSystemsInp( at2, num_at, 0 );
 
5220
        if ( ret2 < 0 ) {
 
5221
            ret = ret2;
 
5222
            goto exit_function;
 
5223
        }
 
5224
#endif
 
5225
        /* --- forbidden edges --- */
 
5226
        ret2 = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_mask );
 
5227
        if ( ret2 < 0 ) {
 
5228
            ret2 = -(ret + 1);
 
5229
        }
 
5230
        nNumFixedEdges = ret2;
 
5231
        ret = AdjustTgroupsToForbiddenEdges2( pBNS, at2, pVA, num_at, forbidden_edge_mask );
 
5232
        nNumAdjEdges = ret;
 
5233
        if ( ret ) {
 
5234
            pBNS->edge_forbidden_mask |= forbidden_edge_mask;
 
5235
            ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
5236
            (*pnNumRunBNS) ++;
 
5237
            if ( ret < 0 ) {
 
5238
                goto exit_function;
 
5239
            } else {
 
5240
                *pnTotalDelta += ret;
 
5241
            }
 
5242
        }
 
5243
        if ( nNumFixedEdges || nNumAdjEdges ) {
 
5244
            /* removes this edge mask from ALL edges */
 
5245
            RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_mask );
 
5246
        }
 
5247
    }
 
5248
 
 
5249
exit_function:
 
5250
 
 
5251
    return ret;
 
5252
}
 
5253
/******************************************************************************************************/
 
5254
/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH  */
 
5255
int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
5256
                              inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
5257
                              int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
5258
{
 
5259
    int i, num_fixes, tot_num_fixes = 0;
 
5260
    
 
5261
    int ret2, ret;
 
5262
    int num_at = pStruct->num_atoms;
 
5263
    int num_deleted_H = pStruct->num_deleted_H;
 
5264
    int len_at = num_at + num_deleted_H;
 
5265
 
 
5266
    int         itg, j, k, n, m;
 
5267
    Vertex      vtg1, endpoint0=NO_VERTEX, endpoint1, endpoint2, centerpoint;
 
5268
    Vertex      centerpoint_found=NO_VERTEX;
 
5269
    BNS_VERTEX *ptg1, *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL;
 
5270
    BNS_EDGE   *etg0=NULL, *etg1, *etg2, *ecp0, *ecp1, *ecp2;
 
5271
    BNS_EDGE   *etg1_found=NULL, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL;
 
5272
    int         tgroup_number, num_endpoints;
 
5273
    
 
5274
    ret = 0;
 
5275
 
 
5276
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
5277
    pStruct->at = at2;
 
5278
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
5279
    if ( ret2 < 0 ) {
 
5280
        ret = ret2;
 
5281
        goto exit_function;
 
5282
    }
 
5283
    while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pTCGroups->num_tgroups ) {
 
5284
        num_fixes = 0;
 
5285
        for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) {
 
5286
            pCentp_found=NULL;
 
5287
            tgroup_number = pTCGroups->pTCG[itg].ord_num;
 
5288
            vtg1 = pTCGroups->pTCG[itg].nVertexNumber;      /* taut group vertex index */
 
5289
            ptg1 = pBNS->vert + vtg1;                       /* taut group vertex */
 
5290
            num_endpoints = pTCGroups->pTCG[itg].num_edges;
 
5291
            for ( i = 0; i < num_endpoints; i ++ ) {
 
5292
                etg0 = pBNS->edge + ptg1->iedge[i];         /* edge from t-group to endpoint */
 
5293
                endpoint0 = etg0->neighbor12 ^ vtg1;        /* taut endpoint vertex index */
 
5294
                pEndp0 = pBNS->vert + endpoint0;            /* taut endpoint vertex (possible location of mobile H */
 
5295
                if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) {
 
5296
                    /* radical endpoint1 has been detected */
 
5297
                    /* find a 1-3 centerpoint that has two or more endpoints */
 
5298
                    /* connected to the t-group vertex by edges with flow>0 and */
 
5299
                    /* to the centerpoint by edges with flow = 0 */
 
5300
                    /* after that: (1) increment etg1 flow to eliminate radical */
 
5301
                    /* (2) increment flow on one of the two other edges to the t-group */
 
5302
                    /* (3) increment st_cap on the found centerpoint */
 
5303
                    /* (4) rerun the BNS and re-create the structure */
 
5304
                    break;
 
5305
                }
 
5306
            }
 
5307
            if ( i == num_endpoints ) {
 
5308
                continue;
 
5309
            }
 
5310
            if ( i < num_endpoints ) {
 
5311
                /* tautomeric endpoint found; traverse its t-group edges */
 
5312
                for ( j = 0; j < num_endpoints; j ++ ) {
 
5313
                    if ( i == j ) {
 
5314
                        continue; /* avoid the already found radical endpoint */
 
5315
                    }
 
5316
                    etg1 = pBNS->edge + ptg1->iedge[j];  /* another edge from t-group to another endpoinr */
 
5317
                    endpoint1 = etg1->neighbor12 ^ vtg1; /* another endpoint vertex index */
 
5318
                    pEndp1 = pBNS->vert + endpoint1;     /* another endpoint vertex */
 
5319
                    if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
 
5320
                        continue; /* one more radical-endpoint! What is going on here??? */
 
5321
                    }
 
5322
                    if ( !etg1->flow ) {
 
5323
                        continue; /* avoid enpoints that do not have an attachment */
 
5324
                    }
 
5325
                    if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) {
 
5326
                        continue; /* should not happen */
 
5327
                    }
 
5328
                    /* traverse endpoint1 edges to find a single bond connecting it to the centerpoint */
 
5329
                    for ( k = 0; k < at2[endpoint1].valence; k ++ ) {
 
5330
                        ecp1 = pBNS->edge + pEndp1->iedge[k];
 
5331
                        if ( ecp1->flow ) {
 
5332
                            continue;
 
5333
                        }
 
5334
                        centerpoint = ecp1->neighbor12 ^ endpoint1;
 
5335
                        pCentp = pBNS->vert + centerpoint;
 
5336
                        /* traverse centerpoint edges to find a single bond to the 2nd endpoint */
 
5337
                        for ( n = 0; n < at2[centerpoint].valence; n ++ ) {
 
5338
                            ecp2 = pBNS->edge + pCentp->iedge[n];
 
5339
                            if ( ecp2->flow ) {
 
5340
                                continue;
 
5341
                            }
 
5342
                            endpoint2 = ecp2->neighbor12 ^ centerpoint;
 
5343
                            if ( endpoint2 <= endpoint1 || !pVA[endpoint2].nTautGroupEdge ) {
 
5344
                                continue; /* don't go back: neighbors are in order of ascending ord. numbers */
 
5345
                            }
 
5346
                            pEndp2 = pBNS->vert + endpoint2;
 
5347
                            if ( !(pEndp2->type & BNS_VERT_TYPE_ENDPOINT) ) {
 
5348
                                continue;
 
5349
                            }
 
5350
                            etg2 = pBNS->edge + pVA[endpoint2].nTautGroupEdge - 1;
 
5351
                            if ( !etg2->flow || (etg2->neighbor12 ^ endpoint2) != vtg1 ) {
 
5352
                                continue;
 
5353
                            }
 
5354
                            /* we have found the path:
 
5355
                                              Endp1                                Endp1        
 
5356
                                       etg1 //     \ ecp1                   etg1 /     \\ ecp1  
 
5357
                                  etg0     //       \                  etg0     /       \\      
 
5358
                             Endp0-----tg1           Centp  -->   Endp0=====tg1           Centp 
 
5359
                              ^            \\       /                           \\       /      
 
5360
                      radical |        etg2 \\     / ecp2                   etg2 \\     / ecp2  
 
5361
                                              Endp2                                Endp2        
 
5362
                            */                                                                  
 
5363
 
 
5364
                            /* compare centerpoints */
 
5365
                            if ( !pCentp_found ||
 
5366
                                 /* try to avoid carbons */
 
5367
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5368
                                  pVA[centerpoint].cPeriodicRowNumber   != 1) &&
 
5369
                                 pVA[centerpoint_found].cNumValenceElectrons == 4 &&
 
5370
                                 pVA[centerpoint_found].cPeriodicRowNumber   == 1 ||
 
5371
                                 /* try a better non-carbon */
 
5372
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5373
                                  pVA[centerpoint].cPeriodicRowNumber   != 1  ) &&
 
5374
                                 (at[centerpoint].valence >  at[centerpoint_found].valence ||
 
5375
                                  at[centerpoint].valence == at[centerpoint_found].valence &&
 
5376
                                  at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
 
5377
 
 
5378
                                pCentp_found = pCentp;
 
5379
                                etg1_found   = etg1;
 
5380
                                ecp1_found   = ecp1;
 
5381
                                centerpoint_found = centerpoint;
 
5382
                                break;
 
5383
                            }                                
 
5384
                        }
 
5385
                    }
 
5386
                }
 
5387
            }
 
5388
            if ( pCentp_found ) {
 
5389
                /* ---- (1) */
 
5390
                etg0->flow ++;
 
5391
                pEndp0->st_edge.flow ++;
 
5392
                /* ---- (2) */
 
5393
                etg1_found->flow --;
 
5394
                /* ---- (3) */
 
5395
                ecp1_found->flow ++;
 
5396
                /* ---- (4) */
 
5397
                pCentp_found->st_edge.flow ++;
 
5398
                pCentp_found->st_edge.cap  ++;
 
5399
 
 
5400
                pBNS->tot_st_flow += 2;
 
5401
                pBNS->tot_st_cap  += 1;
 
5402
                pCentp_found = NULL;
 
5403
                num_fixes ++;
 
5404
                tot_num_fixes ++;   /* #1 Mob-H */
 
5405
                continue;
 
5406
            }
 
5407
 
 
5408
            /* 2nd attempt: increment flow in centerpoint---radical_endpint edge */
 
5409
            if ( i < num_endpoints ) {
 
5410
                /* tautomeric endpoint found; traverse its t-group edges */
 
5411
                for ( j = 0; j < num_endpoints; j ++ ) {
 
5412
                    if ( i == j ) {
 
5413
                        continue; /* avoid the found radical endpoint */
 
5414
                    }
 
5415
                    etg1 = pBNS->edge + ptg1->iedge[j];
 
5416
                    endpoint1 = etg1->neighbor12 ^ vtg1;
 
5417
                    pEndp1 = pBNS->vert + endpoint1;     /* another endpoint */
 
5418
                    if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
 
5419
                        continue; /* one more radical-endpoint! What is going on here??? */
 
5420
                    }
 
5421
                    if ( !etg1->flow ) {
 
5422
                        continue; /* avoid enpoints that do not have an attachment */
 
5423
                    }
 
5424
                    if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) {
 
5425
                        continue; /* should not happen */
 
5426
                    }
 
5427
                    /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
 
5428
                    for ( k = 0; k < at2[endpoint1].valence; k ++ ) {
 
5429
                        ecp1 = pBNS->edge + pEndp1->iedge[k];
 
5430
                        if ( ecp1->flow ) {
 
5431
                            continue;
 
5432
                        }
 
5433
                        centerpoint = ecp1->neighbor12 ^ endpoint1;
 
5434
                        pCentp = pBNS->vert + centerpoint;
 
5435
                        if ( pCentp->type & BNS_VERT_TYPE_ENDPOINT ) {
 
5436
                            continue; /* do not set another endpoint's valence = an unusual value */
 
5437
                        }
 
5438
 
 
5439
                        /* traverse centerpoint edges to find edge connecting it to the endpoint0 */
 
5440
                        ecp2 = NULL;
 
5441
                        pEndp2 = NULL;
 
5442
                        for ( n = 0; n < at2[centerpoint].valence; n ++ ) {
 
5443
                            ecp0 = pBNS->edge + pCentp->iedge[n];
 
5444
                            if ( ecp0->flow ) {
 
5445
                                endpoint2 = ecp0->neighbor12 ^ centerpoint;
 
5446
                                if ( (pBNS->vert[endpoint2].type & BNS_VERT_TYPE_ENDPOINT) ) {
 
5447
                                    continue; /* ignore endpoint2 if it is tautomeric endpoint */
 
5448
                                }
 
5449
                                /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */
 
5450
                                for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) {
 
5451
                                    if ( at[centerpoint].sb_ord[m] == n ) {
 
5452
                                        endpoint2 = NO_VERTEX;
 
5453
                                        break;
 
5454
                                    }
 
5455
                                }
 
5456
                                if ( endpoint2 == NO_VERTEX ) {
 
5457
                                    continue;
 
5458
                                }
 
5459
                                pEndp2 = pBNS->vert + endpoint2;  /* found */
 
5460
                                ecp2   = ecp0;
 
5461
                                break;
 
5462
                            }
 
5463
                        }
 
5464
                        for ( n = 0; n < at[centerpoint].valence; n ++ ) {
 
5465
                            ecp0 = pBNS->edge + pCentp->iedge[n];
 
5466
                            if ( ecp0->flow ) {
 
5467
                                continue;
 
5468
                            }
 
5469
                            if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) {
 
5470
                                continue;
 
5471
                            }                    
 
5472
                            /* Found:
 
5473
                                              Endp2(not endpoint)           Endp2(not radical)
 
5474
                                               ||                             |
 
5475
                                               ||ecp2                         |ecp2
 
5476
                                          ecp0 ||  ecp1                 ecp0  |  ecp1     
 
5477
                                     Endp0----Centp----Endp1       Endp0====Centp----Endp1
 
5478
                                      ^  \             /      -->      \             /    
 
5479
                              radical |   \           /                 \           /     
 
5480
                                           \         /                   \         /      
 
5481
                                       etg0 \       / etg1           etg0 \       / etg1  
 
5482
                                             \     /                       \     /        
 
5483
                                               tg1                           tg1          
 
5484
 
 
5485
                             */
 
5486
 
 
5487
                            /* compare centerpoints */
 
5488
                            if ( !pCentp_found ||
 
5489
                                 /* try to avoid carbons */
 
5490
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5491
                                  pVA[centerpoint].cPeriodicRowNumber   != 1) &&
 
5492
                                 pVA[centerpoint_found].cNumValenceElectrons == 4 &&
 
5493
                                 pVA[centerpoint_found].cPeriodicRowNumber   == 1 ||
 
5494
                                 /* try a better non-carbon */
 
5495
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5496
                                  pVA[centerpoint].cPeriodicRowNumber   != 1  ) &&
 
5497
                                 (at[centerpoint].valence >  at[centerpoint_found].valence ||
 
5498
                                  at[centerpoint].valence == at[centerpoint_found].valence &&
 
5499
                                  at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
 
5500
 
 
5501
                                pCentp_found = pCentp;
 
5502
                                etg1_found   = etg1;
 
5503
                                ecp0_found   = ecp0;
 
5504
                                centerpoint_found = centerpoint;
 
5505
                                pEndp2_found = pEndp2;
 
5506
                                ecp2_found   = ecp2;
 
5507
                                break;
 
5508
                            }                                
 
5509
                        }
 
5510
                    }
 
5511
                }
 
5512
            }
 
5513
            if ( pCentp_found ) {
 
5514
                ecp0_found->flow ++;
 
5515
                if ( ecp0_found->cap < ecp0_found->flow ) {
 
5516
                    ecp0_found->cap = ecp0_found->flow;
 
5517
                }
 
5518
                pEndp0->st_edge.flow ++;
 
5519
                if ( pEndp2_found && ecp2_found ) {
 
5520
                    ecp2_found->flow --;
 
5521
                    pEndp2_found->st_edge.flow --;
 
5522
                } else {
 
5523
                    /* Endp2 not found */
 
5524
                    pCentp_found->st_edge.flow ++;
 
5525
                    pCentp_found->st_edge.cap  ++;
 
5526
                    pBNS->tot_st_flow += 2; /* radical elimination */
 
5527
                    pBNS->tot_st_cap  += 1;
 
5528
                }
 
5529
 
 
5530
                pCentp_found = NULL;
 
5531
                num_fixes ++;
 
5532
                tot_num_fixes ++;      /* #2 Mob-H */
 
5533
                continue;
 
5534
            }
 
5535
            /* 3rd attempt: find =C= and move radical to it */
 
5536
            if ( i < num_endpoints ) {
 
5537
                int jj, delta, bNotFixed = 1;
 
5538
                Vertex     vPathStart, vPathEnd, v1, v2;
 
5539
                int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
5540
                for ( jj = 0; jj < num_at && bNotFixed; jj ++ ) {
 
5541
                    if ( at2[i].endpoint ) {
 
5542
                        continue;
 
5543
                    }
 
5544
                    if ( 2 == at2[jj].valence && pBNS->vert[jj].st_edge.cap == pBNS->vert[jj].st_edge.flow &&
 
5545
                         4 == pVA[jj].cNumValenceElectrons &&
 
5546
                         !(ecp0 = pBNS->edge + pBNS->vert[jj].iedge[0])->forbidden &&
 
5547
                         !(ecp1 = pBNS->edge + pBNS->vert[jj].iedge[1])->forbidden &&
 
5548
                         1 == ecp0->flow && 1 == ecp1->flow &&
 
5549
                         !at2[(int)at2[i].neighbor[0]].sb_parity[0] &&
 
5550
                         !at2[(int)at2[i].neighbor[1]].sb_parity[0]  ) {
 
5551
                        /* found =C=; make a radical and try to cancel the two radicals */
 
5552
                        k = ecp0->neighbor12 ^ jj;
 
5553
                        if ( at2[k].endpoint ) {
 
5554
                            ecp0 = ecp1;
 
5555
                            k = ecp0->neighbor12 ^ jj;
 
5556
                            if ( at2[k].endpoint ) {
 
5557
                                continue;
 
5558
                            }
 
5559
                        }
 
5560
                        delta = 1;
 
5561
                        /* decrement C valence */
 
5562
                        pBNS->vert[jj].st_edge.flow -= delta;
 
5563
                        pBNS->vert[jj].st_edge.cap  -= delta;
 
5564
                        /* decrement bond order */
 
5565
                        ecp0->flow                 -= delta;
 
5566
                        /* reflect the changes in at2[k] to make it a radical */
 
5567
                        pBNS->vert[k].st_edge.flow -= delta;
 
5568
                        pBNS->tot_st_cap           -= delta;
 
5569
                        pBNS->tot_st_flow          -= 2*delta;
 
5570
 
 
5571
                        v1 = endpoint0;
 
5572
                        v2 = k;
 
5573
 
 
5574
                        ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
5575
                                              &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
5576
 
 
5577
                        if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
5578
                                           vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
 
5579
                            ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
5580
                            if ( ret2 > 0 ) {
 
5581
                                num_fixes ++;
 
5582
                                tot_num_fixes ++;   /* #3 Mob-H */
 
5583
                                pBNS->vert[jj].st_edge.cap += delta; /* create radical on =C- */
 
5584
                                pBNS->tot_st_cap          += delta;
 
5585
                                pCentp_found  = NULL;
 
5586
                                bNotFixed     = 0; /* exit from the cycle */
 
5587
                                break;
 
5588
                            }
 
5589
                        } else {
 
5590
                            /* failed */
 
5591
                            pBNS->vert[jj].st_edge.flow += delta;
 
5592
                            pBNS->vert[jj].st_edge.cap  += delta;
 
5593
                            /* decrement bond order */
 
5594
                            ecp0->flow                 += delta;
 
5595
                            /* reflect the changes in at2[k] to make it a radical */
 
5596
                            pBNS->vert[k].st_edge.flow += delta;
 
5597
                            pBNS->tot_st_cap           += delta;
 
5598
                            pBNS->tot_st_flow          += 2*delta;
 
5599
                        }
 
5600
                        if ( ret2 < 0 ) {
 
5601
                            ret = ret2;
 
5602
                            goto exit_function;
 
5603
                        }
 
5604
                    }
 
5605
                }
 
5606
            }
 
5607
        }
 
5608
        if ( !num_fixes ) {
 
5609
            break;
 
5610
        }
 
5611
    }
 
5612
    ret = tot_num_fixes;
 
5613
exit_function:
 
5614
    pStruct->at = at;
 
5615
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
5616
    return ret;
 
5617
}
 
5618
/******************************************************************************************************/
 
5619
/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH  */
 
5620
int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
5621
                              inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
5622
                              int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
5623
{
 
5624
#define IS_C(x) (NO_VERTEX != x && pVA[x].cNumValenceElectrons == 4 && pVA[x].cPeriodicRowNumber == 1)
 
5625
    int i, num_fixes, tot_num_fixes = 0;
 
5626
    
 
5627
    int ret2, ret;
 
5628
    int num_at = pStruct->num_atoms;
 
5629
    int num_deleted_H = pStruct->num_deleted_H;
 
5630
    int len_at = num_at + num_deleted_H;
 
5631
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
5632
    EDGE_LIST ChargeEdgeList, BondEdgeList;
 
5633
    int         itg, j, k, n, m, num_endp;
 
5634
    Vertex      endpoint0=NO_VERTEX, endpoint1, endpoint2=NO_VERTEX, centerpoint;
 
5635
    Vertex      centerpoint_found=NO_VERTEX, endpoint2_found=NO_VERTEX;
 
5636
    BNS_VERTEX *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL;
 
5637
    BNS_EDGE   *ecp0, *ecp1, *ecp2, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL;
 
5638
    int          tgroup_number, num_endpoints;
 
5639
    
 
5640
    ret = 0;
 
5641
 
 
5642
    if ( pStruct->iMobileH != TAUT_NON )
 
5643
        return ret;
 
5644
 
 
5645
    AllocEdgeList( &ChargeEdgeList, EDGE_LIST_CLEAR);
 
5646
    AllocEdgeList( &BondEdgeList, EDGE_LIST_CLEAR);
 
5647
 
 
5648
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
5649
    pStruct->at = at2;
 
5650
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
5651
    if ( ret2 < 0 ) {
 
5652
        ret = ret2;
 
5653
        goto exit_function;
 
5654
    }
 
5655
    while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) {
 
5656
        int iEndpoint = 0;
 
5657
        num_fixes = 0;
 
5658
        for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) {
 
5659
            pCentp_found=NULL;
 
5660
            tgroup_number = pStruct->ti.t_group[itg].nGroupNumber;
 
5661
            num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints;
 
5662
            for ( i = 0; i < num_endpoints; i ++ ) {
 
5663
                endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i];
 
5664
                pEndp0 = pBNS->vert + endpoint0;  /* taut endpoint vertex (possible location of mobile H */
 
5665
                if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) {
 
5666
                    /* radical endpoint1 has been detected */
 
5667
                    /* find a 1-3 centerpoint that has two or more endpoints */
 
5668
                    /* connected to the t-group vertex by edges with flow>0 and */
 
5669
                    /* to the centerpoint by edges with flow = 0 */
 
5670
                    /* after that: (1) increment etg1 flow to eliminate radical */
 
5671
                    /* (2) increment flow on one of the two other edges to the t-group */
 
5672
                    /* (3) increment st_cap on the found centerpoint */
 
5673
                    /* (4) rerun the BNS and re-create the structure */
 
5674
                    break;
 
5675
                }
 
5676
            }
 
5677
 
 
5678
            /* 2nd attempt: increment flow in centerpoint---radical_endpoint edge */
 
5679
            pCentp_found = NULL;
 
5680
            if ( i < num_endpoints ) {
 
5681
                /* tautomeric endpoint found; traverse its t-group edges */
 
5682
                for ( j = 0; j < num_endpoints; j ++ ) {
 
5683
                    if ( i == j ) {
 
5684
                        continue; /* avoid the found radical endpoint */
 
5685
                    }
 
5686
                    endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j];
 
5687
                    pEndp1 = pBNS->vert + endpoint1;     /* another endpoint */
 
5688
 
 
5689
                    if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
 
5690
                        continue; /* one more radical-endpoint! What is going on here??? */
 
5691
                    }
 
5692
                    if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) {
 
5693
                        continue; /* avoid enpoints that do not have an attachment */
 
5694
                    }
 
5695
                    if ( !pStruct->endpoint[endpoint1] ) {
 
5696
                        continue; /* should not happen */
 
5697
                    }
 
5698
                    /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
 
5699
                    for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) {
 
5700
                        ecp1 = pBNS->edge + pEndp1->iedge[k];
 
5701
                        if ( ecp1->flow ) {
 
5702
                            continue;
 
5703
                        }
 
5704
                        centerpoint = ecp1->neighbor12 ^ endpoint1;
 
5705
                        if ( centerpoint >= pBNS->num_atoms ) {
 
5706
                            break; /* no more edges to atoms */
 
5707
                        }
 
5708
                        pCentp = pBNS->vert + centerpoint;
 
5709
                        if ( pStruct->endpoint[centerpoint] ) {
 
5710
                            continue; /* do not set another endpoint's valence = an unusual value */
 
5711
                        }
 
5712
                        /* traverse centerpoint edges to find edge connecting it to the endpoint0 */
 
5713
                        /* 1. Find a double bond to an endpoint */
 
5714
                        ecp2 = NULL;
 
5715
                        pEndp2 = NULL;
 
5716
                        for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) {
 
5717
                            ecp0 = pBNS->edge + pCentp->iedge[n];
 
5718
                            if ( ecp0->flow ) {
 
5719
                                endpoint2 = ecp0->neighbor12 ^ centerpoint;
 
5720
                                if ( pStruct->endpoint[endpoint2] /* ??? */ ) {
 
5721
                                    continue;
 
5722
                                }
 
5723
                                /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */
 
5724
                                for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) {
 
5725
                                    if ( at[centerpoint].sb_ord[m] == n ) {
 
5726
                                        endpoint2 = NO_VERTEX;
 
5727
                                        break;
 
5728
                                    }
 
5729
                                }
 
5730
                                if ( endpoint2 == NO_VERTEX ) {
 
5731
                                    continue;
 
5732
                                }
 
5733
                                pEndp2 = pBNS->vert + endpoint2;
 
5734
                                ecp2   = ecp0;
 
5735
                                break;
 
5736
                            }
 
5737
                        }
 
5738
                        if ( !ecp2 ) {
 
5739
                            continue;
 
5740
                        }
 
5741
                        /* 2. Find a single bond to an endpoint0 */
 
5742
                        for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) {
 
5743
                            ecp0 = pBNS->edge + pCentp->iedge[n];
 
5744
                            if ( ecp0->flow ) {
 
5745
                                continue;
 
5746
                            }
 
5747
                            if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) {
 
5748
                                continue;
 
5749
                            }                    
 
5750
                            /* Found:
 
5751
                                              Endp2                         Endp2(not radical)
 
5752
                                               ||                             |
 
5753
                                               ||ecp2                         |ecp2
 
5754
                                          ecp0 ||  ecp1                 ecp0  |  ecp1     
 
5755
                                     Endp0----Centp----Endp1       Endp0====Centp----Endp1
 
5756
                                      ^  \             /      -->      \             /    
 
5757
                              radical |   \           /                 \           /     
 
5758
                                           \         /                   \         /      
 
5759
                                       etg0 \       / etg1           etg0 \       / etg1  
 
5760
                                             \     /                       \     /        
 
5761
                                               tg1                           tg1          
 
5762
 
 
5763
                             */
 
5764
 
 
5765
                            /* compare centerpoints */
 
5766
                            if ( !pCentp_found ||
 
5767
                                 /* try to avoid carbons */
 
5768
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5769
                                  pVA[centerpoint].cPeriodicRowNumber   != 1) &&
 
5770
                                 pVA[centerpoint_found].cNumValenceElectrons == 4 &&
 
5771
                                 pVA[centerpoint_found].cPeriodicRowNumber   == 1 ||
 
5772
                                 /* try a better non-carbon */
 
5773
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5774
                                  pVA[centerpoint].cPeriodicRowNumber   != 1  ) &&
 
5775
                                 (at[centerpoint].valence >  at[centerpoint_found].valence ||
 
5776
                                  at[centerpoint].valence == at[centerpoint_found].valence &&
 
5777
                                  at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
 
5778
 
 
5779
                                pCentp_found = pCentp;
 
5780
                                ecp0_found   = ecp0;
 
5781
                                centerpoint_found = centerpoint;
 
5782
                                pEndp2_found = pEndp2;
 
5783
                                ecp2_found   = ecp2;
 
5784
                                break;
 
5785
                            }                                
 
5786
                        }
 
5787
                    }
 
5788
                }
 
5789
            }
 
5790
            /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */
 
5791
            /* result: radicals on Endp0 and Centp => run BNS */
 
5792
            if ( pCentp_found ) {
 
5793
                /* make ecp0 a double bond, make ecp2 a single bond, remove radical */
 
5794
                ecp0_found->flow ++;
 
5795
                if ( ecp0_found->cap < ecp0_found->flow ) {
 
5796
                    ecp0_found->cap = ecp0_found->flow;
 
5797
                }
 
5798
                pEndp0->st_edge.flow ++;
 
5799
                if ( pEndp2_found && ecp2_found ) {
 
5800
                    ecp2_found->flow --;
 
5801
                    pEndp2_found->st_edge.flow --;
 
5802
                } else {
 
5803
                    /* Endp2 not found: only make ecp0 a double bond */
 
5804
                    pCentp_found->st_edge.flow ++;
 
5805
                    pCentp_found->st_edge.cap  ++;
 
5806
                    pBNS->tot_st_flow += 2; /* radical elimination */
 
5807
                    pBNS->tot_st_cap  += 1;
 
5808
                }
 
5809
 
 
5810
                pCentp_found = NULL;
 
5811
                num_fixes ++;    /* #2 */
 
5812
                tot_num_fixes ++;
 
5813
                continue;
 
5814
            }
 
5815
            /* 1st attempt */
 
5816
            pCentp_found = NULL;
 
5817
            if ( i < num_endpoints ) {
 
5818
                /* tautomeric endpoint found; traverse its t-group edges */
 
5819
                for ( j = 0; j < num_endpoints; j ++ ) {
 
5820
                    if ( i == j ) {
 
5821
                        continue; /* avoid the found radical endpoint */
 
5822
                    }
 
5823
                    endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j];
 
5824
                    pEndp1 = pBNS->vert + endpoint1;     /* another endpoint */
 
5825
                    if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
 
5826
                        continue; /* one more radical-endpoint! What is going on here??? */
 
5827
                    }
 
5828
                    if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) {
 
5829
                        continue; /* avoid enpoints that do not have an attachment */
 
5830
                    }
 
5831
                    if ( !pStruct->endpoint[endpoint1] ) {
 
5832
                        continue; /* should not happen */
 
5833
                    }
 
5834
                    /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
 
5835
                    for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) {
 
5836
                        ecp1 = pBNS->edge + pEndp1->iedge[k];
 
5837
                        if ( ecp1->flow ) {
 
5838
                            continue;
 
5839
                        }
 
5840
                        centerpoint = ecp1->neighbor12 ^ endpoint1;
 
5841
                        if ( centerpoint >= pBNS->num_atoms ) {
 
5842
                            break;
 
5843
                        }
 
5844
                        pCentp = pBNS->vert + centerpoint;
 
5845
                        /* traverse centerpoint edges to find the 2nd endpoint */
 
5846
                        for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) {
 
5847
                            ecp2 = pBNS->edge + pCentp->iedge[n];
 
5848
                            if ( ecp2->flow ) {
 
5849
                                continue;
 
5850
                            }
 
5851
                            endpoint2 = ecp2->neighbor12 ^ centerpoint;
 
5852
                            if ( endpoint2 >= pBNS->num_atoms ) {
 
5853
                                break;
 
5854
                            }
 
5855
                            if ( !pStruct->endpoint[endpoint2] ) {
 
5856
                                continue; 
 
5857
                            }
 
5858
                            pEndp2 = pBNS->vert + endpoint2;
 
5859
                            
 
5860
                            if ( at2[endpoint2].num_H || at2[endpoint1].charge == -1 ) {
 
5861
                                continue;
 
5862
                            }
 
5863
 
 
5864
                            /* we have found the path:
 
5865
                            
 
5866
                            Endp1 has no attachments, Endp2 has.
 
5867
 
 
5868
                                              Endp1                                Endp1        
 
5869
                                       etg1 //     \ ecp1                   etg1 /     \\ ecp1  
 
5870
                                  etg0     //       \                  etg0     /       \\      
 
5871
                             Endp0-----tg1           Centp  -->   Endp0=====tg1           Centp 
 
5872
                              ^            \\       /                           \\       /      
 
5873
                      radical |        etg2 \\     / ecp2                   etg2 \\     / ecp2  
 
5874
                                              Endp2                                Endp2        
 
5875
                            */                                                                  
 
5876
 
 
5877
                            /* compare centerpoints */
 
5878
                            if ( !pCentp_found ||
 
5879
                                 /* try to avoid carbons */
 
5880
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5881
                                  pVA[centerpoint].cPeriodicRowNumber   != 1) &&
 
5882
                                 pVA[centerpoint_found].cNumValenceElectrons == 4 &&
 
5883
                                 pVA[centerpoint_found].cPeriodicRowNumber   == 1 ||
 
5884
                                 /* try a better non-carbon */
 
5885
                                 (pVA[centerpoint].cNumValenceElectrons != 4 ||
 
5886
                                  pVA[centerpoint].cPeriodicRowNumber   != 1  ) &&
 
5887
                                 (at[centerpoint].valence >  at[centerpoint_found].valence ||
 
5888
                                  at[centerpoint].valence == at[centerpoint_found].valence &&
 
5889
                                  at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
 
5890
 
 
5891
                                pCentp_found = pCentp;
 
5892
                                ecp1_found   = ecp1;
 
5893
                                centerpoint_found = centerpoint;
 
5894
                                break;
 
5895
                            }                                
 
5896
                        }
 
5897
                    }
 
5898
                }
 
5899
            }
 
5900
            if ( pCentp_found ) {
 
5901
                /* create a new radical at the centerpoint and try to cancel them */
 
5902
                int delta = 1, ret3;
 
5903
                Vertex     vPathStart, vPathEnd, v1, v2;
 
5904
                int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
5905
                
 
5906
                pCentp_found->st_edge.cap += delta;
 
5907
                pBNS->tot_st_cap          += delta;
 
5908
 
 
5909
                v1 = pCentp_found - pBNS->vert;
 
5910
                v2 = pEndp0 - pBNS->vert;
 
5911
                ret3 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
5912
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
5913
 
 
5914
                if ( ret3 == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
5915
                                   vPathEnd == v2 && vPathStart == v1) && nDeltaCharge % 2 == 0 ) {
 
5916
                    ret3 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
5917
                    if ( ret3 > 0 ) {
 
5918
                        num_fixes ++;
 
5919
                        tot_num_fixes ++;   /* #1 */
 
5920
                        pCentp_found = NULL;
 
5921
                        continue;
 
5922
                    }
 
5923
                } else {
 
5924
                    pCentp_found->st_edge.cap -= delta;
 
5925
                    pBNS->tot_st_cap          -= delta;
 
5926
                }
 
5927
                if ( ret3 < 0 ) {
 
5928
                    ret = ret3;
 
5929
                    goto exit_function;
 
5930
                }
 
5931
            }
 
5932
            /*----------------------------------------------------------------------------------------
 
5933
               3rd attempt:                                                         add radical to keep
 
5934
              ==============           u,f=>unfixed, fixed edges                    (N electrons)%2
 
5935
                        (-)                       (-)                      (-)          |     
 
5936
                     e0/  \\ e1          =>     u/  \\u            =>     //  \         v     
 
5937
                      /    \\ ecp1 ecp2         /    \\   u    f         //    \             
 
5938
                  C--X*     Y(-)--C==Z      C--X*     Y(-)--C*--Z      --X(-)   Y===C---Z*   
 
5939
                    rad.    endp     not      rad.   endp  rad not      rad.   endp    not 
 
5940
                   Endp0   Endp1     endp     endp         to  endp     endp           endp
 
5941
                                    Endp2                cancel                              
 
5942
             
 
5943
               Note: endpoints X and Y may belong to different t-groups
 
5944
             ----------------------------------------------------------------------------------------*/
 
5945
            pCentp_found = NULL;
 
5946
            if ( i < num_endpoints ) {
 
5947
                int  e0, e1;
 
5948
                if ( (e0=pVA[endpoint0].nCMinusGroupEdge-1)<0 || pBNS->edge[e0].forbidden ) {
 
5949
                    continue; /* no negative charge on Endp0 is possible */
 
5950
                }
 
5951
                /* a radical-tautomeric endpoint found; traverse all endpoints */
 
5952
                for ( j = 0; j < pStruct->ti.nNumEndpoints; j ++ ) {
 
5953
                    if ( iEndpoint+i == j ) {
 
5954
                        continue; /* avoid the found radical endpoint */
 
5955
                    }
 
5956
 
 
5957
                    endpoint1 = pStruct->ti.nEndpointAtomNumber[j];
 
5958
                    pEndp1 = pBNS->vert + endpoint1;     /* another endpoint */
 
5959
 
 
5960
                    if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
 
5961
                        continue; /* one more radical-endpoint! What is going on here??? */
 
5962
                    }
 
5963
                    if ( ((e1=pVA[endpoint1].nCMinusGroupEdge-1)<0 || !pBNS->edge[e1].flow) || pBNS->edge[e1].forbidden ) {
 
5964
                        continue; /* no negative charge on Endp1 */
 
5965
                    }
 
5966
                    if ( !pStruct->endpoint[endpoint1] ) {
 
5967
                        continue; /* should not happen */
 
5968
                    }
 
5969
                    /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
 
5970
                    for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) {
 
5971
                        ecp1 = pBNS->edge + pEndp1->iedge[k];   /* e1C */
 
5972
                        if ( ecp1->flow || ecp1->forbidden ) {
 
5973
                            continue;
 
5974
                        }
 
5975
                        centerpoint = ecp1->neighbor12 ^ endpoint1;
 
5976
                        if ( centerpoint >= pBNS->num_atoms ) {
 
5977
                            break; /* no more edges to atoms */
 
5978
                        }
 
5979
                        pCentp = pBNS->vert + centerpoint;
 
5980
                        if ( pStruct->endpoint[centerpoint] ) {
 
5981
                            continue; /* do not set another endpoint's valence = an unusual value */
 
5982
                        }
 
5983
                        /* traverse centerpoint edges to find edge connecting it to the endpoint0 */
 
5984
                        /* 1. Find a double bond to a not endpoint */
 
5985
                        ecp2 = NULL;
 
5986
                        pEndp2 = NULL;
 
5987
                        for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) {
 
5988
                            ecp0 = pBNS->edge + pCentp->iedge[n];
 
5989
                            if ( ecp0->flow && !ecp0->forbidden ) {
 
5990
                                endpoint2 = ecp0->neighbor12 ^ centerpoint;
 
5991
                                if ( endpoint2 >= pBNS->num_atoms || pStruct->endpoint[endpoint2] ) {
 
5992
                                    continue;
 
5993
                                }
 
5994
                                /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */
 
5995
                                for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) {
 
5996
                                    if ( at[centerpoint].sb_ord[m] == n ) {
 
5997
                                        endpoint2 = NO_VERTEX;
 
5998
                                        break;
 
5999
                                    }
 
6000
                                }
 
6001
                                if ( endpoint2 == NO_VERTEX ) {
 
6002
                                    continue;
 
6003
                                }
 
6004
                                pEndp2 = pBNS->vert + endpoint2;
 
6005
                                ecp2   = ecp0;           /* e2C */
 
6006
                                break;
 
6007
                            }
 
6008
                        }
 
6009
                        if ( !ecp2 )
 
6010
                            continue;
 
6011
                        /* compare centerpoints */
 
6012
                        if ( !pCentp_found ||
 
6013
                             /* try to find carbons */
 
6014
                             !IS_C(endpoint2_found) && IS_C(endpoint2) ||
 
6015
                             IS_C(endpoint2_found) && IS_C(endpoint2) &&
 
6016
                             !IS_C(centerpoint_found) && IS_C(centerpoint) ) {
 
6017
 
 
6018
                            pCentp_found = pCentp;
 
6019
                            centerpoint_found = centerpoint;
 
6020
                            endpoint2_found   = endpoint2;
 
6021
                            ecp2_found   = ecp2;
 
6022
                            ecp1_found   = ecp1;
 
6023
                            ecp0_found   = pBNS->edge + e0;
 
6024
                            break;
 
6025
                        }
 
6026
                    }
 
6027
                }
 
6028
            }
 
6029
            /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */
 
6030
            /* result: radicals on Endp0 and Centp => run BNS */
 
6031
            if ( pCentp_found ) {
 
6032
                Vertex     vPathStart, vPathEnd, v1, v2;
 
6033
                int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
6034
                int        delta;
 
6035
 
 
6036
                Vertex    vEndp0 = ecp0_found->neighbor1;
 
6037
                Vertex    vEndp1 = ecp1_found->neighbor12 ^ centerpoint_found;
 
6038
                Vertex    vEndp2 = ecp2_found->neighbor12 ^ centerpoint_found;
 
6039
                BNS_EDGE *pe0 = ecp0_found;
 
6040
                BNS_EDGE *pe1 = pBNS->edge + (pVA[vEndp1].nCMinusGroupEdge - 1);
 
6041
                pEndp1 = pBNS->vert + vEndp1;
 
6042
                pEndp2 = pBNS->vert + vEndp2;
 
6043
                pCentp = pCentp_found;
 
6044
                if ( !ChargeEdgeList.num_alloc ) {
 
6045
                    for ( n = 0; n < pStruct->num_atoms; n ++ ) {
 
6046
                        if ( (k = pVA[n].nCMinusGroupEdge)>= 0 && !pBNS->edge[k].forbidden &&
 
6047
                             (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) {
 
6048
                            goto exit_function;
 
6049
                        }
 
6050
                        if ( (k = pVA[n].nCPlusGroupEdge)>= 0 && !pBNS->edge[k].forbidden &&
 
6051
                             (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) {
 
6052
                            goto exit_function;
 
6053
                        }
 
6054
                    }
 
6055
                }
 
6056
                if ( !BondEdgeList.num_alloc ) {
 
6057
                    for ( n = 0; n < pBNS->num_bonds; n ++ ) {
 
6058
                        if ( (ret = AddToEdgeList( &BondEdgeList, n, pBNS->num_bonds ) ) ) {
 
6059
                            goto exit_function;
 
6060
                        }
 
6061
                    }
 
6062
                }
 
6063
                /* fix all bonds and charges */
 
6064
                SetForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask );
 
6065
                SetForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask );
 
6066
                /* prepare flow for testing */
 
6067
                delta = 1;
 
6068
                ecp2_found->flow     -= delta;
 
6069
                pCentp->st_edge.flow -= delta;
 
6070
                pEndp2->st_edge.flow -= delta;
 
6071
                pBNS->tot_st_flow    -= 2*delta;
 
6072
                /* unfix edges to be changed */
 
6073
                pe0->forbidden        &= inv_forbidden_edge_mask;
 
6074
                pe1->forbidden        &= inv_forbidden_edge_mask;
 
6075
                ecp1_found->forbidden &= inv_forbidden_edge_mask;
 
6076
 
 
6077
                pBNS->tot_st_cap    += delta;
 
6078
 
 
6079
                v1 = vEndp0;
 
6080
                v2 = centerpoint_found;
 
6081
                ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
6082
                                      &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
6083
 
 
6084
                if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 ||
 
6085
                                   vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
 
6086
                    ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
6087
                    if ( ret2 > 0 ) {
 
6088
                        num_fixes ++;
 
6089
                        tot_num_fixes ++; /* #3 */
 
6090
                        pCentp_found = NULL;
 
6091
                    }
 
6092
                } else {
 
6093
                    /* roll back */
 
6094
                    ecp2_found->flow     += delta;
 
6095
                    pCentp->st_edge.flow += delta;
 
6096
                    pEndp2->st_edge.flow += delta;
 
6097
                    pBNS->tot_st_flow    += 2*delta;
 
6098
                }
 
6099
                RemoveForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask );
 
6100
                RemoveForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask );
 
6101
                if ( ret2 < 0 ) {
 
6102
                    ret = ret2;
 
6103
                    goto exit_function;
 
6104
                }
 
6105
                if ( !pCentp_found )
 
6106
                    continue;
 
6107
            }
 
6108
 
 
6109
        }
 
6110
        if ( !num_fixes ) {
 
6111
            break;
 
6112
        }
 
6113
    }
 
6114
 
 
6115
/************ again ***********************************************************/
 
6116
    while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) {
 
6117
        int iEndpoint = 0;
 
6118
        num_fixes = 0;
 
6119
        for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) {
 
6120
            pCentp_found=NULL;
 
6121
            tgroup_number = pStruct->ti.t_group[itg].nGroupNumber;
 
6122
            num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints;
 
6123
            for ( i = 0; i < num_endpoints; i ++ ) {
 
6124
                endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i];
 
6125
                pEndp0 = pBNS->vert + endpoint0;  /* taut endpoint vertex (possible location of mobile H */
 
6126
                if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) {
 
6127
                    /* radical endpoint1 has been detected */
 
6128
                    /* find a 1-3 centerpoint that has two or more endpoints */
 
6129
                    /* connected to the t-group vertex by edges with flow>0 and */
 
6130
                    /* to the centerpoint by edges with flow = 0 */
 
6131
                    /* after that: (1) increment etg1 flow to eliminate radical */
 
6132
                    /* (2) increment flow on one of the two other edges to the t-group */
 
6133
                    /* (3) increment st_cap on the found centerpoint */
 
6134
                    /* (4) rerun the BNS and re-create the structure */
 
6135
                    break;
 
6136
                }
 
6137
            }
 
6138
            /* 4th attempt */
 
6139
            if ( i < num_endpoints ) {
 
6140
                /* tautomeric endpoint found; traverse its t-group edges */
 
6141
                pEndp2_found = NULL;
 
6142
                for ( j = 0; j < pEndp0->num_adj_edges; j ++ ) {
 
6143
                    ecp0 = pBNS->edge + pEndp0->iedge[j];
 
6144
                    centerpoint = ecp0->neighbor12 ^ endpoint0;
 
6145
                    if ( centerpoint >= pBNS->num_atoms || ecp0->flow || pStruct->endpoint[centerpoint] ) {
 
6146
                        continue; /* ignore non-single bonds, orig. InChI endpoints, and fictitious atoms */
 
6147
                    }
 
6148
                    pCentp = pBNS->vert + centerpoint;
 
6149
                    for ( k = 0; k < pCentp->num_adj_edges; k ++ ) {
 
6150
                        ecp1 = pBNS->edge + pCentp->iedge[k];
 
6151
                        endpoint1 = ecp1->neighbor12 ^ centerpoint;
 
6152
                        if ( endpoint1 >= pBNS->num_atoms || !ecp1->flow || pStruct->endpoint[endpoint1] ) {
 
6153
                            continue; /* ignore single bonds, orig. InChI endpoints, and fictitious atoms */
 
6154
                        }
 
6155
                        pEndp1 = pBNS->vert + endpoint1;
 
6156
                        if ( endpoint1 == endpoint0 || pEndp1->st_edge.cap != pEndp1->st_edge.flow ) {
 
6157
                            continue; /* ignore radicals */
 
6158
                        }
 
6159
                        if ( !pEndp2_found ||
 
6160
                             /* try to find carbons */
 
6161
                             !IS_C(endpoint2_found) && IS_C(endpoint1) ||
 
6162
                             IS_C(endpoint2_found) && IS_C(endpoint1) &&
 
6163
                             !IS_C(centerpoint_found) && IS_C(centerpoint) ) {
 
6164
                            pEndp2_found      = pEndp1;
 
6165
                            pCentp_found      = pCentp;
 
6166
                            endpoint2_found   = endpoint1;
 
6167
                            centerpoint_found = centerpoint;
 
6168
                            ecp1_found        = ecp0;
 
6169
                            ecp2_found        = ecp1;
 
6170
                        }
 
6171
                    }
 
6172
                }
 
6173
                if ( pEndp2_found ) {
 
6174
                    /* move radical from pEndp0 to pEndp2 */
 
6175
                    pEndp0->st_edge.flow ++;
 
6176
                    ecp1_found->flow ++;
 
6177
                    ecp2_found->flow --;
 
6178
                    pEndp2_found->st_edge.flow --;
 
6179
                    pEndp2_found = NULL;
 
6180
                    pCentp_found = NULL;
 
6181
                    num_fixes ++;    /* #4 */
 
6182
                    tot_num_fixes ++;
 
6183
                    continue;
 
6184
                }
 
6185
            }
 
6186
        }
 
6187
        if ( !num_fixes ) {
 
6188
            break;
 
6189
        }
 
6190
    }
 
6191
 
 
6192
 
 
6193
    ret = tot_num_fixes;
 
6194
exit_function:
 
6195
    AllocEdgeList( &ChargeEdgeList, EDGE_LIST_FREE);
 
6196
    AllocEdgeList( &BondEdgeList, EDGE_LIST_FREE);
 
6197
 
 
6198
    pStruct->at = at;
 
6199
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
6200
    return ret;
 
6201
#undef IS_C
 
6202
}
 
6203
/************************************************************************************************/
 
6204
/* move (+) charges to >N- and other centerpoints                                               */
 
6205
int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
 
6206
                              inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
 
6207
                              int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
 
6208
{
 
6209
    int i, j, neigh, num_endpoints, tg_group=0, num_success;
 
6210
    int ret2, ret, delta;
 
6211
    int num_at = pStruct->num_atoms;
 
6212
    int num_deleted_H = pStruct->num_deleted_H;
 
6213
    int len_at = num_at + num_deleted_H;
 
6214
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
 
6215
 
 
6216
    /* for RunBnsTestOnce */
 
6217
    Vertex     vPathStart, vPathEnd;
 
6218
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
 
6219
 
 
6220
    BNS_EDGE   *pEdgePlus, *pEdgeMinus;
 
6221
    Vertex      v1p, v2p, v1m, v2m;
 
6222
    BNS_VERTEX *pv1p, *pv2p, *pv1m, *pv2m;
 
6223
 
 
6224
    ret = 0;
 
6225
    num_success = 0;
 
6226
    /* to simplify, prepare new at[] from pBNS */
 
6227
    memcpy( at2, at, len_at*sizeof(at2[0]));
 
6228
    pStruct->at = at2;
 
6229
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
6230
    pStruct->at = at;
 
6231
    if ( ret2 < 0 ) {
 
6232
        ret = ret2;
 
6233
        goto exit_function;
 
6234
    }
 
6235
 
 
6236
    for ( i = 0; i < num_at; i ++ ) {
 
6237
        if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */
 
6238
             !pVA[i].cMetal && !pVA[i].nTautGroupEdge &&
 
6239
             !at2[i].num_H && at2[i].valence >= 3 &&
 
6240
             at2[i].valence == at2[i].chem_bonds_valence &&
 
6241
             !at2[i].charge && pVA[i].nCPlusGroupEdge > 0 &&
 
6242
             is_centerpoint_elem( at2[i].el_number ) ) {
 
6243
            for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) {
 
6244
                neigh = at2[i].neighbor[j];
 
6245
                if ( at2[neigh].endpoint ) {
 
6246
                    if ( !num_endpoints ) {
 
6247
                        tg_group = at2[neigh].endpoint;
 
6248
                    } else
 
6249
                    if ( tg_group != at2[neigh].endpoint ) {
 
6250
                        break; /* not a centerpoint */
 
6251
                    }
 
6252
                    num_endpoints ++;
 
6253
                }
 
6254
            }
 
6255
            if ( j == at2[i].valence && num_endpoints > 1 ) {
 
6256
                /* found possible centerpoint */
 
6257
                pEdgePlus  = pBNS->edge + (pVA[i].nCPlusGroupEdge-1);
 
6258
                pEdgeMinus = (pVA[i].nCMinusGroupEdge > 0)? pBNS->edge + (pVA[i].nCMinusGroupEdge-1) : NULL;
 
6259
                if ( pEdgePlus->flow + (pEdgeMinus? pEdgeMinus->flow : 0) != 1 ) {
 
6260
                    continue;
 
6261
                }
 
6262
                v1p = pEdgePlus->neighbor1;
 
6263
                v2p = pEdgePlus->neighbor12 ^ v1p;
 
6264
                pv1p = pBNS->vert + v1p;
 
6265
                pv2p = pBNS->vert + v2p;
 
6266
                if ( pEdgeMinus ) {
 
6267
                    v1m  = pEdgeMinus->neighbor1;
 
6268
                    v2m  = pEdgeMinus->neighbor12 ^ v1m;
 
6269
                    pv1m = pBNS->vert + v1m;
 
6270
                    pv2m = pBNS->vert + v2m;
 
6271
                } else {
 
6272
                    v1m = NO_VERTEX;
 
6273
                    v2m = NO_VERTEX;
 
6274
                    pv1m = NULL;
 
6275
                    pv2m = NULL;
 
6276
                }
 
6277
                ret = 0;
 
6278
                /* set new flow to run BNS Search */
 
6279
                if ( delta = pEdgePlus->flow ) {
 
6280
                    /* positive charge <=> flow=0 on (=) edge */
 
6281
                    pEdgePlus->flow -= delta;
 
6282
                    pv1p->st_edge.flow -= delta;
 
6283
                    pv2p->st_edge.flow -= delta;
 
6284
                    pBNS->tot_st_flow  -= 2*delta;
 
6285
                    pEdgePlus->forbidden |= forbidden_edge_mask;
 
6286
                    if ( pEdgeMinus ) {
 
6287
                        pEdgeMinus->forbidden |= forbidden_edge_mask;
 
6288
                    }
 
6289
                    ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
6290
                                          &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
6291
                    if ( ret < 0 ) {
 
6292
                        goto exit_function;
 
6293
                    }
 
6294
                    if ( ret == 1 && (vPathEnd == v1p && vPathStart == v2p ||
 
6295
                                      vPathEnd == v2p && vPathStart == v1p) &&
 
6296
                                      nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) {
 
6297
                        ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
6298
                        (*pnNumRunBNS) ++;
 
6299
                        if ( ret < 0 ) {
 
6300
                            goto exit_function;
 
6301
                        } else
 
6302
                        if ( ret == 1 ) {
 
6303
                            *pnTotalDelta += ret;
 
6304
                        } else {
 
6305
                            ret = RI_ERR_PROGR;
 
6306
                            goto exit_function;
 
6307
                        }
 
6308
                    } else {
 
6309
                        ret = 0;
 
6310
                        pEdgePlus->flow    += delta;
 
6311
                        pv1p->st_edge.flow += delta;
 
6312
                        pv2p->st_edge.flow += delta;
 
6313
                        pBNS->tot_st_flow  += 2*delta;
 
6314
                    }
 
6315
                    pEdgePlus->forbidden &= inv_forbidden_edge_mask;
 
6316
                    if ( pEdgeMinus ) {
 
6317
                        pEdgeMinus->forbidden &= inv_forbidden_edge_mask;
 
6318
                    }
 
6319
                } else
 
6320
                if ( pEdgeMinus && (delta == pEdgeMinus->flow) && pEdgePlus->flow == 0 ) {
 
6321
                    /* positive charge <=> flow=0 on (=) edge and flow=0 on (-) edge */
 
6322
                    pEdgeMinus->flow -= delta;
 
6323
                    pv1m->st_edge.flow -= delta;
 
6324
                    pv2m->st_edge.flow -= delta;
 
6325
                    pBNS->tot_st_flow  -= 2*delta;
 
6326
                    pEdgePlus->forbidden  |= forbidden_edge_mask;
 
6327
                    pEdgeMinus->forbidden |= forbidden_edge_mask;
 
6328
                    ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
 
6329
                                          &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
 
6330
                    if ( ret < 0 ) {
 
6331
                        goto exit_function;
 
6332
                    }
 
6333
                    if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m ||
 
6334
                                      vPathEnd == v2m && vPathStart == v1m) &&
 
6335
                                      nDeltaCharge == -1  /* charge moving to this atom disappers*/ ) {
 
6336
                        ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
 
6337
                        (*pnNumRunBNS) ++;
 
6338
                        if ( ret < 0 ) {
 
6339
                            goto exit_function;
 
6340
                        } else
 
6341
                        if ( ret == 1 ) {
 
6342
                            *pnTotalDelta += ret;
 
6343
                        } else {
 
6344
                            ret = RI_ERR_PROGR;
 
6345
                            goto exit_function;
 
6346
                        }
 
6347
                    } else {
 
6348
                        ret = 0;
 
6349
                        pEdgeMinus->flow   += delta;
 
6350
                        pv1m->st_edge.flow += delta;
 
6351
                        pv2m->st_edge.flow += delta;
 
6352
                        pBNS->tot_st_flow  += 2*delta;
 
6353
                    }
 
6354
                    pEdgePlus->forbidden  &= inv_forbidden_edge_mask;
 
6355
                    pEdgeMinus->forbidden &= inv_forbidden_edge_mask;
 
6356
                }
 
6357
                if ( ret ) {
 
6358
                    num_success ++;
 
6359
                    memcpy( at2, at, len_at*sizeof(at2[0]));
 
6360
                    pStruct->at = at2;
 
6361
                    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
 
6362
                    pStruct->at = at;
 
6363
                    if ( ret2 < 0 ) {
 
6364
                        ret = ret2;
 
6365
                        goto exit_function;
 
6366
                    }
 
6367
                }
 
6368
            }
 
6369
        }
 
6370
    }
 
6371
    ret = num_success;
 
6372
exit_function:
 
6373
    return ret;
 
6374
}
 
6375
#endif