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