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