Coverage Report

Created: 2026-09-14 07:06

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