Coverage Report

Created: 2026-05-30 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/inchi/INCHI-1-SRC/INCHI_BASE/src/ichirvr4.c
Line
Count
Source
1
/*
2
 * International Chemical Identifier (InChI)
3
 * Version 1
4
 * Software version 1.07
5
 * April 30, 2024
6
 *
7
 * MIT License
8
 *
9
 * Copyright (c) 2024 IUPAC and InChI Trust
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in all
19
 * copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
 * SOFTWARE.
28
*
29
* The InChI library and programs are free software developed under the
30
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
31
 * Originally developed at NIST.
32
 * Modifications and additions by IUPAC and the InChI Trust.
33
 * Some portions of code were developed/changed by external contributors
34
 * (either contractor or volunteer) which are listed in the file
35
 * 'External-contributors' included in this distribution.
36
 *
37
 * info@inchi-trust.org
38
 *
39
*/
40
41
#include <string.h>
42
43
/*#define CHECK_WIN32_VC_HEAP*/
44
45
#include "mode.h"
46
47
#if ( READ_INCHI_STRING == 1 )
48
#include "ichitime.h"
49
#include "ichirvrs.h"
50
#include "ichicant.h"
51
#include "ichi_io.h"
52
#include "ichimake.h"
53
54
#include "bcf_s.h"
55
56
/****************************************************************************/
57
int ForbidCarbonChargeEdges(BN_STRUCT* pBNS,
58
    ALL_TC_GROUPS* pTCGroups,
59
    EDGE_LIST* pCarbonChargeEdges,
60
    int forbidden_edge_mask)
61
19.5k
{
62
78.1k
#define MAX_NUM_CARBON_CHARGE_EDGES 2
63
19.5k
    int nType, i, k, ret;
64
19.5k
    BNS_EDGE* pEdge;
65
19.5k
    if ((ret = AllocEdgeList(pCarbonChargeEdges, MAX_NUM_CARBON_CHARGE_EDGES))) /* djb-rwth: addressing LLVM warning */
66
0
    {
67
0
        goto exit_function;
68
0
    }
69
19.5k
    pCarbonChargeEdges->num_edges = 0;
70
58.5k
    for (i = 0; i < MAX_NUM_CARBON_CHARGE_EDGES; i++)
71
39.0k
    {
72
39.0k
        switch (i)
73
39.0k
        {
74
19.5k
        case 0:
75
19.5k
            nType = TCG_Plus_C0;
76
19.5k
            break;
77
19.5k
        case 1:
78
19.5k
            nType = TCG_Minus_C0;
79
19.5k
            break;
80
0
        default:
81
0
            ret = RI_ERR_PROGR;
82
0
            goto exit_function;
83
39.0k
        }
84
39.0k
        if ((k = pTCGroups->nGroup[nType]) >= 0)
85
1.35k
        {
86
1.35k
            k = pTCGroups->pTCG[k].nForwardEdge;
87
1.35k
            if (k > 0)
88
1.35k
            {
89
1.35k
                pEdge = pBNS->edge + k;
90
1.35k
                if (!(pEdge->forbidden & forbidden_edge_mask))
91
1.35k
                {
92
1.35k
                    pEdge->forbidden |= forbidden_edge_mask;
93
1.35k
                    if ((ret = AddToEdgeList(pCarbonChargeEdges, k, 0))) /* djb-rwth: addressing LLVM warning */
94
0
                    {
95
0
                        goto exit_function;
96
0
                    }
97
1.35k
                }
98
1.35k
            }
99
0
            else
100
0
            {
101
0
                ret = RI_ERR_PROGR;
102
0
                goto exit_function;
103
0
            }
104
1.35k
        }
105
39.0k
    }
106
19.5k
    ret = pCarbonChargeEdges->num_edges;
107
108
19.5k
exit_function:
109
110
19.5k
    return ret;
111
19.5k
#undef MAX_NUM_CARBON_CHARGE_EDGES
112
19.5k
}
113
114
115
/****************************************************************************/
116
int ForbidNintrogenPlus2BondsInSmallRings(BN_STRUCT* pBNS,
117
    inp_ATOM* at,
118
    int num_at,
119
    VAL_AT* pVA,
120
    int min_ring_size,
121
    ALL_TC_GROUPS* pTCGroups,
122
    EDGE_LIST* pNplus2BondsEdges,
123
    int forbidden_edge_mask)
124
37.0k
{
125
37.0k
    int i, j, ret;
126
37.0k
    BNS_EDGE* e;
127
128
    /* djb-rwth: removing redundant code */
129
        /* --- forbid edges that allow to make =N(+)= or #N(+)- in small ring */
130
288k
    for (i = 0; i < num_at; i++)
131
251k
    {
132
251k
        if (at[i].valence == 2 &&
133
4.96k
            !at[i].num_H && !at[i].endpoint &&
134
4.10k
            pVA[i].cNumValenceElectrons == 5 &&
135
1.76k
            pVA[i].cPeriodicRowNumber == 1 &&
136
1.62k
            !pVA[i].cMaxFlowToMetal && pVA[i].nCPlusGroupEdge > 0 &&
137
251k
            pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex - 1].bits == cn_bits_MNP &&
138
1.50k
            pVA[i].cMinRingSize && pVA[i].cMinRingSize <= min_ring_size)
139
730
        {
140
141
730
            e = pBNS->edge + (j = pVA[i].nCPlusGroupEdge - 1);
142
730
            if (!(e->forbidden & forbidden_edge_mask))
143
730
            {
144
730
                e->forbidden |= forbidden_edge_mask;
145
730
                if ((ret = AddToEdgeList(pNplus2BondsEdges, j, 128))) /* djb-rwth: addressing LLVM warning */
146
0
                {
147
0
                    goto exit_function;
148
0
                }
149
730
            }
150
730
        }
151
251k
    }
152
37.0k
    ret = 0;
153
154
37.0k
exit_function:
155
156
37.0k
    return ret;
157
37.0k
}
158
159
160
/****************************************************************************
161
Problem: Formula in InChI from the reversed structure has
162
less H than in the input InChI
163
Solutions:
164
165
(a)   |                        |
166
     -B(-)-NH-=..-=N(+)<   => -B(-)-NH(+)=-..=-N<
167
      |                        |
168
169
                                (H is not removed from the ion pair)
170
171
                  |                      |
172
(b)  >N(+)=-=...-=N-NH     =>  >N-=-...=-N(+)-NH
173
                  |                      |
174
175
                                (charge from onium cannot be moved to remove H+)
176
****************************************************************************/
177
int FixLessHydrogenInFormula(BN_STRUCT* pBNS,
178
    BN_DATA* pBD,
179
    StrFromINChI* pStruct,
180
    inp_ATOM* at,
181
    inp_ATOM* at2,
182
    inp_ATOM* atf,
183
    VAL_AT* pVA,
184
    ALL_TC_GROUPS* pTCGroups,
185
    int* pnNumRunBNS,
186
    int* pnTotalDelta, int forbidden_edge_mask)
187
19
{
188
19
    int iBPlus = NO_VERTEX, iNV = NO_VERTEX, iNH = NO_VERTEX, neigh;
189
19
    EDGE_LIST NewlyFixedEdges;
190
19
    int ret, i, j;
191
19
    int num_at = pStruct->num_atoms;
192
19
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
193
    /* for RunBnsTestOnce */
194
19
    Vertex     vPathStart, vPathEnd;
195
19
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
196
197
19
    AllocEdgeList(&NewlyFixedEdges, EDGE_LIST_CLEAR);
198
19
    if ((ret = AllocEdgeList(&NewlyFixedEdges, 2 * num_at))) /* djb-rwth: addressing LLVM warning */
199
0
    {
200
0
        goto exit_function;
201
0
    }
202
171
    for (i = 0; i < num_at; i++)
203
152
    {
204
152
        if ((j = pVA[i].nCMinusGroupEdge - 1) >= 0)
205
64
        {
206
64
            if ((ret = AddToEdgeList(&NewlyFixedEdges, j, 0))) /* djb-rwth: addressing LLVM warning */
207
0
            {
208
0
                goto exit_function;
209
0
            }
210
64
            pBNS->edge[j].forbidden |= forbidden_edge_mask;
211
64
        }
212
152
        if ((j = pVA[i].nCPlusGroupEdge - 1) >= 0)
213
124
        {
214
124
            if ((ret = AddToEdgeList(&NewlyFixedEdges, j, 0))) /* djb-rwth: addressing LLVM warning */
215
0
            {
216
0
                goto exit_function;
217
0
            }
218
124
            pBNS->edge[j].forbidden |= forbidden_edge_mask;
219
124
        }
220
152
    }
221
    /* extra H has been removed; check non-tautomeric atoms */
222
118
    for (i = 0; i < num_at; i++)
223
109
    {
224
109
        if (!at2[i].endpoint && !pVA[i].cMetal &&
225
100
            pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 &&
226
34
            at2[i].num_H == atf[i].num_H + 1)
227
10
        {
228
            /* H was removed from N */
229
10
            iNH = i;
230
10
            break;
231
10
        }
232
109
    }
233
19
    if (0 <= iNH && iNH < num_at)
234
10
    {
235
        /* check neighbors for  |                 |
236
                          (a)  -B(+)-  or  (b)   =N-
237
                                |                 |
238
        */
239
30
        for (j = 0; j < at2[i].valence; j++)
240
20
        {
241
20
            neigh = at2[iNH].neighbor[j];
242
20
            if (at2[neigh].valence == 4)
243
0
            {
244
0
                if (at2[neigh].charge == -1 && at2[neigh].chem_bonds_valence == 4 &&
245
0
                    !at2[neigh].radical && !at[neigh].num_H)
246
0
                {
247
0
                    iBPlus = neigh;
248
0
                }
249
0
            }
250
20
        }
251
10
    }
252
19
    if (0 <= iNH && iNH < num_at)
253
10
    {
254
10
        int bond_type_at2;
255
10
        int bond_type_atf;
256
        /* djb-rwth: removing redundant variables */
257
10
        int delta = -1, nxt = iNH, prv = NO_VERTEX, nxt_is_NPlus;
258
        /* the changed bond to the dehydrogenated atom H should have greater order */
259
        /* delta = (new bond order in atf[]) - (restored bond order in at2[]) */
260
10
        nxt_is_NPlus = 0;
261
10
        do
262
18
        {
263
18
            i = nxt;
264
18
            nxt = NO_VERTEX;
265
18
            delta = -delta;
266
40
            for (j = 0; j < at2[i].valence; j++)
267
32
            {
268
32
                bond_type_at2 = at2[i].bond_type[j] & BOND_TYPE_MASK; /* restored bond */
269
32
                bond_type_atf = atf[i].bond_type[j] & BOND_TYPE_MASK; /* normalized bond */
270
32
                nxt_is_NPlus = 0;
271
32
                if ((bond_type_atf - bond_type_at2 == delta || bond_type_atf == BOND_ALT12NS) &&
272
10
                    BOND_TYPE_SINGLE <= bond_type_at2 + delta && bond_type_at2 + delta <= BOND_TYPE_TRIPLE &&
273
10
                    !at2[(int)at2[i].neighbor[j]].cFlags)
274
10
                {
275
10
                    prv = i;
276
10
                    nxt = at2[i].neighbor[j];
277
10
                    nxt_is_NPlus = at2[nxt].charge == 1 && atf[nxt].charge == 0 &&
278
2
                        pVA[nxt].cNumValenceElectrons == 5 && pVA[nxt].cPeriodicRowNumber == 1;
279
10
                    at2[i].cFlags |= 1;  /* avoid cycling */
280
                    /* djb-rwth: removing redundant code */
281
10
                    if (delta == -1 && at2[prv].valence == 4 && at2[prv].chem_bonds_valence == 5 &&
282
0
                        !at2[prv].charge && !at2[prv].radical && pVA[prv].cNumValenceElectrons == 5 &&
283
0
                        pVA[prv].nCPlusGroupEdge > 0)
284
0
                    {
285
0
                        iNV = prv;
286
0
                    }
287
10
                    if (at2[nxt].charge != atf[nxt].charge)
288
2
                    {
289
2
                        if ((at2[nxt].charge == 1 || atf[nxt].charge == 1) &&
290
2
                            pVA[nxt].nCPlusGroupEdge > 0)
291
2
                        {
292
2
                            pBNS->edge[pVA[nxt].nCPlusGroupEdge - 1].forbidden &= inv_forbidden_edge_mask;
293
2
                        }
294
2
                        if ((at2[nxt].charge == -1 || atf[nxt].charge == -1) &&
295
0
                            pVA[nxt].nCMinusGroupEdge > 0)
296
0
                        {
297
0
                            pBNS->edge[pVA[nxt].nCMinusGroupEdge - 1].forbidden &= inv_forbidden_edge_mask;
298
0
                        }
299
2
                    }
300
10
                    break; /* found */
301
10
                }
302
32
            }
303
18
        } while (nxt >= 0 && !(nxt_is_NPlus && delta == -1));
304
90
        for (i = 0; i < num_at; i++)
305
80
        {
306
80
            at2[i].cFlags = 0;
307
80
        }
308
10
        if (nxt >= 0 && nxt_is_NPlus && delta == -1)
309
2
        {
310
            /* a simple alt path from NH-= to =N(+) has been found */
311
2
            if (iBPlus || iNV)
312
2
            {
313
                /* move (+) charge from N(+) to iNV or, if iBPlus, then to iNH */
314
2
                if ((iNV >= 0 && (j = pVA[iNV].nCPlusGroupEdge - 1) > 0 && pBNS->edge[j].flow > 0) ||
315
2
                    (iNH >= 0 && (j = pVA[iNH].nCPlusGroupEdge - 1) > 0 && pBNS->edge[j].flow > 0)) /* djb-rwth: addressing LLVM warnings */
316
1
                {
317
1
                    int          ieFlower;
318
1
                    BNS_EDGE* pe = pBNS->edge + j, * peFlower = NULL;
319
1
                    Vertex      v1 = pe->neighbor1;
320
1
                    Vertex      v2 = v1 ^ pe->neighbor12;
321
1
                    BNS_VERTEX* pv1 = pBNS->vert + v1;
322
1
                    BNS_VERTEX* pv2 = pBNS->vert + v2;
323
324
1
                    delta = 1;
325
                    /* prevent conversion of >N(+)= into N(V) neutral */
326
1
                    ieFlower = GetChargeFlowerUpperEdge(pBNS, pVA, pVA[nxt].nCPlusGroupEdge - 1);
327
1
                    if (ieFlower >= 0)
328
1
                    {
329
1
                        peFlower = pBNS->edge + ieFlower;
330
1
                        if (peFlower->flow == delta)
331
1
                        {
332
1
                            peFlower->forbidden |= forbidden_edge_mask;
333
1
                            if ((ret = AddToEdgeList(&NewlyFixedEdges, ieFlower, 0))) /* djb-rwth: addressing LLVM warning */
334
0
                            {
335
0
                                goto exit_function;
336
0
                            }
337
1
                        }
338
1
                    }
339
1
                    pe->forbidden |= forbidden_edge_mask;
340
1
                    pe->flow -= delta;
341
1
                    pv1->st_edge.flow -= delta;
342
1
                    pv2->st_edge.flow -= delta;
343
1
                    pBNS->tot_st_flow -= 2 * delta;
344
1
                    ret = RunBnsTestOnce(pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
345
1
                        &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms);
346
1
                    if (ret < 0)
347
0
                    {
348
0
                        goto exit_function;
349
0
                    }
350
1
                    if (ret == 1 && ((vPathEnd == v1 && vPathStart == v2) ||
351
1
                        (vPathEnd == v2 && vPathStart == v1)) &&
352
1
                        nDeltaCharge <= 0  /* charge moving to this atom disappers*/) /* djb-rwth: addressing LLVM warnings */
353
1
                    {
354
1
                        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
355
1
                        (*pnNumRunBNS)++;
356
1
                        if (ret < 0)
357
0
                        {
358
0
                            goto exit_function;
359
0
                        }
360
1
                        else
361
1
                            if (ret == 1)
362
1
                            {
363
1
                                *pnTotalDelta += ret;
364
1
                            }
365
0
                            else
366
0
                            {
367
0
                                ret = RI_ERR_PROGR;
368
0
                                goto exit_function;
369
0
                            }
370
1
                    }
371
0
                    else
372
0
                    {
373
0
                        ret = 0;
374
0
                        pe->flow += delta;
375
0
                        pv1->st_edge.flow += delta;
376
0
                        pv2->st_edge.flow += delta;
377
0
                        pBNS->tot_st_flow += 2 * delta;
378
0
                    }
379
1
                }
380
2
            }
381
2
        }
382
10
    }
383
384
19
exit_function:
385
    /* remove bond fixation */
386
19
    RemoveForbiddenEdgeMask(pBNS, &NewlyFixedEdges, forbidden_edge_mask);
387
19
    AllocEdgeList(&NewlyFixedEdges, EDGE_LIST_FREE);
388
389
19
    return ret;
390
19
}
391
392
393
/****************************************************************************
394
395
    X=Y-O(-)  => X(-)-Y=O
396
397
****************************************************************************/
398
int FixMoreHydrogenInFormula(BN_STRUCT* pBNS,
399
    BN_DATA* pBD,
400
    StrFromINChI* pStruct,
401
    inp_ATOM* at,
402
    inp_ATOM* at2,
403
    inp_ATOM* atf,
404
    VAL_AT* pVA,
405
    ALL_TC_GROUPS* pTCGroups,
406
    int* pnNumRunBNS,
407
    int* pnTotalDelta,
408
    int forbidden_edge_mask)
409
136
{
410
136
    int iNH = NO_VERTEX, neigh, neigh2;
411
136
    EDGE_LIST NewlyFixedEdges;
412
136
    int ret, i, j, k, k2 = 0, delta;
413
136
    int num_at = pStruct->num_atoms;
414
136
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
415
136
    Vertex v1, v2;
416
    /* for RunBnsTestOnce */
417
136
    Vertex     vPathStart, vPathEnd;
418
136
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
419
136
    BNS_EDGE* pe, * pe2;
420
421
136
    AllocEdgeList(&NewlyFixedEdges, EDGE_LIST_CLEAR);
422
136
    if ((ret = AllocEdgeList(&NewlyFixedEdges, 2 * num_at))) /* djb-rwth: addressing LLVM warning */
423
0
    {
424
0
        goto exit_function;
425
0
    }
426
    /* fix all charges */
427
1.19k
    for (i = 0; i < num_at; i++)
428
1.06k
    {
429
1.06k
        if ((j = pVA[i].nCMinusGroupEdge - 1) >= 0)
430
835
        {
431
835
            if ((ret = AddToEdgeList(&NewlyFixedEdges, j, 0))) /* djb-rwth: addressing LLVM warning */
432
0
            {
433
0
                goto exit_function;
434
0
            }
435
835
            pBNS->edge[j].forbidden |= forbidden_edge_mask;
436
835
        }
437
1.06k
        if ((j = pVA[i].nCPlusGroupEdge - 1) >= 0)
438
820
        {
439
820
            if ((ret = AddToEdgeList(&NewlyFixedEdges, j, 0))) /* djb-rwth: addressing LLVM warning */
440
0
            {
441
0
                goto exit_function;
442
0
            }
443
820
            pBNS->edge[j].forbidden |= forbidden_edge_mask;
444
820
        }
445
1.06k
    }
446
447
    /* H(+) has been added to -O(-); check non-tautomeric atoms */
448
1.16k
    for (i = 0; i < num_at; i++)
449
1.03k
    {
450
1.03k
        neigh = at2[i].neighbor[0]; /* djb-rwth: avoiding unsequenced modification and access to neigh */
451
1.03k
        if (!(pStruct->bMobileH ? at2[i].endpoint : pStruct->endpoint[i]) && !pVA[i].cMetal &&
452
598
            at2[i].num_H + 1 == atf[i].num_H &&      /* normalization added H ??? What would happen in Fixed-H case?*/
453
127
            (k = pVA[i].nCMinusGroupEdge - 1) >= 0 &&
454
127
            pBNS->edge[k].flow == 1 &&               /* atom had (-) charge before preprocessing */
455
126
            at2[i].charge == -1 && atf[i].charge == 0 && /* and has no charge after preprocessing */
456
126
            at2[i].valence == 1 && at2[i].chem_bonds_valence == 1 && /* connected by a single bond */
457
126
            pVA[i].cNumValenceElectrons == 6 &&     /* atom is O, S, Se, Te */
458
126
            at2[neigh].chem_bonds_valence > at2[neigh].valence
459
            /* atom's single neighbor has multiple bond(s)*/
460
1.03k
            )
461
104
        {
462
            /* H(+) was added to O in Y=X-O(-), where X is the only neighbor of O, X=neigh, Y=neigh2 */
463
104
            iNH = i;
464
172
            for (j = 0; j < at2[neigh].valence; j++)
465
151
            {
466
151
                neigh2 = at2[neigh].neighbor[j];
467
151
                if (neigh2 != iNH && !at2[neigh2].endpoint &&
468
122
                    !pBNS->edge[(int)pBNS->vert[neigh].iedge[j]].forbidden &&
469
122
                    4 <= pVA[neigh2].cNumValenceElectrons &&
470
116
                    pVA[neigh2].cNumValenceElectrons <= 5 && /* neig2 is C or N */
471
98
                    (k2 = pVA[neigh2].nCMinusGroupEdge - 1) >= 0 &&
472
91
                    !pBNS->edge[k2].flow /* negative charge may be moved to neigh2 */)
473
83
                {
474
83
                    break;
475
83
                }
476
151
            }
477
104
            if (j < at2[neigh].valence)
478
83
            {
479
83
                delta = 1;
480
83
                pe = pBNS->edge + k;  /* -O(-) negative charge edge; flow = 1 */
481
83
                pe2 = pBNS->edge + k2; /* X charge edge; flow = 0 */
482
83
                v1 = pe->neighbor1;
483
83
                v2 = pe->neighbor12 ^ v1;
484
83
                pe->flow -= delta;
485
83
                pBNS->vert[v1].st_edge.flow -= delta;
486
83
                pBNS->vert[v2].st_edge.flow -= delta;
487
83
                pBNS->tot_st_flow -= 2 * delta;
488
83
                pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */
489
490
83
                ret = RunBnsTestOnce(pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
491
83
                    &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms);
492
83
                if (ret < 0)
493
0
                {
494
0
                    goto exit_function;
495
0
                }
496
83
                if (ret == 1 && ((vPathEnd == v1 && vPathStart == v2) ||
497
11
                    (vPathEnd == v2 && vPathStart == v1)) &&
498
10
                    nDeltaCharge <= 1) /* djb-rwth: addressing LLVM warnings */
499
10
                {
500
10
                    ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
501
10
                    (*pnNumRunBNS)++;
502
10
                    if (ret < 0)
503
0
                    {
504
0
                        goto exit_function;
505
0
                    }
506
10
                    else
507
10
                        if (ret)
508
10
                        {
509
10
                            *pnTotalDelta += ret;
510
10
                        }
511
0
                        else
512
0
                        {
513
0
                            ret = RI_ERR_PROGR;
514
0
                        }
515
10
                    break;
516
10
                }
517
73
                else
518
73
                {
519
                    /* the attempt has failed; restore the flow */
520
73
                    ret = 0;
521
73
                    pe->flow += delta;
522
73
                    pBNS->vert[v1].st_edge.flow += delta;
523
73
                    pBNS->vert[v2].st_edge.flow += delta;
524
73
                    pBNS->tot_st_flow += 2 * delta;
525
73
                }
526
83
            }
527
104
        }
528
1.03k
    }
529
530
136
exit_function:
531
    /* remove bond fixation */
532
136
    RemoveForbiddenEdgeMask(pBNS, &NewlyFixedEdges, forbidden_edge_mask);
533
136
    AllocEdgeList(&NewlyFixedEdges, EDGE_LIST_FREE);
534
535
136
    return ret;
536
136
}
537
538
539
#if ( FIX_ADD_PROTON_FOR_ADP == 1 )
540
/****************************************************************************/
541
int FixAddProtonForADP(BN_STRUCT* pBNS,
542
    BN_DATA* pBD,
543
    StrFromINChI* pStruct,
544
    inp_ATOM* at,
545
    inp_ATOM* at2,
546
    inp_ATOM* atf,
547
    VAL_AT* pVA,
548
    ALL_TC_GROUPS* pTCGroups,
549
    ICR* picr,
550
    int* pnNumRunBNS,
551
    int* pnTotalDelta,
552
    int forbidden_edge_mask)
553
{
554
    int iBPlus = NO_VERTEX, iNV = NO_VERTEX, iNH = NO_VERTEX, neigh, neigh2;
555
    EDGE_LIST NewlyFixedEdges;
556
    int ret, i, j, k, k2, delta;
557
    int num_at = pStruct->num_atoms;
558
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
559
    Vertex v1, v2;
560
    /* for RunBnsTestOnce */
561
    Vertex     vPathStart, vPathEnd;
562
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
563
    BNS_EDGE* pe, * pe2;
564
565
    ret = 0;
566
    /*
567
    AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
568
569
    for ( i = 0; i < num_at; i ++ ) {
570
        if ( at2[i].radical == RADICAL_DOUBLET && at2[i].endpoint ) {
571
            pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;
572
            ret = 1;
573
            break;
574
        }
575
    }
576
    */
577
    return ret;
578
}
579
#endif
580
581
582
/****************************************************************************
583
      OH              OH
584
     /               /
585
  -NH      =>   -NH(+)        to eliminate false tautomerism.
586
     \\              \        S(IV) or N(V) or P(V) may be a centerpoint
587
      O               O(-)
588
****************************************************************************/
589
int FixRemoveExtraTautEndpoints(BN_STRUCT* pBNS,
590
    BN_DATA* pBD,
591
    StrFromINChI* pStruct,
592
    inp_ATOM* at,
593
    inp_ATOM* at2,
594
    inp_ATOM* atf,
595
    inp_ATOM* atn,
596
    VAL_AT* pVA,
597
    ALL_TC_GROUPS* pTCGroups, ICR* picr,
598
    int* pnNumRunBNS,
599
    int* pnTotalDelta,
600
    int forbidden_edge_mask)
601
169
{
602
169
    EDGE_LIST NewlyFixedEdges;
603
169
    int ret, i, j, k, delta, centerpoint, endpoint1, endpoint2;
604
169
    int num_at = pStruct->num_atoms;
605
169
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
606
169
    Vertex v1, v2;
607
    /* for RunBnsTestOnce */
608
169
    Vertex     vPathStart, vPathEnd;
609
169
    int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
610
169
    BNS_EDGE* pe, * pe2;
611
612
169
    ret = 0; /* djb-rwth: ignoring LLVM warning: value might be returned */
613
614
169
    AllocEdgeList(&NewlyFixedEdges, EDGE_LIST_CLEAR);
615
169
    if ((ret = AllocEdgeList(&NewlyFixedEdges, 2 * num_at))) /* djb-rwth: addressing LLVM warning */
616
0
    {
617
0
        goto exit_function;
618
0
    }
619
    /* fix all charges */
620
1.50k
    for (i = 0; i < num_at; i++)
621
1.33k
    {
622
1.33k
        if ((j = pVA[i].nCMinusGroupEdge - 1) >= 0)
623
978
        {
624
978
            if ((ret = AddToEdgeList(&NewlyFixedEdges, j, 0))) /* djb-rwth: addressing LLVM warning */
625
0
            {
626
0
                goto exit_function;
627
0
            }
628
978
            pBNS->edge[j].forbidden |= forbidden_edge_mask;
629
978
        }
630
1.33k
        if ((j = pVA[i].nCPlusGroupEdge - 1) >= 0)
631
1.10k
        {
632
1.10k
            if ((ret = AddToEdgeList(&NewlyFixedEdges, j, 0))) /* djb-rwth: addressing LLVM warning */
633
0
            {
634
0
                goto exit_function;
635
0
            }
636
1.10k
            pBNS->edge[j].forbidden |= forbidden_edge_mask;
637
1.10k
        }
638
1.33k
    }
639
640
497
    for (i = 0; i < picr->num_endp_in1_only; i++)
641
341
    {
642
341
        endpoint1 = picr->endp_in1_only[i] - 1;
643
341
        if (at2[endpoint1].valence == at2[endpoint1].chem_bonds_valence ||
644
163
            pVA[endpoint1].nCMinusGroupEdge <= 0)
645
182
        {
646
182
            continue;
647
182
        }
648
        /* find centerpoint */
649
353
        for (j = 0; j < at2[endpoint1].valence; j++)
650
207
        {
651
207
            if (BOND_TYPE_DOUBLE == (BOND_TYPE_MASK & at2[endpoint1].bond_type[j]))
652
159
            {
653
159
                centerpoint = at2[endpoint1].neighbor[j];
654
159
                if (at2[centerpoint].charge || pVA[centerpoint].nCPlusGroupEdge <= 0 ||
655
136
                    !is_centerpoint_elem(at2[centerpoint].el_number))
656
23
                {
657
23
                    continue;
658
23
                }
659
                /* -- the centerpoint as depicted has no ChargeStruct flower ---
660
                m = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[centerpoint].nCPlusGroupEdge-1 );
661
                if ( m < 0 || pBNS->edge[m].flow ) {
662
                    continue;
663
                }
664
                */
665
                /* find 2nd endpoint */
666
518
                for (k = 0; k < at2[centerpoint].valence; k++)
667
398
                {
668
398
                    if (BOND_TYPE_SINGLE != (BOND_TYPE_MASK & at2[centerpoint].bond_type[k]))
669
144
                    {
670
144
                        continue;
671
144
                    }
672
254
                    endpoint2 = at2[centerpoint].neighbor[k];
673
254
                    if (!at2[endpoint2].endpoint && atn[endpoint2].endpoint)
674
16
                    {
675
16
                        break;
676
16
                    }
677
254
                }
678
136
                if (k == at2[centerpoint].valence)
679
120
                {
680
120
                    continue;
681
120
                }
682
                /* the centerpoint and two extra endpoints have been found */
683
16
                pe = pBNS->edge + pVA[centerpoint].nCPlusGroupEdge - 1;
684
16
                if (!pe->flow)
685
0
                {
686
0
                    continue;
687
0
                }
688
16
                pe2 = pBNS->edge + pVA[endpoint1].nCMinusGroupEdge - 1;
689
16
                if (pe2->flow)
690
0
                {
691
0
                    continue;
692
0
                }
693
16
                delta = 1;
694
16
                v1 = pe->neighbor1;
695
16
                v2 = pe->neighbor12 ^ v1;
696
16
                pe->flow -= delta;
697
16
                pBNS->vert[v1].st_edge.flow -= delta;
698
16
                pBNS->vert[v2].st_edge.flow -= delta;
699
16
                pBNS->tot_st_flow -= 2 * delta;
700
16
                pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */
701
702
16
                ret = RunBnsTestOnce(pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
703
16
                    &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms);
704
16
                if (ret < 0)
705
0
                {
706
0
                    goto exit_function;
707
0
                }
708
16
                if (ret == 1 && ((vPathEnd == v1 && vPathStart == v2) ||
709
13
                    (vPathEnd == v2 && vPathStart == v1)) &&
710
13
                    nDeltaCharge <= 1) /* djb-rwth: addressing LLVM warnings */
711
13
                {
712
13
                    ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
713
13
                    (*pnNumRunBNS)++;
714
13
                    if (ret < 0)
715
0
                    {
716
0
                        goto exit_function;
717
0
                    }
718
13
                    else
719
13
                    {
720
13
                        if (ret)
721
13
                        {
722
13
                            *pnTotalDelta += ret;
723
13
                        }
724
0
                        else
725
0
                        {
726
0
                            ret = RI_ERR_PROGR;
727
0
                        }
728
13
                    }
729
13
                    goto exit_function;
730
13
                }
731
3
                else
732
3
                {
733
3
                    ret = 0;
734
3
                    pe->flow += delta;
735
3
                    pBNS->vert[v1].st_edge.flow += delta;
736
3
                    pBNS->vert[v2].st_edge.flow += delta;
737
3
                    pBNS->tot_st_flow += 2 * delta;
738
3
                    pe2->forbidden |= forbidden_edge_mask;
739
3
                }
740
16
            }
741
207
        }
742
159
    }
743
744
169
exit_function:
745
    /* remove bond fixation */
746
169
    RemoveForbiddenEdgeMask(pBNS, &NewlyFixedEdges, forbidden_edge_mask);
747
169
    AllocEdgeList(&NewlyFixedEdges, EDGE_LIST_FREE);
748
749
169
    return ret;
750
169
}
751
752
753
/****************************************************************************/
754
int  FillOutExtraFixedHDataRestr(StrFromINChI* pStruct)
755
2.10k
{
756
2.10k
    int i, j, k, len, ret = 0;
757
2.10k
    AT_NUMB* pNum;
758
6.32k
    for (i = 0; i < TAUT_NUM; i++)
759
4.21k
    {
760
4.21k
        if (pStruct->pOneINChI_Aux[i])
761
2.93k
        {
762
2.93k
            pNum = (pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd &&
763
64
                pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd[0]) ?
764
60
                pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd :
765
2.93k
                (pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd &&
766
2.87k
                    pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd[0]) ?
767
2.87k
                pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd : NULL;
768
2.93k
        }
769
1.27k
        else
770
1.27k
        {
771
1.27k
            pNum = NULL;
772
1.27k
        }
773
4.21k
        if (pNum)
774
2.93k
        {
775
2.93k
            len = pStruct->num_atoms * sizeof(pStruct->nCanon2Atno[0][0]);
776
2.93k
            if ((!pStruct->nCanon2Atno[i] &&
777
1.08k
                !(pStruct->nCanon2Atno[i] = (AT_NUMB*)inchi_malloc(len))) ||
778
2.93k
                (!pStruct->nAtno2Canon[i] &&
779
1.08k
                    !(pStruct->nAtno2Canon[i] = (AT_NUMB*)inchi_malloc(len)))) /* djb-rwth: addressing LLVM warnings */
780
0
            {
781
0
                ret = RI_ERR_ALLOC;
782
0
                goto exit_function;
783
0
            }
784
785
            INCHI_HEAPCHK
786
787
2.93k
                memcpy(pStruct->nCanon2Atno[i], pNum, len); /* ??? the next for(...) fills it out */
788
789
            INCHI_HEAPCHK
790
791
26.6k
                for (j = 0; j < pStruct->num_atoms; j++)
792
23.7k
                {
793
23.7k
                    k = pNum[j] - 1; /* atom number */
794
23.7k
                    pStruct->nCanon2Atno[i][j] = (AT_NUMB)k;
795
23.7k
                    pStruct->nAtno2Canon[i][k] = (AT_NUMB)j;
796
23.7k
                    INCHI_HEAPCHK
797
23.7k
                }
798
2.93k
        }
799
1.27k
        else
800
1.27k
        {
801
1.27k
            if (!i)
802
0
            {
803
0
                ret = RI_ERR_PROGR;
804
0
                goto exit_function;
805
0
            }
806
1.27k
            else
807
1.27k
            {
808
1.27k
                if (pStruct->nCanon2Atno[i])
809
40
                {
810
40
                    inchi_free(pStruct->nCanon2Atno[i]);
811
40
                    pStruct->nCanon2Atno[i] = NULL;
812
40
                }
813
1.27k
                INCHI_HEAPCHK
814
1.27k
                    if (pStruct->nAtno2Canon[i])
815
40
                    {
816
40
                        inchi_free(pStruct->nAtno2Canon[i]);
817
40
                        pStruct->nAtno2Canon[i] = NULL;
818
40
                    }
819
1.27k
                INCHI_HEAPCHK
820
1.27k
            }
821
1.27k
        }
822
4.21k
    }
823
824
2.10k
exit_function:
825
826
2.10k
    return ret;
827
2.10k
}
828
829
830
/****************************************************************************/
831
int  FillOutExtraFixedHDataInChI(StrFromINChI* pStruct, INChI* pInChI[])
832
554
{
833
554
    int ret = 0;
834
    /*--- allocate memory for Mobile/Fixed-H data from the input InChI ---*/
835
554
    if (NULL == pStruct->endpoint)
836
0
    {
837
0
        pStruct->endpoint = (AT_NUMB*)inchi_calloc(pStruct->num_atoms, sizeof(pStruct->endpoint[0]));
838
0
    }
839
554
    else
840
554
    {
841
554
        memset(pStruct->endpoint, 0, pStruct->num_atoms * sizeof(pStruct->endpoint[0])); /* djb-rwth: memset_s C11/Annex K variant? */
842
554
    }
843
554
    if (NULL == pStruct->fixed_H)
844
554
    {
845
554
        pStruct->fixed_H = (S_CHAR*)inchi_malloc(pStruct->num_atoms * sizeof(pStruct->fixed_H[0]));
846
554
    }
847
554
    if (!pStruct->endpoint || !pStruct->fixed_H)
848
0
    {
849
0
        ret = RI_ERR_ALLOC;
850
0
        goto exit_function;
851
0
    }
852
    /*--- fill out Mobile/Fixed-H data from the input InChI ---*/
853
554
    GetTgroupInfoFromInChI(&pStruct->ti, NULL, pStruct->endpoint, pInChI[1]);
854
554
    if (pInChI[0]->nNum_H_fixed)
855
311
    {
856
311
        memcpy(pStruct->fixed_H, pInChI[0]->nNum_H_fixed, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]));
857
311
    }
858
243
    else
859
243
    {
860
243
        memset(pStruct->fixed_H, 0, pStruct->num_atoms * sizeof(pStruct->fixed_H[0])); /* djb-rwth: memset_s C11/Annex K variant? */
861
243
    }
862
863
554
exit_function:
864
865
554
    return ret;
866
554
}
867
868
869
/****************************************************************************/
870
int FillOutCMP2FHINCHI(StrFromINChI* pStruct,
871
    inp_ATOM* at2,
872
    VAL_AT* pVA,
873
    INChI* pInChI[],
874
    CMP2FHINCHI* pc2i)
875
1.22k
{
876
1.22k
    int       ret = 0, i, j;
877
1.22k
    int       bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted;
878
1.22k
    inp_ATOM* at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] &&
879
1.22k
        pStruct->pOne_norm_data[1]->at) ? pStruct->pOne_norm_data[1]->at : NULL;
880
1.22k
    S_CHAR* num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed ? pStruct->pOneINChI[0]->nNum_H_fixed : NULL;
881
    /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */
882
    /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */
883
1.22k
    AT_NUMB* nAtno2CanonRevrs = pStruct->nAtno2Canon[0];
884
1.22k
    S_CHAR* pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H) ? pInChI[1]->nNum_H :
885
1.22k
        (pInChI[0] && pInChI[0]->nNum_H) ? pInChI[0]->nNum_H : NULL;
886
1.22k
    S_CHAR* pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H) ?
887
595
        pStruct->pOneINChI[1]->nNum_H :
888
1.22k
        (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H) ?
889
633
        pStruct->pOneINChI[0]->nNum_H : NULL;
890
1.22k
    int     nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs;
891
1.22k
    memset(pc2i, 0, sizeof(*pc2i)); /* djb-rwth: memset_s C11/Annex K variant? */
892
1.22k
    pc2i->nNumTgInChI = pStruct->ti.num_t_groups;
893
1.22k
    pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups;
894
1.22k
    pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs;
895
896
1.22k
    pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI;
897
1.22k
    pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons;
898
1.22k
    pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;
899
900
1.22k
    pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists;
901
1.22k
    pc2i->bHasDifference |= !bFixHRevrsExists;
902
903
1.69k
    for (i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i++)
904
466
    {
905
466
        nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
906
466
        nNumTgMInChI = pStruct->ti.t_group[i].num[1];
907
466
        nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
908
466
        nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
909
910
466
        pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs;
911
466
        pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs;
912
913
466
        if (pStruct->ti.t_group[i].nNumEndpoints ==
914
466
            pStruct->One_ti.t_group[i].nNumEndpoints)
915
114
        {
916
917
114
            if (nNumTgHInChI != nNumTgHRevrs)
918
114
            {
919
114
                pc2i->nNumTgDiffH++;
920
114
            }
921
114
            if (nNumTgMInChI != nNumTgMRevrs)
922
65
            {
923
65
                pc2i->nNumTgDiffMinus++;
924
65
            }
925
114
        }
926
466
        pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints !=
927
466
            pStruct->One_ti.t_group[i].nNumEndpoints;
928
929
466
        pc2i->nNumTgHInChI += nNumTgHInChI;
930
466
        pc2i->nNumTgMInChI += nNumTgMInChI;
931
466
        pc2i->nNumTgHRevrs += nNumTgHRevrs;
932
466
        pc2i->nNumTgMRevrs += nNumTgMRevrs;
933
466
    }
934
1.96k
    for (; i < pStruct->ti.num_t_groups; i++)
935
736
    {
936
736
        nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
937
736
        nNumTgMInChI = pStruct->ti.t_group[i].num[1];
938
736
        pc2i->nNumTgHInChI += nNumTgHInChI;
939
736
        pc2i->nNumTgMInChI += nNumTgMInChI;
940
736
        pc2i->bHasDifference |= 1;
941
736
    }
942
1.31k
    for (; i < pStruct->One_ti.num_t_groups; i++)
943
91
    {
944
91
        nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
945
91
        nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
946
91
        pc2i->nNumTgHRevrs += nNumTgHRevrs;
947
91
        pc2i->nNumTgMRevrs += nNumTgMRevrs;
948
91
        pc2i->bHasDifference |= 1;
949
91
    }
950
10.8k
    for (i = j = 0; i < pStruct->num_atoms; i++)
951
9.66k
    {
952
        /* i = original InChI canonical number - 1 */
953
        /* k = atom number from InChI created out of restored Fixed-H structure */
954
9.66k
        int iCanonRevrs = nAtno2CanonRevrs[i];
955
9.66k
        int endptInChI = pStruct->endpoint[i]; /* endpoint in InChI */
956
9.66k
        int endptRevrs = at_Mobile_H_Revrs ? at_Mobile_H_Revrs[i].endpoint : 0;
957
9.66k
        int nFixHInChI = pStruct->fixed_H[i];
958
9.66k
        int nFixHRevrs = num_Fixed_H_Revrs ? num_Fixed_H_Revrs[iCanonRevrs] : 0;
959
9.66k
        int nMobHInChI = pnMobHInChI ? pnMobHInChI[i] : 0;
960
9.66k
        int nMobHRevrs = pnMobHRevrs ? pnMobHRevrs[iCanonRevrs] : 0;
961
9.66k
        if ( /*(!endptInChI || !endptRevrs) &&*/ (nFixHInChI != nFixHRevrs) ||
962
8.28k
            (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs)
963
4.07k
        {
964
            /* in InChI or reversed InChI atom[i] is not tautomeric */
965
            /* and number of fixed-H on the atom[i] differs */
966
4.07k
            if (j >= MAX_DIFF_FIXH)
967
0
            {
968
0
                ret = RI_ERR_PROGR;
969
0
                goto exit_function;
970
0
            }
971
4.07k
            pc2i->c2at[j].endptInChI = endptInChI;
972
4.07k
            pc2i->c2at[j].endptRevrs = endptRevrs;
973
4.07k
            pc2i->bHasDifference |= !endptInChI != !endptRevrs;
974
4.07k
            pc2i->c2at[j].atomNumber = i;
975
4.07k
            pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons;
976
4.07k
            pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber;
977
4.07k
            pc2i->c2at[j].nFixHInChI = nFixHInChI;
978
4.07k
            pc2i->c2at[j].nFixHRevrs = nFixHRevrs;
979
4.07k
            pc2i->bHasDifference |= nFixHInChI != nFixHRevrs;
980
4.07k
            pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H ? pInChI[1]->nNum_H[i] :
981
4.07k
                pInChI[0] && pInChI[0]->nNum_H ? pInChI[0]->nNum_H[i] : 0;
982
4.07k
            pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H) ?
983
2.26k
                pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] :
984
4.07k
                (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H) ?
985
1.81k
                pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0;
986
4.07k
            pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI);
987
4.07k
            pc2i->bHasDifference |= nMobHInChI != nMobHRevrs;
988
4.07k
            pc2i->c2at[j].nNumHRevrs = at2[i].num_H;
989
4.07k
            pc2i->c2at[j].nAtChargeRevrs = at2[i].charge;
990
4.07k
            j++;
991
4.07k
        }
992
9.66k
        pc2i->nNumEndpInChI += (endptInChI != 0);
993
9.66k
        pc2i->nNumEndpRevrs += (endptRevrs != 0);
994
995
9.66k
        if (!pVA[i].cMetal)
996
8.17k
        {
997
8.17k
            pc2i->nChargeFixHRevrsNonMetal += at2[i].charge;
998
8.17k
            pc2i->nChargeMobHRevrsNonMetal += at_Mobile_H_Revrs ? at_Mobile_H_Revrs[i].charge : 0;
999
8.17k
        }
1000
1001
        /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
1002
9.66k
    }
1003
1.22k
    pc2i->nChargeFixHInChI = pInChI[0] ? pInChI[0]->nTotalCharge : 0;
1004
1.22k
    pc2i->nChargeMobHInChI = pInChI[1] ? pInChI[1]->nTotalCharge : 0;
1005
1006
1.22k
    pc2i->nChargeMobHRevrs = pStruct->pOneINChI[1] ? pStruct->pOneINChI[1]->nTotalCharge :
1007
1.22k
        pStruct->pOneINChI[0] ? pStruct->pOneINChI[0]->nTotalCharge : 0;
1008
1.22k
    pc2i->nChargeFixHRevrs = pStruct->pOneINChI[0] ? pStruct->pOneINChI[0]->nTotalCharge : 0;
1009
1010
1.22k
    pc2i->bHasDifference |= pc2i->nChargeFixHInChI != pc2i->nChargeFixHRevrs;
1011
1.22k
    pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs;
1012
1013
1.22k
exit_function:
1014
1.22k
    pc2i->len_c2at = j;
1015
1016
1.22k
    return ret;
1017
1.22k
}
1018
1019
1020
/****************************************************************************/
1021
int FillOutCMP2MHINCHI(StrFromINChI* pStruct,
1022
    ALL_TC_GROUPS* pTCGroups,
1023
    inp_ATOM* at2,
1024
    VAL_AT* pVA,
1025
    INChI* pInChI[],
1026
    CMP2MHINCHI* pc2i)
1027
320
{
1028
320
    int       ret = 0, i, j, iat;
1029
320
    int       bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted;
1030
320
    inp_ATOM* at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] &&
1031
320
        pStruct->pOne_norm_data[0]->at) ? pStruct->pOne_norm_data[0]->at : NULL;
1032
    /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */
1033
    /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */
1034
320
    AT_NUMB* nCanon2AtnoRevrs = pStruct->nCanon2Atno[0];
1035
320
    AT_NUMB* nAtno2CanonRevrs = pStruct->nAtno2Canon[0];
1036
320
    S_CHAR* pnMobHInChI = (pInChI[0] && pInChI[0]->nNum_H) ? pInChI[0]->nNum_H : NULL;
1037
320
    S_CHAR* pnMobHRevrs = (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H) ?
1038
320
        pStruct->pOneINChI[0]->nNum_H : NULL;
1039
320
    int     nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs;
1040
320
    memset(pc2i, 0, sizeof(*pc2i)); /* djb-rwth: memset_s C11/Annex K variant? */
1041
320
    pc2i->nNumTgInChI = pStruct->ti.num_t_groups;
1042
320
    pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups;
1043
320
    pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs;
1044
1045
320
    pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI;
1046
320
    pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons;
1047
    /*pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;*/
1048
1049
320
    pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists;
1050
    /*pc2i->bHasDifference |= !bFixHRevrsExists;*/
1051
1052
458
    for (i = 0; i < pStruct->ti.num_t_groups; i++)
1053
152
    {
1054
152
        int jFst = pStruct->ti.t_group[i].nFirstEndpointAtNoPos;
1055
152
        int jNum = pStruct->ti.t_group[i].nNumEndpoints;
1056
152
        int is_N, is_O;
1057
500
        for (j = 0; j < jNum; j++)
1058
362
        {
1059
362
            iat = pStruct->ti.nEndpointAtomNumber[jFst + j];
1060
362
            is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1;
1061
362
            is_O = pVA[iat].cNumValenceElectrons == 6;
1062
362
            if (is_N + is_O != 1)
1063
14
            {
1064
14
                return RI_ERR_SYNTAX;
1065
14
            }
1066
348
            pc2i->nNumTgNInChI += is_N;
1067
348
            pc2i->nNumTgOInChI += is_O;
1068
348
            if (at2[iat].chem_bonds_valence == at2[iat].valence)
1069
172
            {
1070
                /* donor */
1071
172
                if (is_N)
1072
57
                {
1073
                    /* N */
1074
57
                    pc2i->nNumTgNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1;
1075
57
                    pc2i->nNumTgNH2InChI += at2[iat].charge == 0 && at2[iat].num_H == 2;
1076
57
                    pc2i->nNumTgNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0;
1077
57
                    pc2i->nNumTgNHMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 1;
1078
57
                }
1079
115
                else
1080
115
                {
1081
                    /* O, S, Se, Te */
1082
115
                    pc2i->nNumTgOHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1;
1083
115
                    pc2i->nNumTgOMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0;
1084
115
                }
1085
172
            }
1086
176
            else
1087
176
            {
1088
176
                if (at2[iat].chem_bonds_valence == at2[iat].valence + 1)
1089
172
                {
1090
                    /* donor */
1091
172
                    if (is_N)
1092
48
                    {
1093
                        /* N */
1094
48
                        pc2i->nNumTgDBNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1;
1095
48
                        pc2i->nNumTgDBNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0;
1096
48
                        pc2i->nNumTgDBNInChI += at2[iat].charge == 0 && at2[iat].num_H == 0;
1097
48
                    }
1098
124
                    else
1099
124
                    {
1100
                        /* O, S, Se, Te */
1101
124
                        pc2i->nNumTgDBOInChI += at2[iat].charge == 0 && at2[iat].num_H == 0;
1102
124
                    }
1103
172
                }
1104
176
            }
1105
348
        }
1106
152
    }
1107
359
    for (i = 0; i < pStruct->One_ti.num_t_groups; i++)
1108
53
    {
1109
53
        int jFst = pStruct->One_ti.t_group[i].nFirstEndpointAtNoPos;
1110
53
        int jNum = pStruct->One_ti.t_group[i].nNumEndpoints;
1111
53
        int is_N, is_O;
1112
188
        for (j = 0; j < jNum; j++)
1113
135
        {
1114
135
            iat = nCanon2AtnoRevrs[(int)pStruct->One_ti.nEndpointAtomNumber[jFst + j]];
1115
135
            is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1;
1116
135
            is_O = pVA[iat].cNumValenceElectrons == 6;
1117
135
            if (is_N + is_O != 1)
1118
0
            {
1119
0
                return RI_ERR_PROGR;
1120
0
            }
1121
135
            pc2i->nNumTgNRevrs += is_N;
1122
135
            pc2i->nNumTgORevrs += is_O;
1123
135
            if (at2[iat].chem_bonds_valence == at2[iat].valence)
1124
73
            {
1125
                /* donor */
1126
73
                if (is_N)
1127
17
                {
1128
                    /* N */
1129
17
                    pc2i->nNumTgNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1;
1130
17
                    pc2i->nNumTgNH2Revrs += at2[iat].charge == 0 && at2[iat].num_H == 2;
1131
17
                    pc2i->nNumTgNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0;
1132
17
                    pc2i->nNumTgNHMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 1;
1133
17
                }
1134
56
                else
1135
56
                {
1136
                    /* O, S, Se, Te */
1137
56
                    pc2i->nNumTgOHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1;
1138
56
                    pc2i->nNumTgOMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0;
1139
56
                }
1140
73
            }
1141
62
            else
1142
62
            {
1143
62
                if (at2[iat].chem_bonds_valence == at2[iat].valence + 1)
1144
62
                {
1145
                    /* donor */
1146
62
                    if (is_N)
1147
9
                    {
1148
                        /* N */
1149
9
                        pc2i->nNumTgDBNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1;
1150
9
                        pc2i->nNumTgDBNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0;
1151
9
                        pc2i->nNumTgDBNRevrs += at2[iat].charge == 0 && at2[iat].num_H == 0;
1152
9
                    }
1153
53
                    else
1154
53
                    {
1155
                        /* O, S, Se, Te */
1156
53
                        pc2i->nNumTgDBORevrs += at2[iat].charge == 0 && at2[iat].num_H == 0;
1157
53
                    }
1158
62
                }
1159
62
            }
1160
135
        }
1161
53
    }
1162
1163
350
    for (i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i++)
1164
44
    {
1165
44
        nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
1166
44
        nNumTgMInChI = pStruct->ti.t_group[i].num[1];
1167
44
        nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
1168
44
        nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
1169
1170
44
        pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs;
1171
44
        pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs;
1172
1173
44
        if (pStruct->ti.t_group[i].nNumEndpoints ==
1174
44
            pStruct->One_ti.t_group[i].nNumEndpoints)
1175
14
        {
1176
1177
14
            if (nNumTgHInChI != nNumTgHRevrs)
1178
14
            {
1179
14
                pc2i->nNumTgDiffH++;
1180
14
            }
1181
14
            if (nNumTgMInChI != nNumTgMRevrs)
1182
1
            {
1183
1
                pc2i->nNumTgDiffMinus++;
1184
1
            }
1185
14
        }
1186
44
        pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints !=
1187
44
            pStruct->One_ti.t_group[i].nNumEndpoints;
1188
1189
44
        pc2i->nNumTgHInChI += nNumTgHInChI;
1190
44
        pc2i->nNumTgMInChI += nNumTgMInChI;
1191
44
        pc2i->nNumTgHRevrs += nNumTgHRevrs;
1192
44
        pc2i->nNumTgMRevrs += nNumTgMRevrs;
1193
44
    }
1194
397
    for (; i < pStruct->ti.num_t_groups; i++)
1195
91
    {
1196
91
        nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
1197
91
        nNumTgMInChI = pStruct->ti.t_group[i].num[1];
1198
91
        pc2i->nNumTgHInChI += nNumTgHInChI;
1199
91
        pc2i->nNumTgMInChI += nNumTgMInChI;
1200
91
        pc2i->bHasDifference |= 1;
1201
91
    }
1202
315
    for (; i < pStruct->One_ti.num_t_groups; i++)
1203
9
    {
1204
9
        nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
1205
9
        nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
1206
9
        pc2i->nNumTgHRevrs += nNumTgHRevrs;
1207
9
        pc2i->nNumTgMRevrs += nNumTgMRevrs;
1208
9
        pc2i->bHasDifference |= 1;
1209
9
    }
1210
2.29k
    for (i = j = 0; i < pStruct->num_atoms; i++)
1211
1.98k
    {
1212
        /* i = original InChI canonical number - 1 */
1213
        /* k = atom number from InChI created out of restored Fixed-H structure */
1214
1.98k
        int iCanonRevrs = nAtno2CanonRevrs[i];
1215
1.98k
        int endptInChI = at2[i].endpoint; /* endpoint in InChI */
1216
1.98k
        int endptRevrs = at_Mobile_H_Revrs ? at_Mobile_H_Revrs[i].endpoint : 0;
1217
1.98k
        int nMobHInChI = pnMobHInChI ? pnMobHInChI[i] : 0;
1218
1.98k
        int nMobHRevrs = pnMobHRevrs ? pnMobHRevrs[iCanonRevrs] : 0;
1219
1.98k
        if ((!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs)
1220
388
        {
1221
            /* in InChI or reversed InChI atom[i] is not tautomeric */
1222
            /* and number of fixed-H on the atom[i] differs */
1223
388
            if (j >= MAX_DIFF_FIXH)
1224
0
            {
1225
0
                ret = RI_ERR_PROGR;
1226
0
                goto exit_function;
1227
0
            }
1228
388
            pc2i->c2at[j].endptInChI = endptInChI;
1229
388
            pc2i->c2at[j].endptRevrs = endptRevrs;
1230
388
            pc2i->bHasDifference |= !endptInChI != !endptRevrs;
1231
388
            pc2i->c2at[j].atomNumber = i;
1232
388
            pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons;
1233
388
            pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber;
1234
388
            pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H ? pInChI[1]->nNum_H[i] :
1235
388
                pInChI[0] && pInChI[0]->nNum_H ? pInChI[0]->nNum_H[i] : 0;
1236
388
            pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H) ?
1237
0
                pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] :
1238
388
                (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H) ?
1239
388
                pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0;
1240
1241
388
            pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI);
1242
388
            pc2i->bHasDifference |= (nMobHInChI != nMobHRevrs);
1243
388
            pc2i->c2at[j].nNumHRevrs = at2[i].num_H;
1244
388
            pc2i->c2at[j].nAtChargeRevrs = at2[i].charge;
1245
388
            j++;
1246
388
        }
1247
1.98k
        pc2i->nNumEndpInChI += (endptInChI != 0);
1248
1.98k
        pc2i->nNumEndpRevrs += (endptRevrs != 0);
1249
1250
1.98k
        if (!pVA[i].cMetal)
1251
1.70k
        {
1252
1.70k
            pc2i->nChargeMobHRevrsNonMetal += (at_Mobile_H_Revrs && !at_Mobile_H_Revrs[i].endpoint) ? at_Mobile_H_Revrs[i].charge : 0;
1253
1.70k
        }
1254
1255
1256
        /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
1257
1.98k
    }
1258
306
    pc2i->nChargeMobHRevrsNonMetal += pTCGroups->tgroup_charge;
1259
1260
306
    pc2i->nChargeMobHInChI = pInChI[0] ? pInChI[0]->nTotalCharge : 0;
1261
1262
306
    pc2i->nChargeMobHRevrs = pStruct->pOneINChI[0] ? pStruct->pOneINChI[0]->nTotalCharge : 0;
1263
1264
306
    pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs;
1265
1266
306
exit_function:
1267
306
    pc2i->len_c2at = j;
1268
1269
306
    return ret;
1270
306
}
1271
1272
1273
/****************************************************************************/
1274
int NormalizeAndCompare(CANON_GLOBALS* pCG,
1275
    INCHI_CLOCK* ic,
1276
    ICHICONST INPUT_PARMS* ip,
1277
    STRUCT_DATA* sd,
1278
    BN_STRUCT* pBNS,
1279
    BN_DATA* pBD,
1280
    StrFromINChI* pStruct,
1281
    inp_ATOM* at,
1282
    inp_ATOM* at2,
1283
    inp_ATOM* at3,
1284
    VAL_AT* pVA,
1285
    ALL_TC_GROUPS* pTCGroups,
1286
    INChI* pInChI[],
1287
    long num_inp,
1288
    int bHasSomeFixedH,
1289
    int* pnNumRunBNS,
1290
    int* pnTotalDelta,
1291
    int forbidden_edge_mask,
1292
    int forbidden_stereo_edge_mask)
1293
18.5k
{
1294
18.5k
    int i;
1295
18.5k
    int err;
1296
18.5k
    ICR icr, icr2;
1297
18.5k
    int num_norm_endpoints, num_endpoints, num_norm_t_groups, ret = 0; /* djb-rwth: ignoring LLVM warning: variables used; removing redundant variables */
1298
#if ( bRELEASE_VERSION == 0 )
1299
#ifndef TARGET_API_LIB
1300
    const char* szCurHdr = (ip->pSdfValue && ip->pSdfValue[0]) ? ip->pSdfValue : "???";
1301
    int         iComponent = pTCGroups->iComponent;
1302
#endif
1303
#endif
1304
18.5k
    T_GROUP_INFO* t_group_info = NULL;
1305
18.5k
    inp_ATOM* at_norm = NULL; /* normalized */
1306
18.5k
    inp_ATOM* at_prep = NULL; /* preprocessed */
1307
18.5k
    INCHI_MODE  cmpInChI, cmpInChI2;
1308
18.5k
    int         nDeltaPrev, nDeltaCur;
1309
18.5k
    int         iOrigInChI, iRevrInChI;
1310
1311
1312
    /***********************************************************/
1313
    /* normalize and create one component InChI                */
1314
    /***********************************************************/
1315
18.5k
    ret = MakeOneInChIOutOfStrFromINChI2(pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1316
18.5k
        &t_group_info, &at_norm, &at_prep);
1317
18.5k
    if (ret < 0)
1318
7
    {
1319
#if ( bRELEASE_VERSION == 0 )
1320
#ifndef TARGET_API_LIB
1321
        fprintf(stdout, "\nERROR in MakeOneInchi-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1322
            szCurHdr ? szCurHdr : "???", iComponent, pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F', ret);
1323
#endif
1324
#endif
1325
7
        goto exit_function;
1326
7
    }
1327
18.5k
    if (pStruct->bMobileH == TAUT_NON)
1328
548
    {
1329
        /* these indexes are used to compare Mobile-H InChI */
1330
548
        iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted) ? 1 : 0;
1331
548
        iRevrInChI = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted) ? 1 : 0;
1332
548
    }
1333
17.9k
    else
1334
17.9k
    {
1335
17.9k
        iOrigInChI = 0;
1336
17.9k
        iRevrInChI = 0;
1337
17.9k
    }
1338
1339
    /* Intercept and correct non-polymer Zz to Zy if applicable */
1340
18.5k
    if (pStruct->n_zy && pStruct->n_pzz)
1341
0
    {
1342
0
        if (pStruct->pOneINChI[iRevrInChI]->szHillFormula)
1343
0
        {
1344
0
            INCHI_IOS_STRING temp_string_container;
1345
0
            INCHI_IOS_STRING* strbuf = &temp_string_container;
1346
0
            int len0 = strlen(pStruct->pOneINChI[iRevrInChI]->szHillFormula);
1347
0
            if (inchi_strbuf_init(strbuf, len0 + 1, len0 + 1) > 0)
1348
0
            {
1349
0
                inchi_strbuf_printf(strbuf, "%-s", pStruct->pOneINChI[iRevrInChI]->szHillFormula);
1350
0
            }
1351
0
            MergeZzInHillFormula(strbuf);
1352
0
            if (strbuf->nUsedLength > len0 + 1)
1353
0
            {
1354
0
                char* ctmp; /* djb-rwth: supplementary variable */
1355
0
                ctmp = (char*)inchi_realloc(pStruct->pOneINChI[iRevrInChI]->szHillFormula, (long long)strbuf->nUsedLength + 1); /* djb-rwth: cast operator added */
1356
0
                if (ctmp != NULL) /* djb-rwth: NULL pointer must not be assigned to pStruct->pOneINChI[iRevrInChI]->szHillFormula */
1357
0
                    pStruct->pOneINChI[iRevrInChI]->szHillFormula = ctmp;
1358
0
            }
1359
0
            strcpy(pStruct->pOneINChI[iRevrInChI]->szHillFormula, strbuf->pStr);
1360
0
            inchi_strbuf_close(strbuf);
1361
0
        }
1362
0
    }
1363
1364
1365
    /************************************************************/
1366
    /* compare                                                  */
1367
    /************************************************************/
1368
18.5k
    if (pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr(pStruct)))
1369
0
    {
1370
0
        goto exit_function;
1371
0
    }
1372
18.5k
    cmpInChI = CompareReversedINChI2(pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr, &err);
1373
18.5k
    if (cmpInChI & IDIF_PROBLEM)
1374
17.8k
    {
1375
17.8k
        ret = RI_ERR_PROGR; /* severe restore problem */
1376
17.8k
        goto exit_function;
1377
17.8k
    }
1378
660
    if (err)
1379
0
    {
1380
0
        ret = RI_ERR_ALLOC;
1381
0
        goto exit_function;
1382
0
    }
1383
    /********** InChI from restored structure has LESS hydrogen atoms ******************************/
1384
660
    if ((cmpInChI & IDIF_LESS_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1))
1385
19
    {
1386
19
        do
1387
19
        {
1388
19
            ret = FixLessHydrogenInFormula(pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups,
1389
19
                pnNumRunBNS, pnTotalDelta, forbidden_edge_mask);
1390
19
            if (ret < 0)
1391
0
            {
1392
0
                goto exit_function;
1393
0
            }
1394
19
            if (ret)
1395
1
            {
1396
                /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */
1397
1
                ret = MakeOneInChIOutOfStrFromINChI2(pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1398
1
                    &t_group_info, &at_norm, &at_prep);
1399
1
                if (ret < 0)
1400
0
                {
1401
#if ( bRELEASE_VERSION == 0 )
1402
#ifndef TARGET_API_LIB
1403
                    fprintf(stdout, "\nERROR in MakeOneInchi-2: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1404
                        szCurHdr ? szCurHdr : "???", iComponent, pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F', ret);
1405
#endif
1406
#endif
1407
0
                    goto exit_function;
1408
0
                }
1409
                /* compare new InChI to the original InChI */
1410
1
                if (pStruct->bMobileH == TAUT_NON)
1411
1
                {
1412
1
                    iRevrInChI = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted) ? 1 : 0;
1413
1
                }
1414
0
                else
1415
0
                {
1416
0
                    iRevrInChI = 0;
1417
0
                }
1418
1
                if (pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr(pStruct)))
1419
0
                {
1420
0
                    goto exit_function;
1421
0
                }
1422
1
                cmpInChI = CompareReversedINChI2(pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err);
1423
1
                nDeltaPrev = nDeltaCur;
1424
1
                nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1;
1425
1
            }
1426
18
            else
1427
18
            {
1428
18
                break;
1429
18
            }
1430
19
        } while ((cmpInChI & IDIF_LESS_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev);
1431
19
    }
1432
    /********** InChI from restored structure has MORE hydrogen atoms ******************************/
1433
660
    if ((cmpInChI & IDIF_MORE_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2))
1434
129
    {
1435
129
        do
1436
136
        {
1437
136
            ret = FixMoreHydrogenInFormula(pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups,
1438
136
                pnNumRunBNS, pnTotalDelta, forbidden_edge_mask);
1439
136
            if (ret < 0)
1440
0
            {
1441
0
                goto exit_function;
1442
0
            }
1443
136
            if (ret)
1444
10
            {
1445
                /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */
1446
10
                ret = MakeOneInChIOutOfStrFromINChI2(pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1447
10
                    &t_group_info, &at_norm, &at_prep);
1448
10
                if (ret < 0)
1449
0
                {
1450
#if ( bRELEASE_VERSION == 0 )
1451
#ifndef TARGET_API_LIB
1452
                    fprintf(stdout, "\nERROR in MakeOneInchi-3: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1453
                        szCurHdr ? szCurHdr : "???", iComponent, pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F', ret);
1454
#endif
1455
#endif
1456
0
                    goto exit_function;
1457
0
                }
1458
                /* compare new InChI to the original InChI */
1459
10
                if (pStruct->bMobileH == TAUT_NON)
1460
8
                {
1461
8
                    iRevrInChI = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted) ? 1 : 0;
1462
8
                }
1463
2
                else
1464
2
                {
1465
2
                    iRevrInChI = 0;
1466
2
                }
1467
10
                if (pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr(pStruct)))
1468
0
                {
1469
0
                    goto exit_function;
1470
0
                }
1471
10
                cmpInChI = CompareReversedINChI2(pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err);
1472
10
                nDeltaPrev = nDeltaCur;
1473
10
                nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2;
1474
10
            }
1475
126
            else
1476
126
            {
1477
126
                break;
1478
126
            }
1479
136
        } while ((cmpInChI & IDIF_MORE_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev);
1480
129
    }
1481
    /***************** Fix non-taut atoms normalized to tautomeric endpoints ***********************/
1482
660
    if ((cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && 0 < (nDeltaCur = icr.num_endp_in1_only))
1483
163
    {
1484
163
        do
1485
169
        {
1486
169
            ret = FixRemoveExtraTautEndpoints(pBNS, pBD, pStruct, at, at2, at_prep, at_norm, pVA, pTCGroups, &icr,
1487
169
                pnNumRunBNS, pnTotalDelta, forbidden_edge_mask);
1488
169
            if (ret < 0)
1489
0
            {
1490
0
                goto exit_function;
1491
0
            }
1492
169
            if (ret)
1493
13
            {
1494
                /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */
1495
13
                ret = MakeOneInChIOutOfStrFromINChI2(pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1496
13
                    &t_group_info, &at_norm, &at_prep);
1497
13
                if (ret < 0)
1498
0
                {
1499
#if ( bRELEASE_VERSION == 0 )
1500
#ifndef TARGET_API_LIB
1501
                    fprintf(stdout, "\nERROR in MakeOneInchi-4: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1502
                        szCurHdr ? szCurHdr : "???", iComponent, pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F', ret);
1503
#endif
1504
#endif
1505
0
                    goto exit_function;
1506
0
                }
1507
                /* compare new InChI to the original InChI */
1508
13
                if (pStruct->bMobileH == TAUT_NON)
1509
0
                {
1510
0
                    iRevrInChI = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted) ? 1 : 0;
1511
0
                }
1512
13
                else
1513
13
                {
1514
13
                    iRevrInChI = 0;
1515
13
                }
1516
13
                if (pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr(pStruct)))
1517
0
                {
1518
0
                    goto exit_function;
1519
0
                }
1520
13
                cmpInChI = CompareReversedINChI2(pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err);
1521
13
                nDeltaPrev = nDeltaCur;
1522
13
                nDeltaCur = icr.num_endp_in1_only;
1523
13
            }
1524
156
            else
1525
156
            {
1526
156
                break;
1527
156
            }
1528
169
        } while ((cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && nDeltaCur && nDeltaCur < nDeltaPrev);
1529
163
    }
1530
    /************************ case of Fixed-H ******************************************************/
1531
1532
660
    if (pStruct->bMobileH == TAUT_NON)
1533
390
    {
1534
390
        int num_tries = 0;
1535
390
        do
1536
717
        {
1537
717
            if (0 > (ret = FixFixedHRestoredStructure(pCG, ic, ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups,
1538
717
                &t_group_info, &at_norm, &at_prep, pInChI,
1539
717
                num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask,
1540
717
                forbidden_stereo_edge_mask)))
1541
4
            {
1542
4
                goto exit_function;
1543
4
            }
1544
717
        } while (num_tries++ < 2 && ret > 0);
1545
390
    }
1546
    /************************ case of Fixed-H ******************************************************/
1547
656
    if (pStruct->bMobileH == TAUT_YES)
1548
270
    {
1549
270
        if (0 > (ret = FixMobileHRestoredStructure(pCG, ic, ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups,
1550
270
            &t_group_info, &at_norm, &at_prep, pInChI,
1551
270
            num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask,
1552
270
            forbidden_stereo_edge_mask)))
1553
14
        {
1554
14
            goto exit_function;
1555
14
        }
1556
270
    }
1557
    /**********************************************************************************************/
1558
    /* stereo */
1559
642
    cmpInChI = CompareReversedINChI2(pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *a2*/, &icr, &err);
1560
642
    if (cmpInChI & IDIF_PROBLEM)
1561
90
    {
1562
90
        ret = RI_ERR_PROGR; /* severe restore problem */
1563
90
        goto exit_function;
1564
90
    }
1565
552
    if (err)
1566
0
    {
1567
0
        ret = RI_ERR_ALLOC;
1568
0
        goto exit_function;
1569
0
    }
1570
552
    cmpInChI2 = 0;
1571
552
    memset(&icr2, 0, sizeof(icr2)); /* djb-rwth: memset_s C11/Annex K variant? */
1572
552
    if (iRevrInChI || iOrigInChI)
1573
296
    {
1574
        /* additional mobile-H compare in case of Fixed-H */
1575
296
        cmpInChI2 = CompareReversedINChI2(pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr2, &err);
1576
296
        if (cmpInChI & IDIF_PROBLEM)
1577
0
        {
1578
0
            ret = RI_ERR_PROGR; /* severe restore problem */
1579
0
            goto exit_function;
1580
0
        }
1581
296
        if (err)
1582
0
        {
1583
0
            ret = RI_ERR_ALLOC;
1584
0
            goto exit_function;
1585
0
        }
1586
296
    }
1587
552
    ret = FixRestoredStructureStereo(pCG, ic,
1588
552
        cmpInChI, &icr, cmpInChI2, &icr2,
1589
552
        ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups,
1590
552
        &t_group_info, &at_norm, &at_prep, pInChI,
1591
552
        num_inp, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask,
1592
552
        forbidden_stereo_edge_mask);
1593
1594
552
    if (ret < 0)
1595
0
    {
1596
0
        goto exit_function;
1597
0
    }
1598
#if ( FIX_ADD_PROTON_FOR_ADP == 1 )
1599
    /************************ check and fix ADP by adding a proton (dummy) *************************/
1600
    if (cmpInChI && pTCGroups->num_tgroups && pBNS->tot_st_cap > pBNS->tot_st_flow)
1601
    {
1602
        ret = FixAddProtonForADP(pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, &icr,
1603
            pnNumRunBNS, pnTotalDelta, forbidden_edge_mask);
1604
        if (ret < 0)
1605
        {
1606
            goto exit_function;
1607
        }
1608
    }
1609
#endif
1610
    /* moved to MakeOneInChIOutOfStrFromINChI():
1611
      pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0;
1612
    */
1613
1614
    /* count endpoints */
1615
552
    num_endpoints = 0;
1616
552
    num_norm_endpoints = 0;
1617
552
    num_norm_t_groups = 0;
1618
    /* djb-rwth: removing redundant code */
1619
552
    at_norm = pStruct->pOne_norm_data[0]->at;
1620
637
    for (i = 0; i < pTCGroups->num_tgroups; i++)
1621
85
    {
1622
85
        num_endpoints += pTCGroups->pTCG[i].num_edges;
1623
        /* djb-rwth: removing redundant code */
1624
85
    }
1625
1626
552
    if (t_group_info)
1627
47
    {
1628
        /* after canonicalization, t_group_info->t_group[i].num[0] = number of H   */
1629
        /*                         t_group_info->t_group[i].num[1] = number of (-) */
1630
97
        for (i = 0; i < t_group_info->num_t_groups; i++)
1631
50
        {
1632
50
            if (t_group_info->t_group[i].num[0])
1633
50
            {
1634
50
                num_norm_t_groups++;
1635
50
                num_norm_endpoints += t_group_info->t_group[i].nNumEndpoints;
1636
                /* djb-rwth: removing redundant code */
1637
50
            }
1638
50
        }
1639
47
    }
1640
#if ( bRELEASE_VERSION == 0 )
1641
#ifndef TARGET_API_LIB
1642
    if (num_norm_t_groups != pTCGroups->num_tgroups || num_norm_endpoints != num_endpoints)
1643
    {
1644
        /* need aggressive (de)protonation */
1645
        /* pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; */
1646
        fprintf(stdout, "NORMCOMP: %s comp=%d %c%c: InChI/NormRvrs NumTg=%d/%d NumEndp=%d/%d\n",
1647
            (*ip).pSdfValue, (*pTCGroups).iComponent,
1648
            pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F',
1649
            pTCGroups->num_tgroups, num_norm_t_groups,
1650
            num_endpoints, num_norm_endpoints);
1651
    }
1652
#endif
1653
#endif
1654
1655
18.5k
exit_function:
1656
1657
55.6k
    for (i = 0; i < TAUT_NUM; i++)
1658
37.0k
    {
1659
37.0k
        Free_INChI(&pStruct->pOneINChI[i]);
1660
37.0k
        Free_INChI_Aux(&pStruct->pOneINChI_Aux[i]);
1661
37.0k
        FreeInpAtomData(pStruct->pOne_norm_data[i]);
1662
37.0k
        if (pStruct->pOne_norm_data[i])
1663
18.7k
        {
1664
18.7k
            inchi_free(pStruct->pOne_norm_data[i]);
1665
18.7k
            pStruct->pOne_norm_data[i] = NULL;
1666
18.7k
        }
1667
37.0k
    }
1668
1669
18.5k
    free_t_group_info(&pStruct->One_ti);
1670
1671
18.5k
    return ret;
1672
552
}
1673
1674
1675
/****************************************************************************
1676
 Find A=X< where all bonds to X except A=X are marked as stereogenic;
1677
 temporary allow stereobonds  change and make A=X bonds single                                                                   */
1678
int CheckAndRefixStereobonds(BN_STRUCT* pBNS, BN_DATA* pBD, StrFromINChI* pStruct,
1679
    inp_ATOM* at, inp_ATOM* at2, VAL_AT* pVA, ALL_TC_GROUPS* pTCGroups,
1680
    int* pnNumRunBNS, int* pnTotalDelta, int forbidden_edge_mask)
1681
18.5k
{
1682
18.5k
    int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK;
1683
18.5k
    int inv_forbidden_edge_stereo = ~forbidden_edge_stereo;
1684
1685
18.5k
    int i, k, ne, j1, j2, num_wrong, num_fixed;
1686
18.5k
    int ret2, retBNS, ret;
1687
18.5k
    int num_at = pStruct->num_atoms;
1688
18.5k
    int num_deleted_H = pStruct->num_deleted_H;
1689
18.5k
    int len_at = num_at + num_deleted_H;
1690
18.5k
    EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges;
1691
1692
18.5k
    BNS_EDGE* pEdge;
1693
18.5k
    Vertex      v1, v2;
1694
18.5k
    BNS_VERTEX* pv1, * pv2;
1695
1696
18.5k
    ret = 0;
1697
1698
    /* to simplify, prepare new at[] from pBNS */
1699
18.5k
    memcpy(at2, at, len_at * sizeof(at2[0]));
1700
18.5k
    pStruct->at = at2;
1701
18.5k
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
1702
18.5k
    pStruct->at = at;
1703
18.5k
    if (ret2 < 0)
1704
0
    {
1705
0
        return ret;
1706
0
    }
1707
1708
18.5k
    num_wrong = 0;
1709
    /* find wrong double bonds */
1710
144k
    for (i = 0; i < num_at; i++)
1711
125k
    {
1712
125k
        if (at2[i].valence == 3 &&
1713
1.13k
            at2[i].chem_bonds_valence - at2[i].valence == 1 &&
1714
418
            at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
1715
14
            (at2[i].bond_type[j1 = (int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1716
8
            (at2[i].bond_type[j2 = (int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1717
7
            j1 != j2)
1718
7
        {
1719
1720
7
            num_wrong++;
1721
7
        }
1722
125k
    }
1723
18.5k
    if (!num_wrong)
1724
18.5k
    {
1725
18.5k
        return 0;
1726
18.5k
    }
1727
7
    num_fixed = 0;
1728
42
    for (i = 0; i < pBNS->num_bonds; i++)
1729
35
    {
1730
35
        pEdge = pBNS->edge + i;
1731
35
        if (pEdge->forbidden & forbidden_edge_stereo)
1732
0
        {
1733
0
            num_fixed++;
1734
0
        }
1735
35
    }
1736
1737
    /* there may be no fixed stereo bonds at all, see #87607 */
1738
7
    AllocEdgeList(&CarbonChargeEdges, EDGE_LIST_CLEAR);
1739
7
    AllocEdgeList(&FixedEdges, EDGE_LIST_CLEAR);
1740
7
    AllocEdgeList(&WrongEdges, EDGE_LIST_CLEAR);
1741
1742
    /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */
1743
1744
7
    if (0 > (ret = ForbidCarbonChargeEdges(pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask)))
1745
0
    {
1746
0
        goto exit_function;
1747
0
    }
1748
7
    if ((ret = AllocEdgeList(&FixedEdges, num_fixed)) ||
1749
7
        (ret = AllocEdgeList(&WrongEdges, num_wrong)))
1750
0
    {
1751
0
        goto exit_function;
1752
0
    }
1753
    /* collect wrong double bonds and set flow=0 */
1754
16
    for (i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i++)
1755
9
    {
1756
9
        if (at2[i].valence == 3 &&
1757
9
            at2[i].chem_bonds_valence - at2[i].valence == 1 &&
1758
9
            at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
1759
7
            (at2[i].bond_type[j1 = (int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1760
7
            (at2[i].bond_type[j2 = (int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1761
7
            j1 != j2)
1762
7
        {
1763
7
            switch (j1 + j2)
1764
7
            {
1765
0
            case 1: /* 0, 1 */
1766
0
                k = 2;
1767
0
                break;
1768
3
            case 2: /* 0, 2 */
1769
3
                k = 1;
1770
3
                break;
1771
4
            case 3: /* 1, 2 */
1772
4
                k = 0;
1773
4
                break;
1774
0
            default:
1775
0
                ret = RI_ERR_PROGR;
1776
0
                goto exit_function;
1777
7
            }
1778
7
            ne = pBNS->vert[i].iedge[k];
1779
7
            pEdge = pBNS->edge + ne;
1780
7
            v1 = pEdge->neighbor1;
1781
7
            v2 = pEdge->neighbor12 ^ v1;
1782
7
            pv1 = pBNS->vert + v1;
1783
7
            pv2 = pBNS->vert + v2;
1784
1785
7
            if (!pEdge->flow)
1786
0
            {
1787
0
                ret = RI_ERR_PROGR;
1788
0
                goto exit_function;
1789
0
            }
1790
7
            pEdge->flow--;
1791
7
            pEdge->forbidden |= forbidden_edge_mask;
1792
7
            pv1->st_edge.flow--;
1793
7
            pv2->st_edge.flow--;
1794
7
            pBNS->tot_st_flow -= 2;
1795
7
            if ((ret = AddToEdgeList(&WrongEdges, ne, 0))) /* djb-rwth: addressing LLVM warning */
1796
0
            {
1797
0
                goto exit_function;
1798
0
            }
1799
7
        }
1800
9
    }
1801
    /* remove forbidden mark from stereo bonds (unfix stereo bonds) */
1802
7
    for (i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i++)
1803
0
    {
1804
0
        pEdge = pBNS->edge + i;
1805
0
        if (pEdge->forbidden & forbidden_edge_stereo)
1806
0
        {
1807
0
            pEdge->forbidden &= inv_forbidden_edge_stereo;
1808
0
            FixedEdges.pnEdges[FixedEdges.num_edges++] = i;
1809
0
        }
1810
0
    }
1811
    /* Run BNS to move charges and rearrange bond orders */
1812
7
    retBNS = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
1813
7
    (*pnNumRunBNS)++;
1814
7
    if (retBNS < 0)
1815
0
    {
1816
0
        goto exit_function;
1817
0
    }
1818
7
    else
1819
7
    {
1820
7
        if (retBNS > 0)
1821
3
        {
1822
3
            *pnTotalDelta += retBNS;
1823
3
        }
1824
7
    }
1825
    /* remove forbidden_edge_mask and set forbidden_edge_stereo */
1826
7
    RemoveForbiddenEdgeMask(pBNS, &WrongEdges, forbidden_edge_mask);
1827
    /* allow carbon charges to change */
1828
7
    RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, forbidden_edge_mask);
1829
    /* fix previously unfixed stereo bonds */
1830
7
    SetForbiddenEdgeMask(pBNS, &FixedEdges, forbidden_edge_stereo);
1831
    /* Run BNS again in case not all edge flows are maximal */
1832
7
    ret2 = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
1833
7
    (*pnNumRunBNS)++;
1834
7
    if (ret2 < 0)
1835
0
    {
1836
0
        goto exit_function;
1837
0
    }
1838
7
    else
1839
7
    {
1840
7
        if (ret2 > 0)
1841
4
        {
1842
4
            *pnTotalDelta += retBNS;
1843
4
        }
1844
7
    }
1845
7
    ret = retBNS;
1846
1847
7
exit_function:
1848
1849
7
    AllocEdgeList(&CarbonChargeEdges, EDGE_LIST_FREE);
1850
7
    AllocEdgeList(&FixedEdges, EDGE_LIST_FREE);
1851
7
    AllocEdgeList(&WrongEdges, EDGE_LIST_FREE);
1852
1853
7
    return ret;
1854
7
}
1855
1856
1857
/****************************************************************************
1858
 Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4
1859
****************************************************************************/
1860
int MoveChargeToRemoveCenerpoints(BN_STRUCT* pBNS,
1861
    BN_DATA* pBD,
1862
    StrFromINChI* pStruct,
1863
    inp_ATOM* at,
1864
    inp_ATOM* at2,
1865
    VAL_AT* pVA,
1866
    ALL_TC_GROUPS* pTCGroups,
1867
    int* pnNumRunBNS,
1868
    int* pnTotalDelta,
1869
    int forbidden_edge_mask)
1870
18.5k
{
1871
18.5k
    int i, j, neigh, num_success; /* djb-rwth: removing redundant variables */
1872
18.5k
    int num_donors, num_acceptors, bond_type, num_donors_O, num_acceptors_O, is_centerpoint_N, num_known_endpoints, num_wrong_neigh;
1873
18.5k
    int ret2, ret_forbid_edges, ret, delta;
1874
18.5k
    int num_at = pStruct->num_atoms;
1875
18.5k
    int num_deleted_H = pStruct->num_deleted_H;
1876
18.5k
    int len_at = num_at + num_deleted_H;
1877
18.5k
    int forbidden_edge_test = BNS_EDGE_FORBIDDEN_TEST;
1878
18.5k
    int bPossiblyIgnore = pStruct->charge >= 0 && (!pTCGroups->num_tgroups || (pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups)); /* djb-rwth: addressing LLVM warning */
1879
18.5k
    S_CHAR MobileChargeNeigh[MAXVAL], DoubleBondAcceptors[MAXVAL], DoubleBondNotONeigh[MAXVAL];
1880
18.5k
    int    numMobileChargeNeigh, numDoubleBondAcceptors, numDoubleBondNotONeigh; /* djb-rwth: removing redundant variables */
1881
18.5k
    EDGE_LIST ChargeListAllExcept_DB_O;
1882
1883
1884
18.5k
    BNS_EDGE* pEdgeMinus, * pe;
1885
18.5k
    Vertex      v1m, v2m;
1886
18.5k
    BNS_VERTEX* pv1m, * pv2m;
1887
    /* djb-rwth: removing redundant code */
1888
18.5k
    num_success = 0;
1889
1890
    /* count O(+)H, N(+)H */
1891
1892
    /*
1893
    if ( pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups) ) {
1894
        goto exit_function;
1895
    }
1896
    */
1897
18.5k
    if ((ret = AllocEdgeList(&ChargeListAllExcept_DB_O, EDGE_LIST_CLEAR))) /* djb-rwth: addressing LLVM warning */
1898
0
    {
1899
0
        goto exit_function;
1900
0
    }
1901
1902
1903
    /* to simplify, prepare new at[] from pBNS */
1904
18.5k
    memcpy(at2, at, len_at * sizeof(at2[0]));
1905
18.5k
    pStruct->at = at2;
1906
18.5k
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
1907
18.5k
    pStruct->at = at;
1908
18.5k
    if (ret2 < 0)
1909
0
    {
1910
0
        ret = ret2;
1911
0
        goto exit_function;
1912
0
    }
1913
18.5k
#if ( FIND_RING_SYSTEMS == 1 )
1914
18.5k
    ret2 = MarkRingSystemsInp(at2, num_at, 0);
1915
18.5k
    if (ret2 < 0)
1916
0
    {
1917
0
        ret = ret2;
1918
0
        goto exit_function;
1919
0
    }
1920
18.5k
#endif
1921
    /* mark bonds that cannot be tautomeric; do not forget to remove the marks later */
1922
18.5k
    ret_forbid_edges = SetForbiddenEdges(pBNS, at2, num_at, forbidden_edge_test, 0, NULL);
1923
18.5k
    if (ret_forbid_edges < 0)
1924
0
    {
1925
0
        ret = ret_forbid_edges;
1926
0
        goto exit_function;
1927
0
    }
1928
1929
144k
    for (i = 0; i < num_at; i++)
1930
125k
    {
1931
125k
        if (pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */
1932
117k
            !(pVA[i].nTautGroupEdge || (pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[i])) &&
1933
116k
            !at2[i].num_H && !at2[i].charge && at2[i].valence >= 2 &&
1934
2.12k
            at2[i].valence < at2[i].chem_bonds_valence &&
1935
1.15k
            is_centerpoint_elem(at2[i].el_number)) /* djb-rwth: addressing LLVM warning */
1936
1.00k
        {
1937
1938
1.00k
            is_centerpoint_N = (pVA[i].cNumValenceElectrons == 5 && (pVA[i].cPeriodicRowNumber == 1 || pVA[i].cMetal));
1939
            /* look at the neighbors */
1940
1.00k
            numMobileChargeNeigh = numDoubleBondAcceptors = numDoubleBondNotONeigh = num_donors = num_acceptors = 0;
1941
1.00k
            num_donors_O = num_acceptors_O = 0;
1942
1.00k
            num_known_endpoints = num_wrong_neigh = 0;
1943
3.57k
            for (j = 0; j < at2[i].valence; j++) /* djb-rwth: removing redundant code */
1944
2.66k
            {
1945
2.66k
                neigh = at2[i].neighbor[j];
1946
2.66k
                if ((at2[neigh].endpoint || (pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[neigh])) || at2[neigh].charge > 0) /* djb-rwth: addressing LLVM warning */
1947
697
                {
1948
697
                    num_known_endpoints++;
1949
697
                    continue;
1950
697
                }
1951
1.97k
                if (pBNS->edge[pBNS->vert[i].iedge[j]].forbidden & forbidden_edge_test)
1952
118
                {
1953
118
                    continue;
1954
118
                }
1955
1.85k
                bond_type = at2[i].bond_type[j] & BOND_TYPE_MASK;
1956
1.85k
                if (bond_type > BOND_TYPE_DOUBLE)
1957
30
                {
1958
30
                    num_wrong_neigh++;
1959
30
                    continue;
1960
30
                }
1961
1.82k
                if (at2[neigh].num_H && bond_type == BOND_TYPE_SINGLE)
1962
102
                {
1963
102
                    break;  /* not this case */
1964
102
                }
1965
1.72k
                if (at2[neigh].chem_bonds_valence - at2[neigh].charge
1966
1.72k
                    != get_endpoint_valence(at2[neigh].el_number))
1967
924
                {
1968
924
                    if (bond_type == BOND_TYPE_DOUBLE && pVA[neigh].cNumValenceElectrons != 6)
1969
238
                    {
1970
238
                        DoubleBondNotONeigh[numDoubleBondNotONeigh++] = j;
1971
238
                    }
1972
924
                    continue;
1973
924
                }
1974
796
                if (at2[neigh].charge == -1 && bond_type == BOND_TYPE_SINGLE &&
1975
76
                    (pVA[neigh].nCMinusGroupEdge < 1 || pBNS->edge[pVA[neigh].nCMinusGroupEdge - 1].flow != 1))
1976
0
                {
1977
0
                    break;
1978
0
                }
1979
796
                switch (bond_type)
1980
796
                {
1981
399
                case BOND_TYPE_SINGLE:
1982
399
                    if (at2[neigh].charge != -1 || pVA[neigh].nCMinusGroupEdge <= 0)
1983
323
                    {
1984
323
                        num_wrong_neigh++;
1985
323
                        continue;
1986
323
                    }
1987
76
                    num_donors++;
1988
76
                    num_donors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4);
1989
76
                    MobileChargeNeigh[numMobileChargeNeigh++] = j;
1990
76
                    break;
1991
394
                case BOND_TYPE_DOUBLE:
1992
394
                    if (at2[neigh].charge)
1993
6
                    {
1994
6
                        num_wrong_neigh++;
1995
6
                        continue;
1996
6
                    }
1997
388
                    DoubleBondAcceptors[numDoubleBondAcceptors++] = j;
1998
388
                    num_acceptors++;
1999
388
                    num_acceptors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4);
2000
796
                }
2001
796
            }
2002
1.00k
            if (j != at2[i].valence || !num_donors || !num_acceptors)
2003
962
            {
2004
962
                continue;
2005
962
            }
2006
            /* special case NOn(-) */
2007
45
            if (is_centerpoint_N && (num_donors == num_donors_O) && (num_acceptors == num_acceptors_O))
2008
2
            {
2009
2
                continue;
2010
2
            }
2011
43
            if (pStruct->iMobileH == TAUT_NON && num_donors == numDoubleBondNotONeigh)
2012
1
            {
2013
                /* fix all charges except on =O */
2014
1
                Vertex     vPathStart, vPathEnd;
2015
1
                int        nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
2016
1
                int k, e, num_MovedCharges = 0;
2017
2018
1
                if (!ChargeListAllExcept_DB_O.num_edges)
2019
1
                {
2020
                    /* djb-rwth: removing redundant code */
2021
9
                    for (k = 0; k < num_at; k++)
2022
8
                    {
2023
8
                        if (!((1 == at2[k].valence && pBNS->edge[pBNS->vert[k].iedge[0]].flow &&
2024
2
                            !pBNS->edge[pBNS->vert[k].iedge[0]].forbidden &&
2025
2
                            !((e = pVA[k].nCMinusGroupEdge - 1) >= 0 && pBNS->edge[e].flow) &&
2026
2
                            !((e = pVA[k].nCPlusGroupEdge - 1) >= 0 && !pBNS->edge[e].flow) &&
2027
                            /* 0 == at2[k].charge && */
2028
2
                            pVA[k].cNumValenceElectrons == 6 && !pVA[k].cMetal &&
2029
1
                            (pStruct->endpoint && pStruct->endpoint[k])) ||
2030
8
                            (pStruct->fixed_H && pStruct->fixed_H[k]))) /* djb-rwth: addressing LLVM warnings */
2031
                            /* djb-rwth: removing redundant code */
2032
5
                            if ((e = pVA[k].nCMinusGroupEdge - 1) >= 0 && !pBNS->edge[e].flow &&
2033
2
                                !pBNS->edge[e].forbidden &&
2034
2
                                (ret = AddToEdgeList(&ChargeListAllExcept_DB_O, e, 64)))
2035
0
                            {
2036
0
                                goto exit_function;
2037
0
                            }
2038
8
                        if ((e = pVA[k].nCPlusGroupEdge - 1) >= 0 &&
2039
7
                            !pBNS->edge[e].forbidden &&
2040
6
                            (ret = AddToEdgeList(&ChargeListAllExcept_DB_O, e, 64)))
2041
0
                        {
2042
0
                            goto exit_function;
2043
0
                        }
2044
8
                    }
2045
1
                }
2046
                /* fix double bonds to non-O neighbors connected by double bonds;
2047
                   we will try to make these bons single */
2048
2
                for (k = 0; k < numDoubleBondNotONeigh; k++)
2049
1
                {
2050
1
                    e = pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]];
2051
1
                    if (!pBNS->edge[e].forbidden &&
2052
1
                        (ret = AddToEdgeList(&ChargeListAllExcept_DB_O, e, 64)))
2053
0
                    {
2054
0
                        goto exit_function;
2055
0
                    }
2056
1
                }
2057
                /* attempt to make DoubleBondNotONeigh[] single */
2058
1
                SetForbiddenEdgeMask(pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask);
2059
2
                for (k = 0; k < numDoubleBondNotONeigh && num_MovedCharges < numMobileChargeNeigh; k++)
2060
1
                {
2061
1
                    pe = pBNS->edge + pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]];
2062
1
                    delta = 1;
2063
1
                    if (pe->flow != delta)
2064
1
                        continue;
2065
0
                    pv1m = pBNS->vert + (v1m = pe->neighbor1);
2066
0
                    pv2m = pBNS->vert + (v2m = pe->neighbor12 ^ v1m);
2067
0
                    pv1m->st_edge.flow -= delta;
2068
0
                    pv2m->st_edge.flow -= delta;
2069
0
                    pe->flow -= delta;
2070
0
                    pBNS->tot_st_flow -= 2 * delta;
2071
0
                    ret = RunBnsTestOnce(pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
2072
0
                        &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms);
2073
0
                    if (ret < 0)
2074
0
                    {
2075
0
                        goto exit_function;
2076
0
                    }
2077
0
                    if (ret == 1 && ((vPathEnd == v1m && vPathStart == v2m) ||
2078
0
                        (vPathEnd == v2m && vPathStart == v1m)) &&
2079
0
                        nDeltaCharge == 0  /* (-) moving from one to another atom*/) /* djb-rwth: addressing LLVM warnings */
2080
0
                    {
2081
0
                        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2082
0
                        (*pnNumRunBNS)++;
2083
0
                        if (ret < 0)
2084
0
                        {
2085
0
                            goto exit_function;
2086
0
                        }
2087
0
                        else
2088
0
                        {
2089
0
                            if (ret == 1)
2090
0
                            {
2091
0
                                *pnTotalDelta += ret;
2092
0
                                num_MovedCharges++;
2093
0
                            }
2094
0
                            else
2095
0
                            {
2096
0
                                ret = RI_ERR_PROGR;
2097
0
                                goto exit_function;
2098
0
                            }
2099
0
                        }
2100
0
                    }
2101
0
                    else
2102
0
                    {
2103
                        /* djb-rwth: removing redundant code */
2104
0
                        pv1m->st_edge.flow += delta;
2105
0
                        pv2m->st_edge.flow += delta;
2106
0
                        pe->flow += delta;
2107
0
                        pBNS->tot_st_flow += 2 * delta;
2108
0
                    }
2109
0
                }
2110
1
                RemoveForbiddenEdgeMask(pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask);
2111
1
            }
2112
42
            else
2113
42
            {
2114
42
                if (!bPossiblyIgnore || (!num_known_endpoints && !num_wrong_neigh && (num_acceptors_O + num_donors_O >= 3))) /* djb-rwth: addressing LLVM warning */
2115
5
                {
2116
                    /* remove negative charges from the neighbors */
2117
5
                    pBNS->vert[i].st_edge.cap += num_donors; /* enough to make all bonds to donors double */
2118
5
                    pBNS->tot_st_cap += num_donors;
2119
5
                    pVA[i].cInitCharge -= num_donors; /* work no matter what are known charge/valence */
2120
10
                    for (j = 0; j < numMobileChargeNeigh; j++)
2121
5
                    {
2122
5
                        neigh = at2[i].neighbor[(int)MobileChargeNeigh[j]];
2123
5
                        pEdgeMinus = pBNS->edge + ((long long)pVA[neigh].nCMinusGroupEdge - 1); /* djb-rwth: cast operator added */
2124
5
                        v1m = pEdgeMinus->neighbor1;
2125
5
                        v2m = pEdgeMinus->neighbor12 ^ v1m;
2126
5
                        pv1m = pBNS->vert + v1m;
2127
5
                        pv2m = pBNS->vert + v2m;
2128
5
                        delta = pEdgeMinus->flow;
2129
5
                        pv1m->st_edge.flow -= delta;
2130
5
                        pv2m->st_edge.flow -= delta;
2131
5
                        if (IS_BNS_VT_C_GR(pv1m->type))
2132
0
                        {
2133
                            /* irreversible change to ChargeStruct */
2134
0
                            pv1m->st_edge.cap -= delta;
2135
0
                        }
2136
5
                        else
2137
5
                        {
2138
5
                            if (IS_BNS_VT_C_GR(pv2m->type))
2139
5
                            {
2140
                                /* irreversible change to ChargeStruct */
2141
5
                                pv2m->st_edge.cap -= delta;
2142
5
                            }
2143
0
                            else
2144
0
                            {
2145
0
                                ret = RI_ERR_PROGR;
2146
0
                                goto exit_function;
2147
0
                            }
2148
5
                        }
2149
5
                        pBNS->tot_st_cap -= delta;
2150
5
                        pBNS->tot_st_flow -= 2 * delta;
2151
5
                        pEdgeMinus->flow -= delta;
2152
5
                    }
2153
5
                    ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2154
5
                    (*pnNumRunBNS)++;
2155
5
                    if (ret < 0)
2156
0
                    {
2157
0
                        goto exit_function;
2158
0
                    }
2159
5
                    else
2160
5
                        if (ret == num_donors)
2161
5
                        {
2162
5
                            *pnTotalDelta += ret;
2163
5
                            num_success++;
2164
                            /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
2165
5
                        }
2166
0
                        else
2167
0
                        {
2168
0
                            ret = RI_ERR_PROGR;
2169
0
                            goto exit_function;
2170
0
                        }
2171
5
                }
2172
42
            }
2173
43
        }
2174
125k
    }
2175
18.5k
    if (ret_forbid_edges)
2176
143
    {
2177
        /* remove the marks */
2178
143
        RemoveForbiddenBondFlowBits(pBNS, forbidden_edge_test);
2179
143
    }
2180
18.5k
    ret = num_success;
2181
2182
18.5k
exit_function:
2183
2184
18.5k
    AllocEdgeList(&ChargeListAllExcept_DB_O, EDGE_LIST_FREE);
2185
2186
18.5k
    return ret;
2187
18.5k
}
2188
2189
2190
/****************************************************************************
2191
 Find and eliminate cases when Mobile H endpoint has radical on it
2192
 (typical for wrong P(VI)(=O)3OH
2193
****************************************************************************/
2194
int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT* pBNS,
2195
    BN_DATA* pBD,
2196
    StrFromINChI* pStruct,
2197
    inp_ATOM* at,
2198
    inp_ATOM* at2,
2199
    VAL_AT* pVA,
2200
    ALL_TC_GROUPS* pTCGroups,
2201
    int* pnNumRunBNS,
2202
    int* pnTotalDelta,
2203
    int forbidden_edge_mask)
2204
18.5k
{
2205
18.5k
    int i;
2206
2207
18.5k
    int ret2, ret, pass;
2208
18.5k
    int num_at = pStruct->num_atoms;
2209
18.5k
    int num_deleted_H = pStruct->num_deleted_H;
2210
18.5k
    int len_at = num_at + num_deleted_H;
2211
18.5k
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
2212
2213
18.5k
    int         j, k;
2214
18.5k
    int        cur_num_edges;
2215
18.5k
    BNS_EDGE* e;
2216
18.5k
    Vertex     v1, v2;
2217
2218
18.5k
    EdgeIndex* pFixedEdges;
2219
18.5k
    int        nNumEdgesToFix;
2220
2221
18.5k
    ret = 0;
2222
2223
    /* to simplify, prepare new at[] from pBNS */
2224
18.5k
    memcpy(at2, at, len_at * sizeof(at2[0]));
2225
18.5k
    pStruct->at = at2;
2226
18.5k
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2227
18.5k
    pStruct->at = at;
2228
18.5k
    if (ret2 < 0)
2229
0
    {
2230
0
        ret = ret2;
2231
0
        goto exit_function;
2232
0
    }
2233
2234
18.5k
    pFixedEdges = NULL;
2235
2236
18.5k
    nNumEdgesToFix = 0; /* cpunt nNumEdgesToFix only when pass==0 */
2237
18.5k
    cur_num_edges = 0; /* count cur_num_edges  only when pass==1; at the end they must be equal */
2238
37.1k
    for (pass = 0; pass < 2; pass++)
2239
37.0k
    {
2240
37.0k
        if (pass)
2241
18.5k
        {
2242
            /* 2nd pass: allocate edge storage */
2243
18.5k
            if (!nNumEdgesToFix)
2244
18.4k
            {
2245
18.4k
                break; /* nothing to do */
2246
18.4k
            }
2247
50
            pFixedEdges = (EdgeIndex*)inchi_malloc(nNumEdgesToFix * sizeof(pFixedEdges[0]));
2248
50
            if (!pFixedEdges)
2249
0
            {
2250
0
                ret = RI_ERR_ALLOC;
2251
0
                goto exit_function;
2252
0
            }
2253
50
        }
2254
144k
        for (i = 0; i < num_at; i++)
2255
125k
        {
2256
125k
            int neigh;
2257
125k
            if (pVA[i].cMetal)
2258
26.1k
            {
2259
27.7k
                for (j = 0; j < at2[i].valence; j++)
2260
1.55k
                {
2261
1.55k
                    neigh = at2[i].neighbor[j];
2262
1.55k
                    if (pVA[neigh].cNumValenceElectrons == 4 &&
2263
135
                        pVA[neigh].cPeriodicRowNumber == 1)
2264
127
                    {
2265
127
                        continue; /* ignore carbon */
2266
127
                    }
2267
1.42k
                    if (at2[i].bond_type[j] > BOND_TYPE_SINGLE && at2[neigh].charge &&
2268
113
                        !pVA[neigh].cMetal && pVA[neigh].cnListIndex > 0)
2269
113
                    {
2270
113
                        int cnBits = at2[neigh].charge > 0 ? MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) :
2271
113
                            MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0);
2272
113
                        int atBits = cnList[pVA[neigh].cnListIndex - 1].bits;
2273
238
                        for (k = 0; k < MAX_NUM_CN_BITS - 1; k++, atBits >>= cn_bits_shift)
2274
227
                        {
2275
                            /* ??? */
2276
227
                            if ((atBits & cnBits) == cnBits)
2277
102
                            {
2278
102
                                break;
2279
102
                            }
2280
227
                        }
2281
113
                        if (k == MAX_NUM_CN_BITS - 1)
2282
11
                        {
2283
11
                            continue;
2284
11
                        }
2285
102
                        if (pass == 0)
2286
51
                        {
2287
51
                            nNumEdgesToFix++;
2288
51
                        }
2289
51
                        else
2290
51
                        {
2291
51
                            pFixedEdges[cur_num_edges++] = pBNS->vert[i].iedge[j];
2292
51
                        }
2293
102
                    }
2294
1.42k
                }
2295
26.1k
            }
2296
125k
        }
2297
18.5k
    }
2298
2299
    /* restore the initial structures */
2300
18.5k
    memcpy(at2, at, ((long long)num_at + (long long)num_deleted_H) * sizeof(at2[0])); /* djb-rwth: cast operators added */
2301
2302
18.5k
    if (nNumEdgesToFix && pFixedEdges)
2303
50
    {
2304
50
        if (nNumEdgesToFix != cur_num_edges)
2305
0
        {
2306
0
            ret = RI_ERR_PROGR;
2307
0
            goto pre_exit_function; /* djb-rwth: fixing coverity ID #499637 */
2308
0
        }
2309
        /* change edge flow, fix the edges, and run BNS */
2310
101
        for (i = 0; i < nNumEdgesToFix; i++)
2311
51
        {
2312
51
            e = pBNS->edge + pFixedEdges[i];
2313
51
            v1 = e->neighbor1;
2314
51
            v2 = e->neighbor12 ^ v1;
2315
51
            e->flow--;
2316
51
            e->forbidden |= forbidden_edge_mask;
2317
51
            pBNS->vert[v1].st_edge.flow--;
2318
51
            pBNS->vert[v2].st_edge.flow--;
2319
51
            pBNS->tot_st_flow -= 2;
2320
51
            (*pnTotalDelta) -= 2;
2321
51
        }
2322
        /* Run BNS allowing to change any charges */
2323
50
        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2324
50
        (*pnNumRunBNS)++;
2325
50
        if (ret < 0)
2326
0
        {
2327
0
            goto pre_exit_function; /* djb-rwth: fixing coverity ID #499637 */
2328
0
        }
2329
50
        else
2330
50
        {
2331
50
            (*pnTotalDelta) += ret;
2332
50
        }
2333
        /* unfix the edges */
2334
101
        for (i = 0; i < nNumEdgesToFix; i++)
2335
51
        {
2336
51
            e = pBNS->edge + pFixedEdges[i];
2337
51
            e->forbidden &= inv_forbidden_edge_mask;
2338
51
        }
2339
50
        if (ret < 2 * nNumEdgesToFix)
2340
50
        {
2341
            /* not all fixes succeeded */
2342
50
            ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2343
50
            (*pnNumRunBNS)++;
2344
50
            if (ret < 0)
2345
0
            {
2346
0
                goto pre_exit_function; /* djb-rwth: fixing coverity ID #499637 */
2347
0
            }
2348
50
            else
2349
50
            {
2350
50
                (*pnTotalDelta) += ret;
2351
50
            }
2352
50
        }
2353
50
    }
2354
2355
18.5k
pre_exit_function:
2356
18.5k
    if (pFixedEdges)
2357
50
    {
2358
50
        inchi_free(pFixedEdges);
2359
50
        pFixedEdges = NULL;
2360
50
    }
2361
2362
18.5k
exit_function:
2363
18.5k
    return ret;
2364
18.5k
}
2365
2366
2367
2368
/**************************************************************************/
2369
/* In Reconnected structure change 'salt bonds' to 'coordination bonds    */
2370
/* for example, M-O-C=  ->  M(+)-O(-)-C=                                  */
2371
/* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+)     */
2372
/* However, in this release metal-organic compounds do not get much care  */
2373
int SaltBondsToCoordBonds(BN_STRUCT* pBNS,
2374
    BN_DATA* pBD,
2375
    StrFromINChI* pStruct,
2376
    inp_ATOM* at,
2377
    inp_ATOM* at2,
2378
    VAL_AT* pVA,
2379
    ALL_TC_GROUPS* pTCGroups,
2380
    int* pnNumRunBNS,
2381
    int* pnTotalDelta,
2382
    int forbidden_edge_mask)
2383
18.5k
{
2384
18.5k
    int i;
2385
2386
18.5k
    int ret2, ret, cur_success;
2387
18.5k
    int num_at = pStruct->num_atoms;
2388
18.5k
    int num_edges = pBNS->num_bonds + 2 * pBNS->num_atoms;
2389
18.5k
    int num_deleted_H = pStruct->num_deleted_H;
2390
18.5k
    int len_at = num_at + num_deleted_H;
2391
18.5k
    int inv_forbidden_edge_mask = ~forbidden_edge_mask;
2392
18.5k
    EDGE_LIST AllChargeEdges;
2393
2394
18.5k
    int         j, k, n;
2395
18.5k
    BNS_EDGE* pe, * pePlusMetal, * peMinusO;
2396
18.5k
    BNS_VERTEX* pv1, * pv2, * pvO, * pvM;
2397
18.5k
    Vertex     v1, v2, vPlusMinus;
2398
2399
18.5k
    EdgeIndex  ie, iePlusMetal, ieMinusO;
2400
2401
18.5k
    Vertex     vPathStart, vPathEnd;
2402
18.5k
    int        delta, nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
2403
2404
18.5k
    ret = 0;
2405
18.5k
    cur_success = 0;
2406
18.5k
    AllocEdgeList(&AllChargeEdges, EDGE_LIST_CLEAR);
2407
2408
18.5k
    if (pStruct->iInchiRec == INCHI_BAS || !pStruct->pSrm->bMetalAddFlower || pStruct->pSrm->nMetalMinBondOrder)
2409
18.5k
    {
2410
18.5k
        goto exit_function;
2411
18.5k
    }
2412
2413
    /* to simplify, prepare new at[] from pBNS */
2414
3
    memcpy(at2, at, len_at * sizeof(at2[0]));
2415
3
    pStruct->at = at2;
2416
3
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2417
3
    pStruct->at = at;
2418
3
    if (ret2 < 0)
2419
0
    {
2420
0
        ret = ret2;
2421
0
        goto exit_function;
2422
0
    }
2423
18
    for (i = 0; i < num_at; i++)
2424
15
    {
2425
15
        if (bIsMetalSalt(at2, i))
2426
0
        {
2427
0
            if (!AllChargeEdges.num_edges)
2428
0
            {
2429
                /*--------- one-time action: fix all bonds, charges, taut. group edges ------------*/
2430
0
                for (j = 0; j < num_at; j++)
2431
0
                {
2432
                    /* all bonds */
2433
0
                    for (k = 0; k < at2[j].valence; k++)
2434
0
                    {
2435
0
                        n = at2[j].neighbor[k];
2436
0
                        if (n < j && !pBNS->edge[ie = pBNS->vert[j].iedge[k]].forbidden &&
2437
0
                            (ret = AddToEdgeList(&AllChargeEdges, ie, num_edges)))
2438
0
                        {
2439
0
                            goto exit_function;
2440
0
                        }
2441
0
                    }
2442
                    /* charge edges */
2443
0
                    if ((ie = pVA[j].nCMinusGroupEdge - 1) >= 0 && !pBNS->edge[ie].forbidden &&
2444
0
                        (ret = AddToEdgeList(&AllChargeEdges, ie, num_edges)))
2445
0
                    {
2446
0
                        goto exit_function;
2447
0
                    }
2448
0
                    if ((ie = pVA[j].nCPlusGroupEdge - 1) >= 0 && !pBNS->edge[ie].forbidden &&
2449
0
                        (ret = AddToEdgeList(&AllChargeEdges, ie, num_edges)))
2450
0
                    {
2451
0
                        goto exit_function;
2452
0
                    }
2453
0
                }
2454
                /* taut group edges */
2455
0
                for (j = 0; j < pTCGroups->num_tgroups; j++)
2456
0
                {
2457
0
                    pv1 = pBNS->vert + (v1 = pTCGroups->pTCG[j].nVertexNumber); /* t-group vertex */ /* djb-rwth: ignoring LLVM warning: see comment below */
2458
0
                    for (k = 0; k < pv1->num_adj_edges; k++)
2459
0
                    {
2460
                        /* ie, pe - tautomeric atom edge; pv2 - endpoint vertex */
2461
                        /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */
2462
0
                        pv2 = pBNS->vert + (pe = pBNS->edge + (ie = pv1->iedge[k]))->neighbor1; /* djb-rwth: ignoring LLVM warning: see comment above */
2463
0
                        if ((ret = AddToEdgeList(&AllChargeEdges, ie, num_edges))) /* djb-rwth: addressing LLVM warning */
2464
0
                        {
2465
0
                            goto exit_function;
2466
0
                        }
2467
0
                    }
2468
0
                }
2469
                /*---------------------------------------------------------------*/
2470
0
            }
2471
            /* replace all single bonds to neutral neighbors with zero-order bonds
2472
               allow neighbor charge change to (-1) and metal atom charge increment +1 */
2473
0
            for (k = 0; k < at2[i].valence; k++)
2474
0
            {
2475
0
                n = at2[i].neighbor[k];
2476
0
                pe = pBNS->edge + pBNS->vert[i].iedge[k];
2477
0
                if (at2[n].charge || at2[i].bond_type[k] != BOND_TYPE_SINGLE)
2478
0
                {
2479
0
                    continue;
2480
0
                }
2481
0
                iePlusMetal = pVA[i].nCPlusGroupEdge - 1;
2482
0
                ieMinusO = pVA[n].nCMinusGroupEdge - 1;
2483
2484
0
                if (pe->flow != 1 || pe->forbidden || iePlusMetal < 0)
2485
0
                {
2486
0
                    continue;
2487
0
                }
2488
0
                pePlusMetal = pBNS->edge + iePlusMetal;
2489
0
                if (pePlusMetal->flow <= 0)
2490
0
                {
2491
0
                    continue; /* to add (+) to metal this flow must be decremented */
2492
0
                }
2493
0
                if (ieMinusO >= 0)
2494
0
                {
2495
                    /* usually does not happen */
2496
0
                    peMinusO = pBNS->edge + ieMinusO;
2497
2498
0
                    if (peMinusO->flow || pePlusMetal->forbidden || peMinusO->forbidden)
2499
0
                    {
2500
0
                        continue;
2501
0
                    }
2502
2503
                    /* decrement bond order to 0 */
2504
0
                    delta = 1;
2505
0
                    pv1 = pBNS->vert + (v1 = pe->neighbor1);
2506
0
                    pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1);
2507
2508
0
                    pe->flow -= delta;
2509
0
                    pv1->st_edge.flow -= delta;
2510
0
                    pv2->st_edge.flow -= delta;
2511
0
                    pBNS->tot_st_flow -= 2 * delta;
2512
2513
0
                    SetForbiddenEdgeMask(pBNS, &AllChargeEdges, forbidden_edge_mask);
2514
0
                    pePlusMetal->forbidden &= inv_forbidden_edge_mask;
2515
0
                    peMinusO->forbidden &= inv_forbidden_edge_mask;
2516
2517
0
                    ret = RunBnsTestOnce(pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
2518
0
                        &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms);
2519
2520
0
                    if (ret == 1 && ((vPathEnd == v1 && vPathStart == v2) ||
2521
0
                        (vPathEnd == v2 && vPathStart == v1)) /*&& nDeltaCharge > 0*/) /* djb-rwth: addressing LLVM warnings */
2522
0
                    {
2523
                        /* (+)charge was just moved, no change in number of charges */
2524
0
                        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2525
0
                        if (ret > 0)
2526
0
                        {
2527
0
                            (*pnNumRunBNS)++;
2528
0
                            cur_success++; /* 01 */
2529
0
                        }
2530
0
                    }
2531
0
                    else
2532
0
                    {
2533
0
                        pe->flow += delta; /* roll back */
2534
0
                        pv1->st_edge.flow += delta;
2535
0
                        pv2->st_edge.flow += delta;
2536
0
                        pBNS->tot_st_flow += 2 * delta;
2537
0
                    }
2538
0
                    RemoveForbiddenEdgeMask(pBNS, &AllChargeEdges, forbidden_edge_mask);
2539
0
                }
2540
0
                else
2541
0
                {
2542
0
                    if (NO_VERTEX != (vPlusMinus = GetPlusMinusVertex(pBNS, pTCGroups, 1, 1)))
2543
0
                    {
2544
                        /* manually add (-) charge to O and (+) charge to metal */
2545
                        /* decrement bond order to 0 */
2546
                        /*---------------------------------------------------------------------------*/
2547
                        /*                                                                           */
2548
                        /*      (+/-)*               (+/-)           Result:                         */
2549
                        /*        |                    ||                                            */
2550
                        /*        |                    ||            - Added (+) to M                */
2551
                        /*       (+)super             (+)super       - Incremented bond M-O          */
2552
                        /*        ||                   |                                             */
2553
                        /*        ||          =>       |             To make this attachment H,      */
2554
                        /*       (Y)                  (Y)            increment                       */
2555
                        /*        |                    ||            pTCGroups->pTCG[itg].tg_num_H   */
2556
                        /*        |                    ||                                            */
2557
                        /*       (+)metal             (+)hetero      Technical details:              */
2558
                        /*         \\                   \            increase capacities of          */
2559
                        /*           M                    M(+)       edges to (+/-) otherwise        */
2560
                        /*           |                    ||         flow may not be able to         */
2561
                        /*          -O*                -O-O          increase                        */
2562
                        /*                                                                           */
2563
                        /*   After that change M=O bond order from 2 to 0                            */
2564
                        /*---------------------------------------------------------------------------*/
2565
0
                        int i1, j1, k1;
2566
0
                        delta = 1;
2567
0
                        pvO = pBNS->vert + n;
2568
0
                        pvM = pBNS->vert + i;
2569
                        /* Increment st_edge.cap on (+/-) vertex */
2570
0
                        pBNS->vert[vPlusMinus].st_edge.cap += delta;
2571
                        /* Increment st_edge.cap on O */
2572
0
                        pvO->st_edge.cap += delta;
2573
                        /* increment cap on M-O edge */
2574
0
                        pe->cap += delta;
2575
                        /* total cap count */
2576
0
                        pBNS->tot_st_cap += 2 * delta;
2577
2578
0
                        v1 = vPlusMinus;
2579
0
                        v2 = n; /* atom O */
2580
2581
                        /* increase capacities of edges to Y  */
2582
0
                        for (i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1++)
2583
0
                        {
2584
0
                            j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus;
2585
0
                            for (k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1++)
2586
0
                            {
2587
0
                                pBNS->edge[pBNS->vert[j1].iedge[k1]].cap += delta;
2588
0
                            }
2589
0
                        }
2590
0
                        SetForbiddenEdgeMask(pBNS, &AllChargeEdges, forbidden_edge_mask);
2591
0
                        pePlusMetal->forbidden &= inv_forbidden_edge_mask;
2592
0
                        pe->forbidden &= inv_forbidden_edge_mask;
2593
2594
0
                        ret = RunBnsTestOnce(pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
2595
0
                            &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms);
2596
0
                        cur_success = 0;
2597
0
                        if (ret == 1 && ((vPathEnd == v1 && vPathStart == v2) ||
2598
0
                            (vPathEnd == v2 && vPathStart == v1)) /*&& nDeltaCharge == 1*/) /* djb-rwth: addressing LLVM warnings */
2599
0
                        {
2600
                            /* Added (+)charge to -N< => nDeltaCharge == 1 */
2601
                            /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */
2602
0
                            ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2603
0
                            if (ret > 0)
2604
0
                            {
2605
0
                                (*pnNumRunBNS)++;
2606
0
                                cur_success++; /* 01 */
2607
0
                            }
2608
0
                        }
2609
0
                        if (cur_success)
2610
0
                        {
2611
                            /* set bond M=O order = 0 */
2612
0
                            if (pe->flow != 2 * delta)
2613
0
                            {
2614
0
                                ret = RI_ERR_PROGR;
2615
0
                                goto exit_function;
2616
0
                            }
2617
                            /* reduce pe bond order by 2*delta */
2618
0
                            pe->flow -= 2 * delta;
2619
0
                            pvO->st_edge.cap -= 2 * delta;
2620
0
                            pvO->st_edge.flow -= 2 * delta;
2621
0
                            pvM->st_edge.flow -= 2 * delta;
2622
0
                            pvM->st_edge.cap -= 2 * delta;
2623
0
                            pBNS->tot_st_cap -= 3 * delta;
2624
0
                            pBNS->tot_st_flow -= 4 * delta;
2625
                            /* fix M-O bond order to zero */
2626
0
                            pe->cap -= 2 * delta;
2627
                            /* add fixed (-) charge to O */
2628
0
                            pVA[n].cInitCharge -= delta;
2629
0
                        }
2630
0
                        else
2631
0
                        {
2632
                            /* failed */
2633
0
                            pBNS->vert[vPlusMinus].st_edge.cap -= delta;
2634
0
                            pvO->st_edge.cap -= delta;
2635
                            /*pTCGroups->pTCG[itg].edges_cap     -= delta;*/ /* ???bug??? - commented out 2006-03-22 */
2636
0
                            pBNS->tot_st_cap -= 2 * delta;
2637
                            /* decrease capacities of edges to Y  */
2638
0
                            for (i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1++)
2639
0
                            {
2640
0
                                j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus;
2641
0
                                for (k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1++)
2642
0
                                {
2643
0
                                    pBNS->edge[pBNS->vert[j1].iedge[k1]].cap -= delta;
2644
0
                                }
2645
0
                            }
2646
0
                        }
2647
0
                        RemoveForbiddenEdgeMask(pBNS, &AllChargeEdges, forbidden_edge_mask);
2648
0
                    }
2649
0
                }
2650
0
            }
2651
0
        }
2652
15
    }
2653
2654
18.5k
exit_function:
2655
2656
18.5k
    AllocEdgeList(&AllChargeEdges, EDGE_LIST_FREE);
2657
2658
18.5k
    return ret;
2659
3
}
2660
2661
2662
#if ( KEEP_METAL_EDGE_FLOW == 1 )
2663
2664
2665
/****************************************************************************/
2666
int ForbidMetalCarbonEdges(BN_STRUCT* pBNS,
2667
    inp_ATOM* at,
2668
    int num_at,
2669
    VAL_AT* pVA,
2670
    ALL_TC_GROUPS* pTCGroups,
2671
    EDGE_LIST* pMetalCarbonEdges,
2672
    int forbidden_edge_mask)
2673
{
2674
2675
    int i, j, neigh, nNumEdgeMetalCarbon = 0, pass = 0, ret = 0;
2676
    BNS_VERTEX* pVert, * pNeigh;
2677
    BNS_EDGE* pEdge;
2678
2679
    /* count carbon-metal edges */
2680
2681
    if (pTCGroups->num_metal_atoms)
2682
    {
2683
    fill_ForbiddenEdgesMetalCarbon:
2684
        for (i = 0; i < num_at; i++)
2685
        {
2686
            if (pVA[i].cMetal && pVA[i].cNumBondsToMetal)
2687
            {
2688
                pVert = pBNS->vert + i;
2689
                for (j = 0; j < pVert->num_adj_edges; j++)
2690
                {
2691
                    pEdge = pBNS->edge + pVert->iedge[j];
2692
                    neigh = pEdge->neighbor12 ^ i;
2693
                    pNeigh = pBNS->vert + neigh;
2694
                    if (!IS_BNS_VT_ATOM(pNeigh->type))
2695
                        continue;
2696
                    if (at[neigh].endpoint)
2697
                        continue;
2698
                    if (pVA[neigh].cNumValenceElectrons == 4 && pVA[neigh].cPeriodicRowNumber == 1 &&
2699
                        pNeigh->st_edge.cap >= at[neigh].valence + 1)
2700
                    {
2701
                        if (pass)
2702
                        {
2703
                            if (ret = AddToEdgeList(pMetalCarbonEdges, pVert->iedge[j], 0))
2704
                            {
2705
                                goto exit_function;
2706
                            }
2707
                            pEdge->forbidden |= forbidden_edge_mask;
2708
                        }
2709
                        else
2710
                        {
2711
                            nNumEdgeMetalCarbon++;
2712
                        }
2713
                    }
2714
                }
2715
            }
2716
        }
2717
        if (!pass && nNumEdgeMetalCarbon)
2718
        {
2719
            if (ret = AllocEdgeList(pMetalCarbonEdges, nNumEdgeMetalCarbon))
2720
            {
2721
                goto exit_function;
2722
            }
2723
            pass++;
2724
            goto fill_ForbiddenEdgesMetalCarbon;
2725
        }
2726
    }
2727
2728
exit_function:
2729
2730
    return ret;
2731
}
2732
2733
2734
#endif
2735
2736
2737
/****************************************************************************
2738
 Restore bonds & charges
2739
*****************************************************************************/
2740
int RunBnsRestore1(CANON_GLOBALS* pCG,
2741
    INCHI_CLOCK* ic,
2742
    ICHICONST INPUT_PARMS* ip,
2743
    STRUCT_DATA* sd,
2744
    BN_STRUCT* pBNS,
2745
    BN_DATA* pBD,
2746
    StrFromINChI* pStruct,
2747
    VAL_AT* pVA,
2748
    ALL_TC_GROUPS* pTCGroups,
2749
    INChI* pInChI[],
2750
    long num_inp,
2751
    int bHasSomeFixedH)
2752
18.5k
{
2753
18.5k
    int        nNumRunBNS = 0;
2754
2755
18.5k
    EDGE_LIST CarbonChargeEdges, MetalCarbonEdges, Nplus2BondsEdges;
2756
2757
18.5k
    int nTotalDelta = 0, ret = 0; /* djb-rwth: removing redundant variables */
2758
18.5k
    inp_ATOM* at = pStruct->at;
2759
18.5k
    inp_ATOM* at2 = NULL; /* restored structure */
2760
18.5k
    inp_ATOM* at3 = NULL; /* structure for calculating one InChI */
2761
18.5k
    int     num_at = pStruct->num_atoms;
2762
18.5k
    int     num_deleted_H = pStruct->num_deleted_H;
2763
#ifdef _DEBUG
2764
    int ret2;
2765
#endif
2766
2767
#if ( KEEP_METAL_EDGE_FLOW == 1 )
2768
    BNS_VERTEX* pVert, * pNeigh;
2769
    int         j, neigh;
2770
#endif
2771
2772
    /* Edge lists initialization */
2773
18.5k
    AllocEdgeList(&CarbonChargeEdges, EDGE_LIST_CLEAR);
2774
18.5k
    AllocEdgeList(&MetalCarbonEdges, EDGE_LIST_CLEAR);
2775
18.5k
    AllocEdgeList(&Nplus2BondsEdges, EDGE_LIST_CLEAR);
2776
2777
18.5k
    if (pStruct->iMobileH == TAUT_NON &&
2778
554
        (ret = FillOutExtraFixedHDataInChI(pStruct, pInChI)))
2779
0
    {
2780
0
        goto exit_function;
2781
0
    }
2782
2783
18.5k
    if ((!at2 && !(at2 = (inp_ATOM*)inchi_malloc(((long long)num_at + (long long)num_deleted_H) * sizeof(at2[0])))) ||
2784
18.5k
        (!at3 && !(at3 = (inp_ATOM*)inchi_malloc(((long long)num_at + (long long)num_deleted_H) * sizeof(at3[0]))))) /* djb-rwth: cast operators added; addressing LLVM warning */
2785
0
    {
2786
0
        inchi_free(at2);
2787
0
        inchi_free(at3);
2788
0
        return RI_ERR_ALLOC;
2789
0
    }
2790
2791
18.5k
    if (0 > (ret = ForbidCarbonChargeEdges(pBNS, pTCGroups, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP)))
2792
0
    {
2793
0
        goto exit_function;
2794
0
    }
2795
2796
#if ( KEEP_METAL_EDGE_FLOW == 1 )
2797
    /* count edges of -C(IV)< carbons connected to metals */
2798
    if (0 > (ret = ForbidMetalCarbonEdges(pBNS, at, num_at, pVA, pTCGroups, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP)))
2799
    {
2800
        goto exit_function;
2801
    }
2802
#endif
2803
18.5k
    if (0 > (ret = ForbidNintrogenPlus2BondsInSmallRings(pBNS, at, num_at, pVA, 6,
2804
18.5k
        pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP)))
2805
0
    {
2806
0
        goto exit_function;
2807
0
    }
2808
2809
    /*********** Run BNS #1: no charge on carbons and =N= ***************/
2810
18.5k
    if (Nplus2BondsEdges.num_edges)
2811
234
    {
2812
        /* Run BNS leaving carbon charges unchanged */
2813
234
        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2814
234
        nNumRunBNS++;
2815
234
        if (ret < 0)
2816
0
        {
2817
0
            goto exit_function;
2818
0
        }
2819
234
        else
2820
234
        {
2821
234
            nTotalDelta += ret;
2822
234
        }
2823
234
        RemoveForbiddenEdgeMask(pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP);
2824
234
        AllocEdgeList(&Nplus2BondsEdges, EDGE_LIST_FREE);
2825
234
    }
2826
#ifdef _DEBUG
2827
    /* debug only */
2828
    memcpy(at2, at, ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H) * sizeof(at2[0])); /* djb-rwth: cast operators added */
2829
    pStruct->at = at2;
2830
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2831
    pStruct->at = at;
2832
#endif
2833
    /*************************** extend min ring size to 8 ****************************/
2834
18.5k
    if (0 > (ret = ForbidNintrogenPlus2BondsInSmallRings(pBNS, at, num_at, pVA, 8,
2835
18.5k
        pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP)))
2836
0
    {
2837
0
        goto exit_function;
2838
0
    }
2839
18.5k
    if (Nplus2BondsEdges.num_edges)
2840
288
    {
2841
        /* Run BNS leaving carbon charges unchanged */
2842
288
        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2843
288
        nNumRunBNS++;
2844
288
        if (ret < 0)
2845
0
        {
2846
0
            goto exit_function;
2847
0
        }
2848
288
        else
2849
288
        {
2850
288
            nTotalDelta += ret;
2851
288
        }
2852
288
        RemoveForbiddenEdgeMask(pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP);
2853
288
        AllocEdgeList(&Nplus2BondsEdges, EDGE_LIST_FREE);
2854
288
    }
2855
#ifdef _DEBUG
2856
    /* debug only */
2857
    memcpy(at2, at, ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H) * sizeof(at2[0])); /* djb-rwth: cast operators added */
2858
    pStruct->at = at2;
2859
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2860
    pStruct->at = at;
2861
#endif
2862
    /*******************************************************************/
2863
18.5k
    if (CarbonChargeEdges.num_edges > 0)
2864
340
    {
2865
        /* Run BNS leaving carbon charges unchanged */
2866
340
        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2867
340
        nNumRunBNS++;
2868
340
        if (ret < 0)
2869
0
        {
2870
0
            goto exit_function;
2871
0
        }
2872
340
        else
2873
340
        {
2874
340
            nTotalDelta += ret;
2875
340
        }
2876
340
        RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP);
2877
340
        AllocEdgeList(&CarbonChargeEdges, EDGE_LIST_FREE);
2878
340
    }
2879
#ifdef _DEBUG
2880
    /* debug only */
2881
    memcpy(at2, at, ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H) * sizeof(at2[0])); /* djb-rwth: cast operators added */
2882
    pStruct->at = at2;
2883
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2884
    pStruct->at = at;
2885
#endif
2886
    /*******************************************************************/
2887
18.5k
    if (MetalCarbonEdges.num_edges > 0)
2888
0
    {
2889
        /* Run BNS leaving carbon charges unchanged */
2890
0
        ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2891
0
        nNumRunBNS++;
2892
0
        if (ret < 0)
2893
0
        {
2894
0
            goto exit_function;
2895
0
        }
2896
0
        else
2897
0
        {
2898
0
            nTotalDelta += ret;
2899
0
        }
2900
0
        RemoveForbiddenEdgeMask(pBNS, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP);
2901
0
        AllocEdgeList(&MetalCarbonEdges, EDGE_LIST_FREE);
2902
0
    }
2903
    /*******************************************************************/
2904
    /* Run BNS allowing to change any charges */
2905
18.5k
    ret = RunBnsRestoreOnce(pBNS, pBD, pVA, pTCGroups);
2906
18.5k
    nNumRunBNS++;
2907
18.5k
    if (ret < 0)
2908
0
    {
2909
0
        goto exit_function;
2910
0
    }
2911
18.5k
    else
2912
18.5k
    {
2913
18.5k
        nTotalDelta += ret;
2914
18.5k
    }
2915
#ifdef _DEBUG
2916
    /* debug only */
2917
    memcpy(at2, at, ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H) * sizeof(at2[0])); /* djb-rwth: cast operators added */
2918
    pStruct->at = at2;
2919
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2920
    pStruct->at = at;
2921
#endif
2922
2923
18.5k
#if ( BNS_RAD_SEARCH == 1 )
2924
    /****************************************************************************/
2925
    /* move unfulfilled 'radicals' from ChargeStruct to atoms         */
2926
    /* and set change charges of affected atoms to fit total charge   */
2927
18.5k
    ret = MoveRadToAtomsAddCharges(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP);
2928
18.5k
    if (ret < 0)
2929
4
    {
2930
4
        goto exit_function;
2931
4
    }
2932
18.5k
#endif
2933
    /**************************************************************/
2934
    /**************************************************************/
2935
    /*****           fix restore inconsistencies              *****/
2936
    /**************************************************************/
2937
    /**************************************************************/
2938
#ifdef _DEBUG
2939
    /* debug only */
2940
    memcpy(at2, at, ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H) * sizeof(at2[0])); /* djb-rwth: cast operators added */
2941
    pStruct->at = at2;
2942
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2943
    pStruct->at = at;
2944
#endif
2945
2946
    /* rearrange (+) and (-) edges flow so that there is no (+)flow=0 and (-)flow=1 */
2947
18.5k
    ret = RearrangePlusMinusEdgesFlow(pBNS, pBD, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP);
2948
18.5k
    if (ret < 0)
2949
0
    {
2950
0
        goto exit_function;
2951
0
    }
2952
2953
    /*****************************************************************/
2954
    /*       Increment zero order metal bonds to heteroatoms         */
2955
    /*****************************************************************/
2956
18.5k
    ret = IncrementZeroOrderBondsToHeteroat(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2957
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2958
18.5k
    if (ret < 0)
2959
1
    {
2960
1
        goto exit_function;
2961
1
    }
2962
2963
#ifdef _DEBUG
2964
    /* debug only */
2965
    memcpy(at2, at, ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H) * sizeof(at2[0])); /* djb-rwth: cast operators added */
2966
    pStruct->at = at2;
2967
    ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
2968
    pStruct->at = at;
2969
#endif
2970
2971
#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 )
2972
    /*****************************************************************/
2973
    /* move charges from heteroatoms to metal atoms                  */
2974
    /*****************************************************************/
2975
    ret = MoveChargeFromHeteroatomsToMetals(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2976
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2977
    if (ret < 0)
2978
    {
2979
        goto exit_function;
2980
    }
2981
#endif
2982
    /***********************************************************************
2983
            NH2                NH2
2984
               \                  \
2985
                C==S(+)-   =>      C(+)-S-   where NH2 are not tautomeric
2986
               /                  /
2987
            NH2                NH2
2988
    ************************************************************************/
2989
18.5k
    ret = MovePlusFromS2DiaminoCarbon(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2990
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2991
18.5k
    if (ret < 0)
2992
3
    {
2993
3
        goto exit_function;
2994
3
    }
2995
    /*****************************************************************/
2996
    /*       Avoid charge separation on heteroatoms                  */
2997
    /*****************************************************************/
2998
18.5k
    ret = EliminateChargeSeparationOnHeteroatoms(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2999
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, 0);
3000
18.5k
    if (ret < 0)
3001
0
    {
3002
0
        goto exit_function;
3003
0
    }
3004
18.5k
    if (ret)
3005
406
    {
3006
        /*charge separation remains; allow changes of stereobonds in a ring and try again */
3007
406
        ret = EliminateChargeSeparationOnHeteroatoms(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3008
406
            &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP,
3009
406
            BNS_EDGE_FORBIDDEN_MASK);
3010
406
        if (ret < 0)
3011
0
        {
3012
0
            goto exit_function;
3013
0
        }
3014
406
    }
3015
    /*****************************************************************/
3016
    /*         convert N#N(+)-N= into N(-)=N(+)=N-                   */
3017
    /*****************************************************************/
3018
18.5k
    ret = RestoreNNNgroup(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3019
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3020
18.5k
    if (ret < 0)
3021
0
    {
3022
0
        goto exit_function;
3023
0
    }
3024
    /*****************************************************************/
3025
    /*     convert Metal(q)-N(-)-O(-) Metal(q-2)-N=O (local change)  */
3026
    /*****************************************************************/
3027
18.5k
    ret = FixMetal_Nminus_Ominus(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3028
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3029
18.5k
    if (ret < 0)
3030
0
    {
3031
0
        goto exit_function;
3032
0
    }
3033
    /*****************************************************************/
3034
    /*         convert N(-)=C= into N#C-         -                   */
3035
    /*****************************************************************/
3036
18.5k
    ret = RestoreCyanoGroup(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3037
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3038
18.5k
    if (ret < 0)
3039
0
    {
3040
0
        goto exit_function;
3041
0
    }
3042
    /*****************************************************************/
3043
    /*         convert C(+)#N(+)- into C(-)#N(+)-                    */
3044
    /*****************************************************************/
3045
18.5k
    ret = RestoreIsoCyanoGroup(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3046
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3047
18.5k
    if (ret < 0)
3048
0
    {
3049
0
        goto exit_function;
3050
0
    }
3051
    /*****************************************************************/
3052
    /*         eliminate =N(V)= if possible                          */
3053
    /*                    |                                          */
3054
    /*****************************************************************/
3055
18.5k
    ret = EliminateNitrogen5Val3Bonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3056
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3057
18.5k
    if (ret < 0)
3058
0
    {
3059
0
        goto exit_function;
3060
0
    }
3061
3062
    /*****************************************************************/
3063
    /*                    |      |                                   */
3064
    /*         convert   -S- to =S= if possible                      */
3065
    /*                    |      |                                   */
3066
    /*****************************************************************/
3067
18.5k
    ret = Convert_SIV_to_SVI(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3068
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3069
18.5k
    if (ret < 0)
3070
0
    {
3071
0
        goto exit_function;
3072
0
    }
3073
3074
    /*****************************************************************/
3075
    /*                  =N(+)=O     =N-O(-)                          */
3076
    /*         convert           => if possible                      */
3077
    /*                  Metal(q)    Metal(q+2)                       */
3078
    /*****************************************************************/
3079
18.5k
    ret = PlusFromDB_N_DB_O_to_Metal(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3080
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3081
18.5k
    if (ret < 0)
3082
0
    {
3083
0
        goto exit_function;
3084
0
    }
3085
3086
    /*****************************************************************/
3087
    /*  forbidden edges prevents required in InChI tautomerism       */
3088
    /*  incorrectly restored mobile H mix separate tautomeric groups */
3089
    /*  because an edge may not become forbidden                     */
3090
    /* note: removes this 'forbidden_edge' bit from ALL edges        */
3091
    /*****************************************************************/
3092
18.5k
    ret = MoveMobileHToAvoidFixedBonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3093
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3094
3095
18.5k
    if (ret < 0)
3096
0
    {
3097
0
        goto exit_function;
3098
0
    }
3099
    /**************************************************************************/
3100
    /* 2. Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */
3101
    /* djb-rwth: removing redundant code */
3102
18.5k
    if (pStruct->iMobileH == TAUT_NON)
3103
554
    {
3104
554
        ret = RemoveRadFromMobileHEndpointFixH(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3105
554
            &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3106
554
    }
3107
17.9k
    else
3108
17.9k
    {
3109
17.9k
        ret = RemoveRadFromMobileHEndpoint(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3110
17.9k
            &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3111
17.9k
    }
3112
18.5k
    if (ret < 0)
3113
0
    {
3114
0
        goto exit_function;
3115
0
    }
3116
    /* djb-rwth: removing redundant code */
3117
    /**************************************************************/
3118
    /* make bonds between a charged heteroatom and a metal single */
3119
18.5k
    ret = MakeSingleBondsMetal2ChargedHeteroat(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3120
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3121
18.5k
    if (ret < 0)
3122
0
    {
3123
0
        goto exit_function;
3124
0
    }
3125
    /**************************************************************/
3126
    /* move (+) charges to >N- and other centerpoints             */
3127
18.5k
    ret = MoveChargeToMakeCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3128
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3129
18.5k
    if (ret < 0)
3130
0
    {
3131
0
        goto exit_function;
3132
0
    }
3133
3134
    /**************************************************************************/
3135
    /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */
3136
18.5k
    ret = MoveChargeToRemoveCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3137
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3138
18.5k
    if (ret < 0)
3139
0
    {
3140
0
        goto exit_function;
3141
0
    }
3142
    /**************************************************************************/
3143
    /* Find A=X< where all bonds to X except A=X are marked as stereogenic    */
3144
    /* make bonds A=X single                                                  */
3145
18.5k
    ret = CheckAndRefixStereobonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3146
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3147
18.5k
    if (ret < 0)
3148
0
    {
3149
0
        goto exit_function;
3150
0
    }
3151
    /**************************************************************************/
3152
    /* In Reconnected structure change 'salt bonds' to 'coordination bonds    */
3153
    /* for example, M-O-C=  ->  M(+)-O(-)-C=                                  */
3154
    /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+)     */
3155
    /* However, in this release metal-organic compounds do not get much care  */
3156
18.5k
    ret = SaltBondsToCoordBonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
3157
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
3158
18.5k
    if (ret < 0)
3159
0
    {
3160
0
        goto exit_function;
3161
0
    }
3162
    /**************************************************************************/
3163
    /* Normalize the structure and compare t-groups and stereobonds           */
3164
18.5k
    ret = NormalizeAndCompare(pCG, ic, ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH,
3165
18.5k
        &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, BNS_EDGE_FORBIDDEN_MASK);
3166
18.5k
    if (ret < 0)
3167
17.9k
    {
3168
17.9k
        goto exit_function;
3169
17.9k
    }
3170
    /**************************************************************************/
3171
    /* Create InChI out of the restored structure                             */
3172
3173
3174
    /*ret = nTotalDelta;*/
3175
3176
18.5k
exit_function:
3177
3178
18.5k
    pStruct->at = at;
3179
18.5k
    pStruct->at2 = at2;
3180
18.5k
    at2 = NULL;
3181
18.5k
    AllocEdgeList(&CarbonChargeEdges, EDGE_LIST_FREE);
3182
18.5k
    AllocEdgeList(&MetalCarbonEdges, EDGE_LIST_FREE);
3183
18.5k
    AllocEdgeList(&Nplus2BondsEdges, EDGE_LIST_FREE);
3184
18.5k
    if (at2)
3185
0
    {
3186
0
        inchi_free(at2);
3187
0
    }
3188
18.5k
    if (at3)
3189
18.5k
    {
3190
18.5k
        inchi_free(at3);
3191
18.5k
    }
3192
3193
18.5k
    return ret;
3194
18.5k
}
3195
3196
3197
/****************************************************************************/
3198
int RestoreAtomMakeBNS(INCHI_CLOCK* ic, CANON_GLOBALS* pCG,
3199
    ICHICONST INPUT_PARMS* ip,
3200
    STRUCT_DATA* sd,
3201
    StrFromINChI* pStruct,
3202
    int iComponent,
3203
    int iAtNoOffset,
3204
    INChI* pInChI[],
3205
    const char* szCurHdr,
3206
    long num_inp,
3207
    int bHasSomeFixedH)
3208
37.3k
{
3209
37.3k
    int i, j, ret = 0, ret2;
3210
    /*int nDelta, nTotalDelta;*/
3211
37.3k
    VAL_AT* pVA = NULL;
3212
37.3k
    VAL_AT    va1;
3213
37.3k
    int    num_at = pStruct->num_atoms;
3214
37.3k
    inp_ATOM* at = pStruct->at;
3215
37.3k
    ALL_TC_GROUPS   TCGroups;
3216
37.3k
    ALL_TC_GROUPS* pTCGroups = &TCGroups;
3217
37.3k
    int            nAddEdges2eachAtom = 2, nAddVertices = 0;
3218
3219
37.3k
    BFS_Q bfsq;
3220
3221
    /* BNS creation */
3222
37.3k
    BN_STRUCT* pBNS = NULL;
3223
37.3k
    BN_DATA* pBD = NULL;
3224
37.3k
    int            nNum_changed_bonds = 0;
3225
37.3k
    int            bTreatMoreAtomsAsMetals = 0, bSecondPassNewMetals = 0;
3226
37.3k
    int            nMaxAddAtoms = 2, nMaxAddEdges = 2, max_altp = BN_MAX_ALTP;
3227
3228
37.3k
    memset(pTCGroups, 0, sizeof(pTCGroups[0])); /* djb-rwth: memset_s C11/Annex K variant? */
3229
708k
    for (i = 0; i < NUM_TCGROUP_TYPES; i++)
3230
671k
    {
3231
671k
        pTCGroups->nGroup[i] = TCG_None; /* unassigned */
3232
671k
    }
3233
37.3k
    pTCGroups->iComponent = iComponent;
3234
37.3k
    pTCGroups->iAtNoOffset = iAtNoOffset;
3235
3236
37.3k
    if (num_at == 1)
3237
18.7k
    {
3238
        /* single atom -- no bonds to restore */
3239
18.7k
        inp_ATOM* at2 = (inp_ATOM*)inchi_malloc(sizeof(at2[0]) * ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H)); /* djb-rwth: cast operators added */
3240
18.7k
        inp_ATOM* at3 = (inp_ATOM*)inchi_malloc(sizeof(at3[0]) * ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H)); /* djb-rwth: cast operators added */
3241
18.7k
        pStruct->at2 = at2;
3242
18.7k
        at[0].charge = pInChI[0]->nTotalCharge;
3243
18.7k
        if (at2)
3244
18.7k
        {
3245
18.7k
            memcpy(at2, at, sizeof(at2[0]) * ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H)); /* djb-rwth: cast operators added */
3246
18.7k
        }
3247
18.7k
        if (!at2 || !at3)
3248
0
        {
3249
0
            if (at3) inchi_free(at3);
3250
0
            return RI_ERR_ALLOC;
3251
0
        }
3252
18.7k
        ret = MakeOneInChIOutOfStrFromINChI(pCG, ic, ip, sd, pStruct, pStruct->at2, at3, pTCGroups);
3253
        /* clean up */
3254
56.2k
        for (i = 0; i < TAUT_NUM; i++)
3255
37.4k
        {
3256
37.4k
            Free_INChI(&pStruct->pOneINChI[i]);
3257
37.4k
            Free_INChI_Aux(&pStruct->pOneINChI_Aux[i]);
3258
37.4k
            FreeInpAtomData(pStruct->pOne_norm_data[i]);
3259
37.4k
            if (pStruct->pOne_norm_data[i])
3260
18.7k
            {
3261
18.7k
                inchi_free(pStruct->pOne_norm_data[i]);
3262
18.7k
                pStruct->pOne_norm_data[i] = NULL;
3263
18.7k
            }
3264
37.4k
        }
3265
        /* djb-rwth: fixing oss-fuzz issue #69602 */
3266
        /* free_t_group_info(&pStruct->One_ti); */
3267
18.7k
        inchi_free(at3);
3268
3269
18.7k
        return ret;
3270
18.7k
    }
3271
3272
18.5k
    AllocBfsQueue(&bfsq, BFS_Q_CLEAR, 0);
3273
18.5k
    if (!(pVA = (VAL_AT*)inchi_calloc(num_at, sizeof(pVA[0]))))
3274
0
    {
3275
0
        ret = RI_ERR_ALLOC;
3276
0
        goto exit_function;
3277
0
    }
3278
18.5k
    pStruct->pVA = pVA;
3279
18.5k
    memset(&va1, 0, sizeof(va1)); /* djb-rwth: memset_s C11/Annex K variant? */
3280
18.5k
    pTCGroups->total_charge = pInChI[0]->nTotalCharge;
3281
18.5k
    if (0 > (ret = AllocBfsQueue(&bfsq, num_at, 0 /* min ring size undefined */)))
3282
0
    {
3283
0
        goto exit_function;
3284
0
    }
3285
18.5k
    pStruct->pbfsq = &bfsq;
3286
3287
18.5k
    if (pStruct->iMobileH == TAUT_NON && pInChI[1] && pInChI[1]->nNumberOfAtoms > 1 &&
3288
554
        (ret = FillOutpStructEndpointFromInChI(pInChI[1], &pStruct->endpoint)))
3289
0
    {
3290
0
        goto exit_function;
3291
0
    }
3292
3293
    /* mark metal atoms; find min ring sizes for atoms that have 2 bonds */
3294
370k
    for (i = 0; i < num_at; i++)
3295
351k
    {
3296
351k
        pVA[i].cNumValenceElectrons = get_sp_element_type(at[i].el_number, &j);
3297
351k
        pVA[i].cPeriodicRowNumber = j;
3298
351k
        pVA[i].cPeriodicNumber = at[i].el_number;
3299
351k
        pVA[i].cNumValenceElectrons--; /* = -1 d- and f- metals, 0 for H, 1 for Na, 2 for Mg,.. = (ATYPE_Xx-1)  */
3300
3301
351k
        if (is_el_a_metal(at[i].el_number))
3302
209k
        {
3303
209k
            if (pStruct->pSrm->bStereoRemovesMetalFlag)
3304
0
            {
3305
                /* treat metal as non-metal if it is stereogenic or has a stereobond */
3306
0
                pVA[i].cMetal = !(at[i].p_parity || at[i].sb_parity[0]);
3307
0
            }
3308
209k
            else
3309
209k
            {
3310
209k
                pVA[i].cMetal = 1;
3311
209k
            }
3312
209k
        }
3313
351k
        if (at[i].valence == 2 && !at[i].num_H)
3314
2.22k
        {
3315
2.22k
            pVA[i].cMinRingSize = is_bond_in_Nmax_memb_ring(at, i, 0, bfsq.q, bfsq.nAtomLevel,
3316
2.22k
                bfsq.cSource, 99 /* max ring size */);
3317
2.22k
        }
3318
349k
        else
3319
349k
        {
3320
349k
            pVA[i].cMinRingSize = 0;
3321
349k
        }
3322
351k
    }
3323
    /* AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); */
3324
3325
18.6k
repeat_for_new_metals:
3326
    /* set valences for the first time; find ChargeValence structures for each atom */
3327
371k
    for (i = 0; i < num_at; i++)
3328
352k
    {
3329
        /* get additional fictitious atoms information */
3330
352k
        pVA[i].cInitFreeValences = 0;
3331
3332
352k
        ret = GetAtomRestoreInfo(pCG, at, i, pVA, pStruct->pSrm, pStruct->bMobileH, pStruct->endpoint);
3333
3334
352k
        if (ret < 0)
3335
0
        {
3336
0
            goto exit_function;
3337
0
        }
3338
352k
        if (ret == TREAT_ATOM_AS_METAL && !bSecondPassNewMetals && !pVA[i].cMetal)
3339
141
        {
3340
141
            if (pStruct->pSrm->bStereoRemovesMetalFlag)
3341
0
            {
3342
                /* treat metal as non-metal if it is stereogenic or has a stereobond */
3343
0
                pVA[i].cMetal = !(at[i].p_parity || at[i].sb_parity[0]);
3344
0
            }
3345
141
            else
3346
141
            {
3347
141
                pVA[i].cMetal = 1;
3348
141
            }
3349
141
            if (pVA[i].cMetal)
3350
141
            {
3351
141
                bTreatMoreAtomsAsMetals++;
3352
141
            }
3353
141
        }
3354
352k
        pTCGroups->charge_on_atoms += pVA[i].cInitCharge;
3355
352k
    }
3356
18.6k
    if (bTreatMoreAtomsAsMetals && !bSecondPassNewMetals)
3357
119
    {
3358
1.09k
        for (i = 0; i < num_at; i++)
3359
971
        {
3360
            /* clear all members of pVA[i] except two */
3361
971
            pTCGroups->charge_on_atoms -= pVA[i].cInitCharge;
3362
971
            va1.cMetal = pVA[i].cMetal;
3363
971
            va1.cMinRingSize = pVA[i].cMinRingSize;
3364
971
            va1.cNumValenceElectrons = pVA[i].cNumValenceElectrons;
3365
971
            va1.cPeriodicRowNumber = pVA[i].cPeriodicRowNumber;
3366
971
            va1.cPeriodicNumber = pVA[i].cPeriodicNumber;
3367
971
            pVA[i] = va1;
3368
971
        }
3369
119
        bSecondPassNewMetals = 1;
3370
119
        goto repeat_for_new_metals;
3371
119
    }
3372
3373
    /* count atoms, bonds, additional edges and vertices in ChargeValence structures and t-groups */
3374
18.5k
    ret = nCountBnsSizes(at, num_at, nAddEdges2eachAtom, nAddVertices, &pStruct->ti,
3375
18.5k
        pVA, pStruct->pSrm, pTCGroups);
3376
18.5k
    if (ret < 0)
3377
34
    {
3378
34
        goto exit_function;
3379
34
    }
3380
3381
    /* find and count groups; add counts of all other vertices to be created */
3382
18.5k
    ret = nAddSuperCGroups(pTCGroups);
3383
18.5k
    if (ret < 0)
3384
0
    {
3385
0
        goto exit_function;
3386
0
    }
3387
3388
    /* create the BNS and fill it with all real atoms */
3389
18.5k
    pBNS = AllocateAndInitTCGBnStruct(pStruct, pVA, pTCGroups,
3390
18.5k
        nMaxAddAtoms, nMaxAddEdges, max_altp, &nNum_changed_bonds);
3391
18.5k
    if (!pBNS)
3392
0
    {
3393
0
        ret = BNS_OUT_OF_RAM;
3394
0
        goto exit_function;
3395
0
    }
3396
    /* add t-groups to the BNS */
3397
18.5k
    ret = AddTGroups2TCGBnStruct(pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges);
3398
18.5k
    if (ret < 0)
3399
0
    {
3400
0
        goto exit_function;
3401
0
    }
3402
3403
    /* add c-groups to the BNS; adjust charges */
3404
18.5k
    ret = AddCGroups2TCGBnStruct(pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges);
3405
18.5k
    if (ret < 0)
3406
0
    {
3407
0
        goto exit_function;
3408
0
    }
3409
3410
18.5k
    pBNS->ulTimeOutTime = NULL;             /* v. 1.05 */
3411
18.5k
    pBNS->ic = ic;                          /* v. 1.05 */
3412
3413
    /* allocate BNData */
3414
18.5k
    pBD = AllocateAndInitBnData(pBNS->max_vertices + pBNS->max_vertices / 2);
3415
18.5k
    if (!pBD)
3416
0
    {
3417
0
        ret = BNS_OUT_OF_RAM;
3418
0
        goto exit_function;
3419
0
    }
3420
18.5k
    CheckBnsConsistency(pStruct, pBNS, pVA, pTCGroups, 0);
3421
3422
    /* restore bonds & charges */
3423
18.5k
    ret = RunBnsRestore1(pCG, ic, ip, sd, pBNS, pBD, pStruct, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH);
3424
18.5k
    if (ret < 0)
3425
17.9k
    {
3426
17.9k
        goto exit_function;
3427
17.9k
    }
3428
3429
552
    ret = CheckBnsConsistency(pStruct, pBNS, pVA, pTCGroups, 1);
3430
#if ( bRELEASE_VERSION == 0 )
3431
#ifndef TARGET_API_LIB
3432
    if (ret)
3433
    {
3434
        fprintf(stdout, "Msg for: %ld %s comp=%d %c%c\n", num_inp, (szCurHdr && szCurHdr[0]) ? szCurHdr : "", iComponent, pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F');
3435
    }
3436
    if (pStruct->iMobileH == TAUT_YES && pStruct->nNumRemovedProtons)
3437
    {
3438
        fprintf(stdout, "REMOVED_PROTONS%+d %ld %s\n", pStruct->nNumRemovedProtons, num_inp, (szCurHdr && szCurHdr[0]) ? szCurHdr : "");
3439
        /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
3440
    }
3441
    if (pStruct->bExtract & EXTRACT_STRUCT_NUMBER)
3442
    {
3443
        fprintf(stdout, "EXTRACT: %ld: %s\n", num_inp, (szCurHdr && szCurHdr[0]) ? szCurHdr : "");
3444
    }
3445
#endif
3446
#endif
3447
552
    {  /* create the final structure in pStruct->at2 */
3448
552
        inp_ATOM* at_tmp = pStruct->at;
3449
552
        pStruct->at = pStruct->at2;
3450
552
        memcpy(pStruct->at, at_tmp, sizeof(pStruct->at[0]) * ((long long)pStruct->num_atoms + (long long)pStruct->num_deleted_H)); /* djb-rwth: cast operators added */
3451
552
        ret2 = CopyBnsToAtom(pStruct, pBNS, pVA, pTCGroups, 1);
3452
552
        pStruct->at2 = pStruct->at;
3453
552
        pStruct->at = at_tmp;
3454
552
        if (ret2 < 0)
3455
0
        {
3456
0
            ret = ret2;
3457
0
        }
3458
552
    }
3459
3460
18.5k
exit_function:
3461
3462
18.5k
    pStruct->pbfsq = NULL;
3463
18.5k
    AllocBfsQueue(&bfsq, BFS_Q_FREE, 0);
3464
3465
18.5k
    pBD = DeAllocateBnData(pBD); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
3466
18.5k
    pBNS = DeAllocateBnStruct(pBNS); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
3467
    /*
3468
    if ( pVA ) inchi_free( pVA );
3469
    */
3470
18.5k
    if (pTCGroups->pTCG) inchi_free(pTCGroups->pTCG);
3471
3472
18.5k
    return ret;
3473
552
}
3474
3475
3476
/****************************************************************************/
3477
int OneInChI2Atom(INCHI_CLOCK* ic,
3478
    CANON_GLOBALS* pCG,
3479
    ICHICONST INPUT_PARMS* ip_inp,
3480
    STRUCT_DATA* sd,
3481
    const char* szCurHdr,
3482
    long num_inp,
3483
    StrFromINChI* pStruct,
3484
    int iComponent,
3485
    int iAtNoOffset,
3486
    int bHasSomeFixedH,
3487
    INChI* pInChI[])
3488
37.3k
{
3489
37.3k
    int ret;
3490
37.3k
    INPUT_PARMS* ip, ip_loc;
3491
3492
37.3k
    ip_loc = *ip_inp;
3493
37.3k
    ip = &ip_loc;
3494
3495
37.3k
    sd->pStrErrStruct[0] = '\0';
3496
37.3k
    ret = RestoreAtomConnectionsSetStereo(pStruct, iComponent, iAtNoOffset, pInChI[0], pInChI[1]);
3497
37.3k
    if (ret < 0)
3498
13
    {
3499
13
        goto exit_function;
3500
13
    }
3501
37.3k
    ret = SetStereoBondTypesFrom0DStereo(pStruct, pInChI[0]);
3502
37.3k
    if (ret < 0)
3503
2
    {
3504
2
        goto exit_function;
3505
2
    }
3506
37.3k
    ret = ReconcileAllCmlBondParities(pStruct->at, pStruct->num_atoms, 0);
3507
37.3k
    if (ret < 0)
3508
0
    {
3509
0
        goto exit_function;
3510
0
    }
3511
3512
    /* main InChI restore function */
3513
37.3k
    ret = RestoreAtomMakeBNS(ic, pCG, ip, sd, pStruct, iComponent, iAtNoOffset, pInChI, szCurHdr, num_inp, bHasSomeFixedH);
3514
3515
#ifndef COMPILE_ANSI_ONLY
3516
    if ((pStruct->num_inp_actual > 0 ? pStruct->num_inp_actual : num_inp) >= ip->first_struct_number &&
3517
        ((/*ret > 0 &&*/ ip->bDisplayIfRestoreWarnings) && pStruct->pXYZ))
3518
    {
3519
        inchiTime     ulTStart;
3520
        InchiTimeGet(&ulTStart);
3521
        DisplayRestoredComponent(pCG, pStruct, iComponent, iAtNoOffset, pInChI[0], szCurHdr);
3522
        sd->ulStructTime -= InchiTimeElapsed(ic, &ulTStart); /* subtract display time */
3523
    }
3524
#endif
3525
3526
37.3k
    if (ret < 0)
3527
18.0k
    {
3528
18.0k
        goto exit_function;
3529
18.0k
    }
3530
19.2k
    if ((pStruct->num_inp_actual ? pStruct->num_inp_actual : num_inp) >= ip->first_struct_number && ret >= 0)
3531
19.2k
    {
3532
        /* remove t-group markings and increment zero-order bonds,
3533
           otherwise MakeInChIOutOfStrFromINChI2() woild fail */
3534
           /* --- moved to MakeInChIOutOfStrFromINChI2 ---
3535
           IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1);
3536
           CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms );
3537
           */
3538
           /* include all restored structure features in pStruct->at2 */
3539
           /* make full InChI out of pStruct->at2, pStruct->num_atoms */
3540
           /***************************************************************************************/
3541
           /* !!! pStruct->One_InChI etc. were removed at the exit from NormalizeAndCompare() !!! */
3542
           /***************************************************************************************/
3543
19.2k
        if (bHasSomeFixedH && pStruct->iInchiRec == INCHI_REC && pStruct->iMobileH == TAUT_YES &&
3544
0
            !pStruct->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC))
3545
0
        {
3546
            /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
3547
0
            ip->nMode |= REQ_MODE_BASIC;
3548
0
        }
3549
3550
19.2k
        ret = MakeInChIOutOfStrFromINChI2(ic, pCG, ip, sd, pStruct, iComponent, iAtNoOffset, num_inp);
3551
3552
19.2k
        if (ret >= 0)
3553
19.0k
        {
3554
19.0k
            ;
3555
19.0k
        }
3556
#if ( bRELEASE_VERSION == 0 )
3557
#ifndef TARGET_API_LIB
3558
        else
3559
        {
3560
            fprintf(stdout, "\nERROR in MakeInChI-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
3561
                szCurHdr ? szCurHdr : "???", iComponent, pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F', ret);
3562
        }
3563
#endif
3564
#endif
3565
19.2k
    }
3566
3567
37.3k
exit_function:
3568
3569
37.3k
    return ret;
3570
19.2k
}
3571
3572
3573
/****************************************************************************/
3574
int MakeProtonComponent(StrFromINChI* pStruct, int iComponent, int num_prot)
3575
9
{
3576
9
    inp_ATOM* at = NULL;
3577
9
    int        i;
3578
3579
9
    if (num_prot <= 0)
3580
0
    {
3581
0
        return 0;
3582
0
    }
3583
    /* allocate */
3584
9
    pStruct->at = (inp_ATOM*)inchi_calloc(num_prot, sizeof(pStruct->at[0]));
3585
9
    pStruct->at2 = (inp_ATOM*)inchi_calloc(num_prot, sizeof(pStruct->at2[0]));
3586
9
    if (!pStruct->at || !pStruct->at2)
3587
0
    {
3588
0
        return 0;
3589
0
    }
3590
    /* create protons */
3591
9
    at = pStruct->at;
3592
    /* fill out proton atom info */
3593
23.7k
    for (i = 0; i < num_prot; i++)
3594
23.6k
    {
3595
23.6k
        strcpy(at[i].elname, "H");
3596
23.6k
        at[i].el_number = EL_NUMBER_H;
3597
23.6k
        at[i].orig_at_number = i + 1;
3598
        /*
3599
        at[i].orig_compt_at_numb = i + 1;
3600
        at[i].component = i + 1;
3601
        */
3602
23.6k
        at[i].charge = 1;
3603
23.6k
    }
3604
9
    memcpy(pStruct->at2, at, num_prot * sizeof(pStruct->at2[0]));
3605
9
    pStruct->bDeleted = 0;
3606
9
    pStruct->num_atoms = num_prot;
3607
9
    pStruct->bMobileH = TAUT_YES;
3608
9
    pStruct->iMobileH = TAUT_YES;
3609
3610
9
    return num_prot;
3611
9
}
3612
3613
3614
/****************************************************************************/
3615
int AddRemProtonsInRestrStruct(INCHI_CLOCK* ic,
3616
    CANON_GLOBALS* pCG,
3617
    ICHICONST INPUT_PARMS* ip_inp,
3618
    STRUCT_DATA* sd, long num_inp,
3619
    int bHasSomeFixedH,
3620
    StrFromINChI* pStruct,
3621
    int num_components,
3622
    StrFromINChI* pStructR,
3623
    int num_componentsR,
3624
    NUM_H* nProtonsToBeRemovedByNormFromRevrs,
3625
    int* recmet_change_balance)
3626
440
{
3627
    /* on entry and exit, all at[i].num_H do not include isotopic H  and explicit terminal H are connected */
3628
440
    int  iComp, q, ret = 0;
3629
440
    int      num_atoms, num_deleted_H, num_tg, num_changed, num_deleted_components; /* djb-rwth: removing redundant variables */
3630
440
    inp_ATOM* at;
3631
440
    INPUT_PARMS* ip, ip_loc;
3632
440
    int      num_prot = *nProtonsToBeRemovedByNormFromRevrs;
3633
440
    int      delta_recmet_prot, num_prot_prev, bAccumulateChanges = 0, nNumProtAddedByRevrs;
3634
440
    INChI_Aux* pINChI_Aux;
3635
440
    INCHI_MODE bNormalizationFlags;
3636
440
    int        nChargeRevrs, nChargeInChI;
3637
3638
440
    if (!num_prot)
3639
379
    {
3640
379
        return 0;
3641
379
    }
3642
61
    delta_recmet_prot = 0;
3643
61
    num_changed = 0;
3644
61
    num_deleted_components = 0;
3645
61
    ip_loc = *ip_inp;
3646
61
    ip = &ip_loc;
3647
    /*----------------------------------------------------------------------------------
3648
    nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is
3649
                                        same as already processed reconnected one
3650
                                        Do no preicess it
3651
3652
    nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component;
3653
    (should not happen)                 It it is a result of (nLink-1)th Reconeected
3654
                                        component disconnection (NOT IMPLEMENTED YET)
3655
3656
    nLink = 0                        => Process this component. It is either a reconnected
3657
                                        component, or a result of a disconnection (for now)
3658
3659
    nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as
3660
                                        a disconnected one that will not be processed.
3661
                                        Process and save charge delta.
3662
    -----------------------------------------------------------------------------------*/
3663
3664
191
    for (iComp = 0; iComp < num_components && num_prot; iComp++)
3665
130
    {
3666
130
        bAccumulateChanges = 0;
3667
130
        if (pStruct[iComp].nLink < 0 && num_componentsR > 0)
3668
0
        {
3669
            /* check */
3670
0
            q = -(pStruct[iComp].nLink + 1);
3671
0
            if (!pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp + 1))
3672
0
            {
3673
0
                ret = RI_ERR_PROGR;
3674
0
                goto exit_function;
3675
0
            }
3676
0
            continue; /* Disconnected structure component has already been processed as a Reconnected one */
3677
0
        }
3678
3679
130
        at = pStruct[iComp].at2;
3680
130
        num_atoms = pStruct[iComp].num_atoms;
3681
130
        num_deleted_H = pStruct[iComp].num_deleted_H; /* djb-rwth: removing redundant code */
3682
130
        bAccumulateChanges = (pStruct[iComp].nLink > 0 && !num_componentsR);
3683
130
        nChargeRevrs = pStruct[iComp].nChargeRevrs;
3684
130
        nChargeInChI = pStruct[iComp].nChargeInChI;
3685
130
        num_deleted_components += (0 != pStruct[iComp].bDeleted);
3686
130
        if (!at || !num_atoms)
3687
47
        {
3688
47
            continue;
3689
47
        }
3690
        /* find whether it is a reconnected structure */
3691
83
        q = bRevInchiComponentExists(pStruct + iComp, INCHI_REC, TAUT_YES, 0) ? INCHI_REC : INCHI_BAS;
3692
        /*
3693
        q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] &&
3694
            pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] &&
3695
            pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS;
3696
        */
3697
83
        pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */
3698
        /*nNumProtAddedByRevrs = pINChI_Aux->nNumRemovedProtons;*/
3699
83
        nNumProtAddedByRevrs = -pStruct[iComp].nNumRemovedProtonsByRevrs;
3700
83
        bNormalizationFlags = pINChI_Aux->bNormalizationFlags;
3701
83
        num_tg = pINChI_Aux->nNumberOfTGroups;
3702
3703
3704
        /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */
3705
83
        if (0 > (ret = DisconnectedConnectedH(at, num_atoms, num_deleted_H)))
3706
0
        {
3707
0
            goto exit_function;
3708
0
        }
3709
83
        num_prot_prev = num_prot;
3710
83
        ret = AddRemoveProtonsRestr(at, num_atoms, &num_prot, nNumProtAddedByRevrs,
3711
83
            bNormalizationFlags, num_tg, nChargeRevrs, nChargeInChI);
3712
3713
83
        pStruct[iComp].bPostProcessed = ret;
3714
83
        num_changed += (ret > 0);
3715
83
        if (ret < 0)
3716
0
        {
3717
0
            goto exit_function;
3718
0
        }
3719
83
        if (ret > 0)
3720
19
        {
3721
            /* recalculate InChI; it will reconnect at */
3722
19
            StrFromINChI* pStruct1 = pStruct + iComp;
3723
19
            INCHI_MODE    nMode = ip->nMode;
3724
19
            FreeAllINChIArrays(pStruct1->RevInChI.pINChI,
3725
19
                pStruct1->RevInChI.pINChI_Aux,
3726
19
                pStruct1->RevInChI.num_components);
3727
3728
19
            if (bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES &&
3729
0
                !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC))
3730
0
            {
3731
                /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
3732
0
                ip->nMode |= REQ_MODE_BASIC;
3733
0
            }
3734
            /* calls ConnectDisconnectedH(...): subtracts number of implicit iso H from implicit H */
3735
3736
19
            ret = MakeInChIOutOfStrFromINChI2(ic, pCG, ip, sd, pStruct1, 0, 0, num_inp);
3737
3738
19
            ip->nMode = nMode;
3739
19
            if (ret < 0)
3740
0
            {
3741
0
                goto exit_function;
3742
0
            }
3743
19
        }
3744
64
        else
3745
64
        {
3746
            /* reconnect disconnected terminal H and subtracts number of implicit iso H from implicit H */
3747
64
            if (0 > (ret = ConnectDisconnectedH(at, num_atoms, num_deleted_H)))
3748
0
            {
3749
0
                goto exit_function;
3750
0
            }
3751
64
        }
3752
83
        if (bAccumulateChanges && recmet_change_balance)
3753
0
        {
3754
            /* processed Reconnected layer component that is also present in Disconnected layer */
3755
0
            delta_recmet_prot += num_prot - num_prot_prev;
3756
0
        }
3757
83
    }
3758
3759
61
    iComp = num_components - 1;
3760
61
    if (!bHasSomeFixedH && num_prot > 0 && 1 == num_deleted_components && iComp >= 0 && pStruct[iComp].bDeleted)
3761
9
    {
3762
        /* add bare protons to the deleted Mobile-H component; undelete the component */
3763
9
        num_prot_prev = num_prot;
3764
9
        if (!MakeProtonComponent(pStruct + iComp, iComp, num_prot))
3765
0
        {
3766
0
            goto exit_function;
3767
0
        }
3768
9
        else
3769
9
        {
3770
            /* recalculate InChI; it will reconnect at */
3771
9
            StrFromINChI* pStruct1 = pStruct + iComp;
3772
9
            INCHI_MODE    nMode = ip->nMode;
3773
9
            num_changed++;
3774
9
            num_prot = 0;
3775
9
            FreeAllINChIArrays(pStruct1->RevInChI.pINChI,
3776
9
                pStruct1->RevInChI.pINChI_Aux,
3777
9
                pStruct1->RevInChI.num_components);
3778
3779
9
            if (bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES &&
3780
0
                !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC))
3781
0
            {
3782
                /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
3783
0
                ip->nMode |= REQ_MODE_BASIC;
3784
0
            }
3785
            /* Although MakeInChIOutOfStrFromINChI2() calls ConnectDisconnectedH(...) */
3786
            /* to subtracts number of implicit iso H from implicit H */
3787
            /* this CANNOT have any effect on the deleted H component */
3788
3789
9
            ret = MakeInChIOutOfStrFromINChI2(ic, pCG, ip, sd, pStruct1, 0, 0, num_inp);
3790
3791
9
            ip->nMode = nMode;
3792
9
            if (ret < 0)
3793
0
            {
3794
0
                goto exit_function;
3795
0
            }
3796
9
            if (bAccumulateChanges && recmet_change_balance)
3797
0
            {
3798
                /* processed Reconnected layer component that is also present in Disconnected layer */
3799
0
                delta_recmet_prot += num_prot - num_prot_prev;
3800
0
            }
3801
9
        }
3802
9
    }
3803
61
    *nProtonsToBeRemovedByNormFromRevrs = num_prot;
3804
61
    if (recmet_change_balance)
3805
0
    {
3806
0
        *recmet_change_balance = delta_recmet_prot;
3807
0
    }
3808
3809
61
exit_function:
3810
3811
61
    return ret < 0 ? ret : num_changed;
3812
61
}
3813
3814
3815
/****************************************************************************/
3816
int AddRemIsoProtonsInRestrStruct(INCHI_CLOCK* ic,
3817
    CANON_GLOBALS* pCG,
3818
    ICHICONST INPUT_PARMS* ip_inp,
3819
    STRUCT_DATA* sd,
3820
    long num_inp,
3821
    int bHasSomeFixedH,
3822
    StrFromINChI* pStruct,
3823
    int num_components,
3824
    StrFromINChI* pStructR,
3825
    int num_componentsR,
3826
    NUM_H pProtonBalance[],
3827
    NUM_H recmet_change_balance[])
3828
440
{
3829
    /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */
3830
440
    int  iComp, q, k, ret = 0, bNotEmpty;
3831
440
    int      num_atoms, num_deleted_H, num_tg, num_changed; /* djb-rwth: removing redundant variables */
3832
440
    inp_ATOM* at;
3833
440
    NUM_H    num_prot[NUM_H_ISOTOPES], delta_recmet_prot[NUM_H_ISOTOPES], num_prot_prev[NUM_H_ISOTOPES];
3834
440
    int      bAccumulateChanges;
3835
440
    INChI_Aux* pINChI_Aux;
3836
    /* djb-rwth: removing redundant variables */
3837
440
    INPUT_PARMS* ip, ip_loc;
3838
3839
440
    ip_loc = *ip_inp;
3840
440
    ip = &ip_loc;
3841
3842
440
    memcpy(num_prot, pProtonBalance, sizeof(num_prot));
3843
1.76k
    for (bNotEmpty = 0, k = 0; k < NUM_H_ISOTOPES; k++)
3844
1.32k
    {
3845
1.32k
        bNotEmpty |= num_prot[k];
3846
1.32k
    }
3847
440
    if (!bNotEmpty)
3848
427
    {
3849
427
        return 0;
3850
427
    }
3851
13
    memset(delta_recmet_prot, 0, sizeof(delta_recmet_prot)); /* djb-rwth: memset_s C11/Annex K variant? */
3852
13
    num_changed = 0;
3853
    /*----------------------------------------------------------------------------------
3854
    nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is
3855
                                        same as already processed reconnected one
3856
                                        Do no process it
3857
3858
    nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component;
3859
    (should not happen)                 It it is a result of (nLink-1)th Reconeected
3860
                                        component disconnection (NOT IMPLEMENTED YET)
3861
3862
    nLink = 0                        => Process this component. It is either a reconnected
3863
                                        component, or a result of a disconnection (for now)
3864
3865
    nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as
3866
                                        a disconnected one that will not be processed.
3867
                                        Process and save charge delta.
3868
    -----------------------------------------------------------------------------------*/
3869
3870
31
    for (iComp = 0; iComp < num_components && num_prot; iComp++) /* djb-rwth: the condition will always evaluate to true only if pProtonBalance is not NULL */
3871
22
    {
3872
        /* djb-rwth: removing redundant code */
3873
22
        if (pStruct[iComp].nLink < 0 && num_componentsR > 0)
3874
0
        {
3875
            /* check */
3876
0
            q = -(pStruct[iComp].nLink + 1);
3877
0
            if (!pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp + 1))
3878
0
            {
3879
0
                ret = RI_ERR_PROGR;
3880
0
                goto exit_function;
3881
0
            }
3882
0
            continue; /* Disconnected structure component has already been processed as a Reconnected one */
3883
0
        }
3884
3885
22
        at = pStruct[iComp].at2;
3886
22
        num_atoms = pStruct[iComp].num_atoms;
3887
22
        num_deleted_H = pStruct[iComp].num_deleted_H; /* djb-rwth: removing redundant code */
3888
22
        bAccumulateChanges = (pStruct[iComp].nLink > 0 && !num_componentsR);
3889
3890
22
        if (!at || !num_atoms)
3891
9
        {
3892
9
            continue;
3893
9
        }
3894
        /* find whether it is a reconnected structure */
3895
13
        q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] &&
3896
10
            pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] &&
3897
10
            pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms ? INCHI_REC : INCHI_BAS;
3898
3899
13
        pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */
3900
        /* djb-rwth: removing redundant code */
3901
13
        num_tg = pINChI_Aux->nNumberOfTGroups;
3902
13
        memcpy(num_prot_prev, num_prot, sizeof(num_prot_prev));
3903
3904
        /* pass CONNECTED explicit H to AddRemoveIsoProtonsRestr() for isotopic H addition */
3905
13
        ret = AddRemoveIsoProtonsRestr(at, num_atoms, num_prot, num_tg);
3906
3907
13
        pStruct[iComp].bPostProcessed |= ret;
3908
13
        num_changed += (ret > 0);
3909
13
        if (ret < 0)
3910
1
        {
3911
1
            goto exit_function;
3912
1
        }
3913
12
        if (ret > 0)
3914
8
        {
3915
8
            StrFromINChI* pStruct1 = pStruct + iComp;
3916
8
            INCHI_MODE    nMode = ip->nMode;
3917
            /* recalculate InChI; MakeInChIOutOfStrFromINChI2() will reconnect explicit H */
3918
            /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */
3919
8
            if (0 > (ret = DisconnectedConnectedH(at, num_atoms, num_deleted_H)))
3920
0
            {
3921
0
                goto exit_function;
3922
0
            }
3923
8
            FreeAllINChIArrays(pStruct1->RevInChI.pINChI,
3924
8
                pStruct1->RevInChI.pINChI_Aux,
3925
8
                pStruct1->RevInChI.num_components);
3926
8
            if (bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES &&
3927
0
                !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC))
3928
0
            {
3929
                /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
3930
0
                ip->nMode |= REQ_MODE_BASIC;
3931
0
            }
3932
            /* input: disconnected explicit H, output: connected explicit H */
3933
8
            ret = MakeInChIOutOfStrFromINChI2(ic, pCG, ip, sd, pStruct1, 0, 0, num_inp);
3934
8
            ip->nMode = nMode;
3935
8
            if (ret < 0)
3936
3
            {
3937
3
                goto exit_function;
3938
3
            }
3939
8
        }
3940
        /* the following was commented out 2007-08-28 by DT. Reason: it's a bug since H must be already connected */
3941
        /* else {
3942
            if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) {
3943
                goto exit_function;
3944
            }
3945
        } */
3946
9
        if (bAccumulateChanges)
3947
0
        {
3948
            /* processed Reconnected layer component that is also present in Disconnected layer */
3949
0
            for (k = 0; k < NUM_H_ISOTOPES; k++)
3950
0
            {
3951
0
                delta_recmet_prot[k] += num_prot[k] - num_prot_prev[k];
3952
0
            }
3953
0
        }
3954
9
    }
3955
3956
9
    memcpy(pProtonBalance, num_prot, sizeof(num_prot));
3957
9
    if (recmet_change_balance)
3958
0
    {
3959
0
        memcpy(recmet_change_balance, delta_recmet_prot, sizeof(delta_recmet_prot));
3960
0
    }
3961
3962
13
exit_function:
3963
3964
13
    return ret < 0 ? ret : num_changed;
3965
9
}
3966
3967
#endif