Branch data Line data Source code
1 : : /*
2 : : * SPDX-License-Identifier: MIT
3 : : * Copyright (c) 2024 IUPAC and InChI Trust
4 : : */
5 : :
6 : : #include <string.h>
7 : :
8 : : #include "mode.h"
9 : : #include "ichitaut.h"
10 : :
11 : : #include "bcf_s.h"
12 : :
13 : : /****************************************************************************/
14 : :
15 : : #if ( FIND_RING_SYSTEMS == 1 ) /* { */
16 : :
17 : : /* local prototypes */
18 : : int are_alt_bonds( U_CHAR *bonds, int len );
19 : : int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, int nMaxNumBondPos, int nNumBondPos );
20 : : int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, int nNumEndPoint );
21 : :
22 : :
23 : :
24 : :
25 : :
26 : : /******************************************
27 : : *
28 : : * Tautomerism in 5- and 6-member rings
29 : : *
30 : : ******************************************/
31 : :
32 : : const int NONE = (AT_RANK) ~0;
33 : :
34 : :
35 : : /*
36 : : 1,5 Tautomerism in 6-member alt ring:
37 : :
38 : : /=\ /==\
39 : : HN C=O <-> N C-OH
40 : : \=/ \\-//
41 : :
42 : :
43 : : 1,2 Tautomerism in 5-member ring:
44 : :
45 : :
46 : : HN--X N==X
47 : : | \\ | \
48 : : | Z <-> | Z
49 : : | / | //
50 : : N==Y HN--Y
51 : :
52 : :
53 : : 1,4 tautomerism in 7-member ring
54 : :
55 : : /C==D //C-D
56 : : O=B \ HO-B \\
57 : : | E <-> | E
58 : : HO-A // O=A /
59 : : \\G-F \\G-F
60 : :
61 : :
62 : : 1,4 tautomerism in 5-member ring
63 : :
64 : :
65 : : O=B--C O-B==C
66 : : | \\ | \
67 : : | D <-> | D
68 : : | / | //
69 : : HO-A==E HO=A--E
70 : :
71 : : */
72 : : typedef int CHECK_DFS_RING( struct tagCANON_GLOBALS *pCG,
73 : : inp_ATOM *atom,
74 : : DFS_PATH *DfsPath,
75 : : int nLenDfsPath,
76 : : int nStartAtomNeighbor,
77 : : int nStartAtomNeighbor2,
78 : : int nStartAtomNeighborNeighbor,
79 : : T_ENDPOINT *EndPoint,
80 : : int nMaxNumEndPoint,
81 : : T_BONDPOS *BondPos,
82 : : int nMaxNumBondPos,
83 : : int *pnNumEndPoint,
84 : : int *pnNumBondPos,
85 : : struct BalancedNetworkStructure *pBNS,
86 : : struct BalancedNetworkData *pBD,
87 : : int num_atoms );
88 : :
89 : : typedef int CHECK_CENTERPOINT( inp_ATOM *atom, int iat );
90 : :
91 : : CHECK_DFS_RING Check7MembTautRing;
92 : : CHECK_DFS_RING Check6MembTautRing;
93 : : CHECK_DFS_RING Check5MembTautRing;
94 : :
95 : : #if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */
96 : : /* DFS simple alt path for 1,5 tautomerism, post v.1 feature */
97 : : typedef int CHECK_DFS_PATH( struct tagCANON_GLOBALS *pCG,
98 : : inp_ATOM *atom,
99 : : DFS_PATH *DfsPath,
100 : : int nLenDfsPath,
101 : : int jNxtNeigh,
102 : : int nStartAtomNeighbor,
103 : : int nStartAtomNeighbor2,
104 : : int nStartAtomNeighborNeighbor,
105 : : T_ENDPOINT *EndPoint,
106 : : int nMaxNumEndPoint,
107 : : T_BONDPOS *BondPos,
108 : : int nMaxNumBondPos,
109 : : int *pnNumEndPoint,
110 : : int *pnNumBondPos,
111 : : struct BalancedNetworkStructure *pBNS,
112 : : struct BalancedNetworkData *pBD,
113 : : int num_atoms );
114 : :
115 : : typedef int CHECK_DFS_CENTERPOINT( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh,
116 : : struct BalancedNetworkStructure *pBNS,
117 : : struct BalancedNetworkData *pBD, int num_atoms );
118 : :
119 : :
120 : : CHECK_DFS_PATH Check15TautPath;
121 : : CHECK_DFS_CENTERPOINT Check15TautPathCenterpoint;
122 : :
123 : : int DFS_FindTautAltPath( struct tagCANON_GLOBALS *pCG,
124 : : inp_ATOM *atom,
125 : : int nStartAtom,
126 : : int nStartAtomNeighbor,
127 : : int nStartAtomNeighbor2,
128 : : int nStartAtomNeighborNeighbor,
129 : : int nCycleLen,
130 : : AT_RANK *nDfsPathPos,
131 : : DFS_PATH *DfsPath,
132 : : CHECK_DFS_PATH *CheckDfsPath,
133 : : CHECK_DFS_CENTERPOINT *CheckCenterPoint,
134 : : T_ENDPOINT *EndPoint,
135 : : int nMaxNumEndPoint,
136 : : T_BONDPOS *BondPos,
137 : : int nMaxNumBondPos,
138 : : int *pnNumEndPoint,
139 : : int *pnNumBondPos,
140 : : struct BalancedNetworkStructure *pBNS,
141 : : struct BalancedNetworkData *pBD,
142 : : int num_atoms );
143 : :
144 : : #define BOND_WRONG 64
145 : : #define IS_ALT_OR_DBLBOND(X) (((X) == BOND_SINGLE || (X) == BOND_DOUBLE)? (X) : \
146 : : ((X) == BOND_ALTERN || (X) == BOND_TAUTOM || (X) == BOND_ALT12NS)? BOND_ALTERN : \
147 : : BOND_WRONG);
148 : :
149 : : #endif /* TAUT_15_NON_RING */
150 : :
151 : : int DFS_FindTautInARing( struct tagCANON_GLOBALS *pCG,
152 : : inp_ATOM *atom,
153 : : int nStartAtom,
154 : : int nStartAtomNeighbor,
155 : : int nStartAtomNeighbor2,
156 : : int nStartAtomNeighborNeighbor,
157 : : int nCycleLen,
158 : : AT_RANK *nDfsPathPos,
159 : : DFS_PATH *DfsPath,
160 : : CHECK_DFS_RING *CheckDfsRing,
161 : : CHECK_CENTERPOINT *CheckCenterPoint,
162 : : T_ENDPOINT *EndPoint,
163 : : int nMaxNumEndPoint,
164 : : T_BONDPOS *BondPos,
165 : : int nMaxNumBondPos,
166 : : int *pnNumEndPoint,
167 : : int *pnNumBondPos,
168 : : struct BalancedNetworkStructure *pBNS,
169 : : struct BalancedNetworkData *pBD,
170 : : int num_atoms );
171 : :
172 : : #if ( REPLACE_ALT_WITH_TAUT == 1 )
173 : : #define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE || (X) == BOND_ALTERN || (X) == BOND_ALT12NS )
174 : : #else
175 : : #define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE )
176 : : #endif
177 : :
178 : :
179 : : /****************************************************************************/
180 : 172 : int bIsCenterPointStrict( inp_ATOM *atom, int iat )
181 : : {
182 [ + + ]: 172 : if (atom[iat].valence == atom[iat].chem_bonds_valence)
183 : : {
184 : 86 : int endpoint_valence = get_endpoint_valence( atom[iat].el_number );
185 [ + + - + ]: 86 : if (endpoint_valence && ( (endpoint_valence > atom[iat].valence && /* added a check for negative charge or H 3-31-03 */
186 [ # # # # ]: 0 : ( atom[iat].num_H || atom[iat].charge == -1 )) ||
187 [ + - - + ]: 8 : (!atom[iat].charge && atom[iat].c_point) )) /* djb-rwth: addressing LLVM warnings */
188 : : {
189 : 0 : return 1; /* may appear to be tautomeric or chargable
190 : : (this increases chem_bonds_valence), should be explored */
191 : : }
192 : 86 : return 0;
193 : : }
194 [ + - + + ]: 172 : if (atom[iat].valence + 1 == atom[iat].chem_bonds_valence &&
195 : 86 : is_centerpoint_elem_strict( atom[iat].el_number ))
196 : : {
197 : 84 : return 1;
198 : : }
199 : :
200 : 2 : return 0;
201 : : }
202 : :
203 : :
204 : : /****************************************************************************/
205 : 0 : int nGet14TautIn7MembAltRing( struct tagCANON_GLOBALS *pCG,
206 : : inp_ATOM *atom,
207 : : int nStartAtom,
208 : : int nStartAtomNeighbor,
209 : : int nStartAtomNeighborEndpoint,
210 : : int nStartAtomNeighborNeighborEndpoint,
211 : : AT_RANK *nDfsPathPos,
212 : : DFS_PATH *DfsPath,
213 : : int nMaxLenDfsPath,
214 : : T_ENDPOINT *EndPoint,
215 : : int nMaxNumEndPoint,
216 : : T_BONDPOS *BondPos,
217 : : int nMaxNumBondPos,
218 : : int *pnNumEndPoint,
219 : : int *pnNumBondPos,
220 : : struct BalancedNetworkStructure *pBNS,
221 : : struct BalancedNetworkData *pBD,
222 : : int num_atoms )
223 : : {
224 : : int nRet;
225 : :
226 : 0 : *pnNumEndPoint = 0;
227 : 0 : *pnNumBondPos = 0;
228 : :
229 [ # # ]: 0 : if (nMaxLenDfsPath <= 7)
230 : : {
231 : 0 : return -1; /* path is too short */
232 : : }
233 : :
234 : 0 : nRet = DFS_FindTautInARing( pCG, atom, nStartAtom, nStartAtomNeighbor,
235 : : nStartAtomNeighborEndpoint,
236 : : nStartAtomNeighborNeighborEndpoint, 7,
237 : : nDfsPathPos, DfsPath, Check7MembTautRing,
238 : : bIsCenterPointStrict, EndPoint, nMaxNumEndPoint,
239 : : BondPos, nMaxNumBondPos, pnNumEndPoint,
240 : : pnNumBondPos, pBNS, pBD, num_atoms );
241 : :
242 : :
243 : 0 : return nRet;
244 : : }
245 : :
246 : :
247 : : /****************************************************************************/
248 : 0 : int nGet14TautIn5MembAltRing( struct tagCANON_GLOBALS *pCG,
249 : : inp_ATOM *atom,
250 : : int nStartAtom,
251 : : int nStartAtomNeighbor,
252 : : int nStartAtomNeighborEndpoint,
253 : : int nStartAtomNeighborNeighborEndpoint,
254 : : AT_RANK *nDfsPathPos,
255 : : DFS_PATH *DfsPath,
256 : : int nMaxLenDfsPath,
257 : : T_ENDPOINT *EndPoint,
258 : : int nMaxNumEndPoint,
259 : : T_BONDPOS *BondPos,
260 : : int nMaxNumBondPos,
261 : : int *pnNumEndPoint,
262 : : int *pnNumBondPos,
263 : : struct BalancedNetworkStructure *pBNS,
264 : : struct BalancedNetworkData *pBD,
265 : : int num_atoms )
266 : : {
267 : : int nRet;
268 : :
269 : 0 : *pnNumEndPoint = 0;
270 : 0 : *pnNumBondPos = 0;
271 : :
272 [ # # ]: 0 : if (nMaxLenDfsPath <= 5)
273 : : {
274 : 0 : return -1; /* path is too short */
275 : : }
276 : :
277 : 0 : nRet = DFS_FindTautInARing( pCG, atom, nStartAtom, nStartAtomNeighbor,
278 : : nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 5,
279 : : nDfsPathPos, DfsPath, Check7MembTautRing, bIsCenterPointStrict,
280 : : EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos,
281 : : pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms );
282 : :
283 : 0 : return nRet;
284 : : }
285 : :
286 : : /****************************************************************************/
287 : 0 : int nGet12TautIn5MembAltRing( struct tagCANON_GLOBALS *pCG,
288 : : inp_ATOM *atom,
289 : : int nStartAtom,
290 : : int nStartAtomNeighbor,
291 : : AT_RANK *nDfsPathPos,
292 : : DFS_PATH *DfsPath,
293 : : int nMaxLenDfsPath,
294 : : T_ENDPOINT *EndPoint,
295 : : int nMaxNumEndPoint,
296 : : T_BONDPOS *BondPos,
297 : : int nMaxNumBondPos,
298 : : int *pnNumEndPoint,
299 : : int *pnNumBondPos,
300 : : struct BalancedNetworkStructure *pBNS,
301 : : struct BalancedNetworkData *pBD,
302 : : int num_atoms )
303 : : {
304 : : int nRet;
305 : :
306 : 0 : *pnNumEndPoint = 0;
307 : 0 : *pnNumBondPos = 0;
308 : :
309 [ # # ]: 0 : if (nMaxLenDfsPath <= 5)
310 : : {
311 : 0 : return -1; /* path is too short */
312 : : }
313 : :
314 : 0 : nRet = DFS_FindTautInARing( pCG, atom, nStartAtom, nStartAtomNeighbor,
315 : : -1, -1, 5,
316 : : nDfsPathPos, DfsPath, Check5MembTautRing,
317 : : bIsCenterPointStrict, EndPoint, nMaxNumEndPoint,
318 : : BondPos, nMaxNumBondPos, pnNumEndPoint,
319 : : pnNumBondPos, pBNS, pBD, num_atoms );
320 : :
321 : 0 : return nRet;
322 : : }
323 : :
324 : :
325 : : /****************************************************************************/
326 : 0 : int nGet15TautIn6MembAltRing( struct tagCANON_GLOBALS *pCG,
327 : : inp_ATOM *atom,
328 : : int nStartAtom,
329 : : AT_RANK *nDfsPathPos,
330 : : DFS_PATH *DfsPath,
331 : : int nMaxLenDfsPath,
332 : : T_ENDPOINT *EndPoint,
333 : : int nMaxNumEndPoint,
334 : : T_BONDPOS *BondPos,
335 : : int nMaxNumBondPos,
336 : : int *pnNumEndPoint,
337 : : int *pnNumBondPos,
338 : : struct BalancedNetworkStructure *pBNS,
339 : : struct BalancedNetworkData *pBD,
340 : : int num_atoms )
341 : : {
342 : : int nRet;
343 : :
344 : 0 : *pnNumEndPoint = 0;
345 : 0 : *pnNumBondPos = 0;
346 : :
347 [ # # ]: 0 : if (nMaxLenDfsPath <= 7)
348 : : {
349 : 0 : return -1; /* path is too short */
350 : : }
351 : :
352 : 0 : nRet = DFS_FindTautInARing( pCG, atom, nStartAtom,
353 : : -1/*nStartAtomNeighbor*/,
354 : : -1/*nStartAtomNeighbor2*/,
355 : : -1/*nStartAtomNeighborNeighbor*/,
356 : : 6 /* nCycleLen*/,
357 : : nDfsPathPos, DfsPath,
358 : : Check6MembTautRing, bIsCenterPointStrict,
359 : : EndPoint, nMaxNumEndPoint,
360 : : BondPos, nMaxNumBondPos,
361 : : pnNumEndPoint, pnNumBondPos,
362 : : pBNS, pBD, num_atoms );
363 : :
364 : 0 : return nRet;
365 : : }
366 : :
367 : :
368 : : #if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/
369 : :
370 : :
371 : : /****************************************************************************/
372 : 0 : int nGet15TautInAltPath( struct tagCANON_GLOBALS *pCG,
373 : : inp_ATOM *atom,
374 : : int nStartAtom,
375 : : AT_RANK *nDfsPathPos,
376 : : DFS_PATH *DfsPath,
377 : : int nMaxLenDfsPath,
378 : : T_ENDPOINT *EndPoint,
379 : : int nMaxNumEndPoint,
380 : : T_BONDPOS *BondPos,
381 : : int nMaxNumBondPos,
382 : : int *pnNumEndPoint,
383 : : int *pnNumBondPos,
384 : : struct BalancedNetworkStructure *pBNS,
385 : : struct BalancedNetworkData *pBD,
386 : : int num_atoms )
387 : : {
388 : : int nRet;
389 : :
390 : 0 : *pnNumEndPoint = 0;
391 : 0 : *pnNumBondPos = 0;
392 : :
393 [ # # ]: 0 : if (nMaxLenDfsPath <= 7)
394 : : {
395 : 0 : return -1; /* path is too short */
396 : : }
397 : :
398 : 0 : nRet = DFS_FindTautAltPath( pCG, atom, nStartAtom,
399 : : -1/*nStartAtomNeighbor*/,
400 : : -1/*nStartAtomNeighbor2*/,
401 : : -1/*nStartAtomNeighborNeighbor*/,
402 : : 4 /* nCycleLen*/,
403 : : nDfsPathPos, DfsPath,
404 : : Check15TautPath, Check15TautPathCenterpoint,
405 : : EndPoint, nMaxNumEndPoint,
406 : : BondPos, nMaxNumBondPos,
407 : : pnNumEndPoint, pnNumBondPos,
408 : : pBNS, pBD, num_atoms );
409 : :
410 : 0 : return nRet;
411 : : }
412 : : #endif
413 : :
414 : :
415 : : /****************************************************************************
416 : : DFS version
417 : : ****************************************************************************/
418 : : #define MAX_DFS_DEPTH 16
419 : :
420 : :
421 : :
422 : : /********************************************************************************/
423 : 0 : int DFS_FindTautInARing( struct tagCANON_GLOBALS *pCG,
424 : : inp_ATOM *atom,
425 : : int nStartAtom,
426 : : int nStartAtomNeighbor,
427 : : int nStartAtomNeighbor2,
428 : : int nStartAtomNeighborNeighbor,
429 : : int nCycleLen,
430 : : AT_RANK *nDfsPathPos,
431 : : DFS_PATH *DfsPath,
432 : : CHECK_DFS_RING *CheckDfsRing,
433 : : CHECK_CENTERPOINT *CheckCenterPoint,
434 : : T_ENDPOINT *EndPoint,
435 : : int nMaxNumEndPoint,
436 : : T_BONDPOS *BondPos,
437 : : int nMaxNumBondPos,
438 : : int *pnNumEndPoint,
439 : : int *pnNumBondPos,
440 : : struct BalancedNetworkStructure *pBNS,
441 : : struct BalancedNetworkData *pBD,
442 : : int num_atoms )
443 : : {
444 : : /* Depth First Search */
445 : : /* Ignore all atoms not belonging to the current ring system (=biconnected component) */
446 : : AT_RANK nMinLenDfsPath;
447 : : int j, cur_at, nxt_at, prv_at;
448 : : int nLenDfsPath, nNumFound, ret;
449 : : /* djb-rwth: removing redundant variables */
450 : 0 : int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1;
451 : :
452 : 0 : nLenDfsPath = 0;
453 : 0 : nNumFound = 0;
454 : :
455 : 0 : nCycleLen--;
456 : :
457 : 0 : DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom;
458 : 0 : DfsPath[nLenDfsPath].bond_type = 0;
459 : 0 : DfsPath[nLenDfsPath].bond_pos = -1;
460 : 0 : nDfsPathPos[cur_at] = nLenDfsPath + 1; /* mark */
461 : : /* djb-rwth: removing redundant code */
462 : 0 : nMinLenDfsPath = 0;
463 [ # # ]: 0 : if (nStartAtomNeighbor2 >= 0)
464 : : {
465 : 0 : nDoNotTouchAtom1 = (int) atom[cur_at].neighbor[nStartAtomNeighbor2];
466 : : }
467 : :
468 : : /* add the first neighbor to the 2nd tree position if required */
469 [ # # ]: 0 : if (nStartAtomNeighbor >= 0)
470 : : {
471 : 0 : j = nStartAtomNeighbor;
472 : 0 : prv_at = cur_at;
473 : 0 : cur_at = atom[prv_at].neighbor[j];
474 : 0 : DfsPath[nLenDfsPath].bond_type = ( atom[prv_at].bond_type[j] & ~BOND_MARK_ALL );
475 : : #if ( FIX_BOND23_IN_TAUT == 1 )
476 : : DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER( pBNS, prv_at, j, DfsPath[nLenDfsPath].bond_type );
477 : : #endif
478 : 0 : DfsPath[nLenDfsPath].bond_pos = j;
479 : :
480 : 0 : nLenDfsPath++;
481 : :
482 : 0 : DfsPath[nLenDfsPath].at_no = cur_at;
483 : 0 : DfsPath[nLenDfsPath].bond_type = 0;
484 : 0 : DfsPath[nLenDfsPath].bond_pos = -1;
485 : 0 : nDfsPathPos[cur_at] = nLenDfsPath + 1;
486 : 0 : nMinLenDfsPath++;
487 [ # # ]: 0 : if (nStartAtomNeighborNeighbor >= 0)
488 : : {
489 : 0 : nDoNotTouchAtom2 = (int) atom[cur_at].neighbor[nStartAtomNeighborNeighbor];
490 : : }
491 : : }
492 : :
493 : : /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */
494 : : /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */
495 [ # # ]: 0 : while (nLenDfsPath >= nMinLenDfsPath)
496 : : {
497 : 0 : j = ++DfsPath[nLenDfsPath].bond_pos;
498 [ # # ]: 0 : if (j < atom[cur_at = (int) DfsPath[nLenDfsPath].at_no].valence)
499 : : {
500 : 0 : DfsPath[nLenDfsPath].bond_type = ( atom[cur_at].bond_type[j] & ~BOND_MARK_ALL );
501 : : #if ( FIX_BOND23_IN_TAUT == 1 )
502 : : DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER( pBNS, cur_at, j, DfsPath[nLenDfsPath].bond_type );
503 : : #endif
504 : 0 : nxt_at = (int) atom[cur_at].neighbor[j];
505 [ # # # # ]: 0 : if (nxt_at == nDoNotTouchAtom1 ||
506 : : nxt_at == nDoNotTouchAtom2)
507 : : {
508 : : ; /* ignore */
509 : : }
510 : : else
511 : : {
512 [ # # ]: 0 : if (nDfsPathPos[nxt_at])
513 : : {
514 : : /* found a ring closure or a step backwards */
515 [ # # # # ]: 0 : if (1 == nDfsPathPos[nxt_at] && nLenDfsPath == nCycleLen)
516 : : {
517 : : /* we have found the cycle; check it */
518 : 0 : ret = ( *CheckDfsRing )( pCG,
519 : : atom,
520 : : DfsPath, nLenDfsPath,
521 : : nStartAtomNeighbor,
522 : : nStartAtomNeighbor2,
523 : : nStartAtomNeighborNeighbor,
524 : : EndPoint, nMaxNumEndPoint,
525 : : BondPos, nMaxNumBondPos,
526 : : pnNumEndPoint, pnNumBondPos,
527 : : pBNS, pBD, num_atoms );
528 [ # # ]: 0 : if (ret < 0)
529 : : {
530 : 0 : nNumFound = ret;
531 : 0 : goto clear_path;
532 : : }
533 : 0 : nNumFound += ret;
534 : : }
535 : : }
536 : : else
537 : : {
538 [ # # ]: 0 : if (!( *CheckCenterPoint )( atom, nxt_at ))
539 : : {
540 : : ; /* cannot advance to a non-centerpoint; ignore */
541 : : }
542 : : else
543 : : {
544 [ # # ]: 0 : if (nLenDfsPath < nCycleLen)
545 : : {
546 : : /* advance */
547 : 0 : nLenDfsPath++;
548 : 0 : cur_at = nxt_at;
549 : 0 : DfsPath[nLenDfsPath].at_no = cur_at;
550 : 0 : DfsPath[nLenDfsPath].bond_type = 0;
551 : 0 : DfsPath[nLenDfsPath].bond_pos = -1;
552 : 0 : nDfsPathPos[cur_at] = nLenDfsPath + 1; /* mark */
553 : : }
554 : : }
555 : : }
556 : : }
557 : : }
558 : : else
559 : : {
560 : : /* retract */
561 : 0 : nDfsPathPos[(int) DfsPath[nLenDfsPath].at_no] = 0;
562 : 0 : nLenDfsPath--;
563 : : }
564 : : }
565 : :
566 : 0 : clear_path:
567 [ # # ]: 0 : while (0 <= nLenDfsPath)
568 : : {
569 : 0 : nDfsPathPos[(int) DfsPath[nLenDfsPath].at_no] = 0;
570 : 0 : nLenDfsPath--;
571 : : }
572 : :
573 : 0 : return nNumFound;
574 : : }
575 : :
576 : :
577 : : #if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/
578 : :
579 : :
580 : : /****************************************************************************/
581 : 0 : int DFS_FindTautAltPath( struct tagCANON_GLOBALS *pCG,
582 : : inp_ATOM *atom,
583 : : int nStartAtom,
584 : : int nStartAtomNeighbor,
585 : : int nStartAtomNeighbor2,
586 : : int nStartAtomNeighborNeighbor,
587 : : int nCycleLen,
588 : : AT_RANK *nDfsPathPos,
589 : : DFS_PATH *DfsPath,
590 : : CHECK_DFS_PATH *CheckDfsPath,
591 : : CHECK_DFS_CENTERPOINT *CheckCenterPointPath,
592 : : T_ENDPOINT *EndPoint,
593 : : int nMaxNumEndPoint,
594 : : T_BONDPOS *BondPos,
595 : : int nMaxNumBondPos,
596 : : int *pnNumEndPoint,
597 : : int *pnNumBondPos,
598 : : struct BalancedNetworkStructure *pBNS,
599 : : struct BalancedNetworkData *pBD,
600 : : int num_atoms )
601 : : {
602 : : /* Naive Depth First Search: same atom may be approached along different alt paths */
603 : : /* Ignore all atoms not belonging to the current ring system (=biconnected component) */
604 : : AT_RANK nMinLenDfsPath;
605 : : int j, cur_at, nxt_at, prv_at;
606 : : int nLenDfsPath, nNumFound, ret;
607 : : /* djb-rwth: removing redundant variables */
608 : 0 : int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1;
609 : :
610 : 0 : nLenDfsPath = 0;
611 : 0 : nNumFound = 0;
612 : :
613 : 0 : nCycleLen--; /* indef of the last atom in the alt path, statring from 0 */
614 : :
615 : 0 : DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom;
616 : 0 : DfsPath[nLenDfsPath].bond_type = 0;
617 : 0 : DfsPath[nLenDfsPath].bond_pos = -1; /* initialize index of the bond to the next atom */
618 : 0 : nDfsPathPos[cur_at] = nLenDfsPath + 1; /* mark with distance + 1 */
619 : : /* djb-rwth: removing redundant variables/code */
620 : 0 : nMinLenDfsPath = 0; /* allow to restart from nStartAtom */
621 [ # # ]: 0 : if (nStartAtomNeighbor2 >= 0)
622 : : {
623 : 0 : nDoNotTouchAtom1 = (int) atom[cur_at].neighbor[nStartAtomNeighbor2];
624 : : }
625 : :
626 : : /* add the first neighbor to the 2nd tree position if required */
627 [ # # ]: 0 : if (nStartAtomNeighbor >= 0)
628 : : {
629 : 0 : j = nStartAtomNeighbor;
630 : 0 : prv_at = cur_at;
631 : 0 : cur_at = atom[prv_at].neighbor[j];
632 : 0 : DfsPath[nLenDfsPath].bond_type = ( atom[prv_at].bond_type[j] & ~BOND_MARK_ALL );
633 : : #if ( FIX_BOND23_IN_TAUT == 1 )
634 : : DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER( pBNS, prv_at, j, DfsPath[nLenDfsPath].bond_type );
635 : : #endif
636 : 0 : DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */
637 : :
638 : 0 : nLenDfsPath++;
639 : :
640 : 0 : DfsPath[nLenDfsPath].at_no = cur_at;
641 : 0 : DfsPath[nLenDfsPath].bond_type = 0;
642 : 0 : DfsPath[nLenDfsPath].bond_pos = -1;
643 : 0 : nDfsPathPos[cur_at] = nLenDfsPath + 1; /* mark with distance + 1 */
644 : 0 : nMinLenDfsPath++; /* allow to restart from nStartAtom's neighbor */
645 [ # # ]: 0 : if (nStartAtomNeighborNeighbor >= 0)
646 : : {
647 : 0 : nDoNotTouchAtom2 = (int) atom[cur_at].neighbor[nStartAtomNeighborNeighbor];
648 : : }
649 : : }
650 : :
651 : : /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */
652 : : /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */
653 [ # # ]: 0 : while (nLenDfsPath >= nMinLenDfsPath)
654 : : {
655 : 0 : j = ++DfsPath[nLenDfsPath].bond_pos;
656 [ # # ]: 0 : if (j < atom[cur_at = (int) DfsPath[nLenDfsPath].at_no].valence)
657 : : {
658 : 0 : DfsPath[nLenDfsPath].bond_type = ( atom[cur_at].bond_type[j] & ~BOND_MARK_ALL );
659 : : #if ( FIX_BOND23_IN_TAUT == 1 )
660 : : DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER( pBNS, cur_at, j, DfsPath[nLenDfsPath].bond_type );
661 : : #endif
662 : 0 : nxt_at = (int) atom[cur_at].neighbor[j];
663 [ # # # # ]: 0 : if (nxt_at == nDoNotTouchAtom1 || /* forbidden */
664 : 0 : nxt_at == nDoNotTouchAtom2 || /* forbidden */
665 [ # # # # ]: 0 : nDfsPathPos[nxt_at] || /* ring closure */
666 [ # # ]: 0 : (nLenDfsPath && nxt_at == (int) DfsPath[nLenDfsPath - 1].at_no) /* step backwards */
667 : : ) /* djb-rwth: addressing LLVM warning */
668 : : {
669 : : ; /* ignore nxt_at */
670 : : }
671 : : else
672 : : {
673 [ # # ]: 0 : if (nLenDfsPath == nCycleLen &&
674 : : /* 1,5 and at least one of the endpoints is not in a ring */
675 [ # # # # : 0 : ( atom[nxt_at].nNumAtInRingSystem == 1 || atom[nStartAtom].nNumAtInRingSystem == 1 ) &&
# # ]
676 : : /* we have found the alt path of the requested length; check it */
677 : : /* calling Check15TautPath() */
678 : 0 : ( ret = ( *CheckDfsPath )( pCG,
679 : : atom,
680 : : DfsPath, nLenDfsPath,
681 : : j, nStartAtomNeighbor,
682 : : nStartAtomNeighbor2,
683 : : nStartAtomNeighborNeighbor,
684 : : EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos,
685 : : pnNumEndPoint, pnNumBondPos,
686 : : pBNS, pBD, num_atoms ) ))
687 : : {
688 [ # # ]: 0 : if (ret < 0)
689 : : {
690 : 0 : nNumFound = ret;
691 : 0 : goto clear_path; /* program error */
692 : : }
693 : 0 : nNumFound += ret; /* success */
694 : : }
695 : : else
696 : : {
697 : : /* calling Check15TautPathCenterpoint() */
698 [ # # ]: 0 : if (!( *CheckCenterPointPath )( atom, DfsPath, nLenDfsPath, j,
699 : : pBNS, pBD, num_atoms ))
700 : : {
701 : : ; /* cannot advance to a non-centerpoint; ignore */
702 : : }
703 : : else
704 : : {
705 [ # # ]: 0 : if (nLenDfsPath < nCycleLen)
706 : : {
707 : : /* advance */
708 : 0 : nLenDfsPath++;
709 : 0 : cur_at = nxt_at;
710 : 0 : DfsPath[nLenDfsPath].at_no = cur_at;
711 : 0 : DfsPath[nLenDfsPath].bond_type = 0;
712 : 0 : DfsPath[nLenDfsPath].bond_pos = -1;
713 : 0 : nDfsPathPos[cur_at] = nLenDfsPath + 1; /* mark */
714 : : }
715 : : }
716 : : }
717 : : }
718 : : }
719 : : else
720 : : {
721 : : /* retract */
722 : 0 : nDfsPathPos[(int) DfsPath[nLenDfsPath].at_no] = 0;
723 : 0 : nLenDfsPath--;
724 : : }
725 : : }
726 : :
727 : 0 : clear_path:
728 [ # # ]: 0 : while (0 <= nLenDfsPath)
729 : : {
730 : 0 : nDfsPathPos[(int) DfsPath[nLenDfsPath].at_no] = 0;
731 : 0 : nLenDfsPath--;
732 : : }
733 : :
734 : 0 : return nNumFound;
735 : : }
736 : :
737 : : #endif /* TAUT_15_NON_RING */
738 : :
739 : :
740 : : /****************************************************************************
741 : : Check if bonds are alternating
742 : : ****************************************************************************/
743 : 0 : int are_alt_bonds( U_CHAR *bonds, int len )
744 : : {
745 : : U_CHAR next_bond;
746 : 0 : int i, bAnyBond, bTautBondPresent = BOND_ALTERN;
747 [ # # # # : 0 : if (len < 2 || bonds[0] == BOND_TRIPLE || bonds[0] == BOND_ALT_13)
# # ]
748 : : {
749 : 0 : return 0;
750 : : }
751 [ # # ]: 0 : next_bond = bonds[0] == BOND_SINGLE ? BOND_DOUBLE : bonds[0] == BOND_DOUBLE ? BOND_SINGLE : 0; /* djb-rwth: removing redundant code; ignoring LLVM warning: possible presence of global variables */
752 [ # # ]: 0 : if (bonds[0] == BOND_TAUTOM)
753 : : {
754 : 0 : bTautBondPresent = BOND_TAUTOM;
755 : 0 : next_bond = 0;
756 : : }
757 : : else
758 : : {
759 [ # # ]: 0 : next_bond = bonds[0] == BOND_SINGLE ? BOND_DOUBLE : bonds[0] == BOND_DOUBLE ? BOND_SINGLE : 0;
760 : : }
761 : :
762 [ # # ]: 0 : for (i = 1; i < len; i++)
763 : : {
764 [ # # ]: 0 : if (bonds[i] == BOND_TAUTOM)
765 : : {
766 : 0 : bTautBondPresent = BOND_TAUTOM;
767 : 0 : bAnyBond = 1;
768 : : }
769 : : else
770 : : {
771 [ # # # # ]: 0 : bAnyBond = ( bonds[i] == BOND_ALTERN || bonds[i] == BOND_ALT12NS );
772 : : }
773 [ # # ]: 0 : if (next_bond)
774 : : {
775 [ # # # # ]: 0 : if (bonds[i] == next_bond || bAnyBond)
776 : : {
777 [ # # ]: 0 : next_bond = ( next_bond == BOND_SINGLE ) ? BOND_DOUBLE : BOND_SINGLE;
778 : 0 : continue;
779 : : }
780 : 0 : return 0;
781 : : }
782 : : else
783 : : {
784 [ # # ]: 0 : if (bonds[i] == BOND_SINGLE)
785 : : {
786 : 0 : next_bond = BOND_DOUBLE;
787 : 0 : continue;
788 : : }
789 : : else
790 : : {
791 [ # # ]: 0 : if (bonds[i] == BOND_DOUBLE)
792 : : {
793 : 0 : next_bond = BOND_SINGLE;
794 : 0 : continue;
795 : : }
796 : : else
797 : : {
798 [ # # ]: 0 : if (!bAnyBond)
799 : : {
800 : 0 : return 0;
801 : : }
802 : : }
803 : : }
804 : : }
805 : : }
806 [ # # ]: 0 : return !next_bond ? bTautBondPresent : ( next_bond == BOND_SINGLE )
807 [ # # ]: 0 : ? BOND_DOUBLE : BOND_SINGLE; /* bond to the end atom */
808 : : }
809 : :
810 : : /****************************************************************************/
811 : 0 : int AddBondsPos( inp_ATOM *atom,
812 : : T_BONDPOS *BondPosTmp,
813 : : int nNumBondPosTmp,
814 : : T_BONDPOS *BondPos,
815 : : int nMaxNumBondPos,
816 : : int nNumBondPos )
817 : : {
818 : : int i, j, k, cur_at, nxt_at;
819 : : /* add opposite direction bonds to BondPosTmp */
820 [ # # ]: 0 : for (j = 0; j < nNumBondPosTmp; j += 2)
821 : : {
822 : 0 : cur_at = BondPosTmp[j].nAtomNumber;
823 : 0 : nxt_at = atom[cur_at].neighbor[(int) BondPosTmp[j].neighbor_index];
824 [ # # ]: 0 : for (k = 0; k < atom[nxt_at].valence; k++)
825 : : {
826 [ # # ]: 0 : if (cur_at == atom[nxt_at].neighbor[k])
827 : : {
828 : 0 : BondPosTmp[j + 1].nAtomNumber = nxt_at;
829 : 0 : BondPosTmp[j + 1].neighbor_index = k;
830 : 0 : break;
831 : : }
832 : : }
833 : : }
834 : : /* add new tautomeric bonds */
835 [ # # ]: 0 : for (j = 0; j < nNumBondPosTmp; j += 2)
836 : : {
837 [ # # ]: 0 : for (i = 0; i < nNumBondPos; i++)
838 : : {
839 [ # # ]: 0 : if ((BondPos[i].nAtomNumber == BondPosTmp[j].nAtomNumber &&
840 [ # # ]: 0 : BondPos[i].neighbor_index == BondPosTmp[j].neighbor_index) ||
841 [ # # ]: 0 : (BondPos[i].nAtomNumber == BondPosTmp[j + 1].nAtomNumber &&
842 [ # # ]: 0 : BondPos[i].neighbor_index == BondPosTmp[j + 1].neighbor_index)) /* djb-rwth: addressing LLVM warnings */
843 : : {
844 : : break; /* bond has already been added */
845 : : }
846 : : }
847 [ # # ]: 0 : if (i == nNumBondPos)
848 : : {
849 [ # # ]: 0 : if (i > nMaxNumBondPos)
850 : : {
851 : 0 : return -1; /* overflow */
852 : : }
853 : 0 : BondPos[nNumBondPos++] = BondPosTmp[j];
854 : : }
855 : : }
856 : :
857 : 0 : return nNumBondPos;
858 : : }
859 : :
860 : :
861 : : /****************************************************************************/
862 : 0 : int AddEndPoints( T_ENDPOINT *EndPointTmp,
863 : : int nNumNewEndPoint,
864 : : T_ENDPOINT *EndPoint,
865 : : int nMaxNumEndPoint,
866 : : int nNumEndPoint )
867 : : {
868 : : int i, j;
869 : : /* add new endpoints */
870 [ # # ]: 0 : for (j = 0; j < nNumNewEndPoint; j++)
871 : : {
872 [ # # ]: 0 : for (i = 0; i < nNumEndPoint; i++)
873 : : {
874 [ # # ]: 0 : if (EndPoint[i].nAtomNumber == EndPointTmp[j].nAtomNumber)
875 : : {
876 : 0 : break;
877 : : }
878 : : }
879 [ # # ]: 0 : if (i == nNumEndPoint)
880 : : {
881 [ # # ]: 0 : if (i > nMaxNumEndPoint)
882 : : {
883 : 0 : return -1; /* overflow */
884 : : }
885 : 0 : EndPoint[nNumEndPoint++] = EndPointTmp[j];
886 : : }
887 : : }
888 : :
889 : 0 : return nNumEndPoint;
890 : : }
891 : :
892 : :
893 : : /****************************************************************************/
894 : : /*
895 : :
896 : : 1,4 tautomerism in 7-member ring
897 : :
898 : : /C==D //C-D A=DfsPath[0].at_no
899 : : O=B \ HO-B \\ B=DfsPath[1].at_no
900 : : | E <-> | E nStartAtomNeighbor2: from A to HO
901 : : HO-A // O=A / nStartAtomNeighborNeighbor: from B to O
902 : : \\G-F \\G-F
903 : :
904 : :
905 : : 1,4 tautomerism in 5-member ring
906 : :
907 : :
908 : : O=B--C O-B==C
909 : : | \\ | \
910 : : | D <-> | D
911 : : | / | //
912 : : HO-A==E HO=A--E
913 : :
914 : : */
915 : : /****************************************************************************/
916 : 0 : int Check7MembTautRing( struct tagCANON_GLOBALS *pCG,
917 : : inp_ATOM *atom,
918 : : DFS_PATH *DfsPath,
919 : : int nLenDfsPath,
920 : : int nStartAtomNeighbor,
921 : : int nStartAtomNeighbor2,
922 : : int nStartAtomNeighborNeighbor,
923 : : T_ENDPOINT *EndPoint,
924 : : int nMaxNumEndPoint,
925 : : T_BONDPOS *BondPos,
926 : : int nMaxNumBondPos,
927 : : int *pnNumEndPoint,
928 : : int *pnNumBondPos,
929 : : struct BalancedNetworkStructure *pBNS,
930 : : struct BalancedNetworkData *pBD,
931 : : int num_atoms )
932 : : {
933 : : #define PATH_LEN 8
934 : :
935 : : int i, j, k, /*m,*/ nNumEndPoint, nNumEndPointTmp, nNumBondPos, nNumBondPosTmp;
936 : : int endpoint, /*nMobile, nMobile1, nMobile2,*/ o1_at, o2_at;
937 : : int ret;
938 : : U_CHAR path_bonds[PATH_LEN + 1], bond_type;
939 : : T_ENDPOINT EndPointTmp[2];
940 : : T_BONDPOS BondPosTmp[2 * PATH_LEN];
941 : : ENDPOINT_INFO eif1, eif2;
942 : 0 : int nErr = 0;
943 : :
944 : :
945 [ # # ]: 0 : if (nLenDfsPath + 2 > PATH_LEN)
946 : : {
947 : 0 : return -1; /* too long path */
948 : : }
949 [ # # # # ]: 0 : if (nLenDfsPath != 6 && nLenDfsPath != 4)
950 : : {
951 : 0 : return -1; /* wrong call */
952 : : }
953 : :
954 : 0 : nNumBondPos = *pnNumBondPos;
955 : 0 : nNumEndPoint = *pnNumEndPoint;
956 : 0 : nNumBondPosTmp = 0;
957 : 0 : nNumEndPointTmp = 0;
958 : 0 : ret = 0;
959 : :
960 : 0 : o1_at = atom[(int) DfsPath[1].at_no].neighbor[nStartAtomNeighborNeighbor];
961 : 0 : o2_at = atom[(int) DfsPath[0].at_no].neighbor[nStartAtomNeighbor2];
962 : : /*
963 : : nMobile1 = (atom[o1_at].charge == -1) + atom[o1_at].num_H;
964 : : nMobile2 = (atom[o2_at].charge == -1) + atom[o2_at].num_H;
965 : : */
966 [ # # # # ]: 0 : if (!nGetEndpointInfo( atom, o1_at, &eif1 ) ||
967 : 0 : !nGetEndpointInfo( atom, o2_at, &eif2 ))
968 : : {
969 : 0 : return 0;
970 : : }
971 : :
972 : : /* save endpoints */
973 [ # # ]: 0 : for (j = 0; j < 2; j++)
974 : : {
975 [ # # ]: 0 : endpoint = j ? o2_at : o1_at;
976 [ # # ]: 0 : if (!atom[endpoint].endpoint)
977 : : {
978 : 0 : AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
979 : 0 : AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
980 : : /*
981 : : nMobile = j? nMobile2 : nMobile1;
982 : : } else {
983 : : nMobile = 0;
984 : : }
985 : : if ( nMobile ) {
986 : : EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1);
987 : : EndPointTmp[nNumEndPointTmp].num[0] = nMobile;
988 : : for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) {
989 : : EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1];
990 : : }
991 : : */
992 : : }
993 : : else
994 : : {
995 : 0 : memset( EndPointTmp + nNumEndPointTmp, 0, sizeof( EndPointTmp[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
996 : : }
997 : 0 : EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
998 : 0 : EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
999 : 0 : EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
1000 : 0 : nNumEndPointTmp++;
1001 : : }
1002 : :
1003 : : /* extract bonds */
1004 : 0 : k = (int) DfsPath[1].at_no;
1005 : 0 : bond_type = ( atom[k].bond_type[nStartAtomNeighborNeighbor] & ~BOND_MARK_ALL );
1006 : : #if ( FIX_BOND23_IN_TAUT == 1 )
1007 : : bond_type = ACTUAL_ORDER( pBNS, k, nStartAtomNeighborNeighbor, bond_type );
1008 : : #endif
1009 : 0 : path_bonds[0] = bond_type;
1010 [ # # # # : 0 : if (REPLACE_THE_BOND( bond_type ))
# # # # ]
1011 : : {
1012 : 0 : BondPosTmp[nNumBondPosTmp].nAtomNumber = k;
1013 : 0 : BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighborNeighbor;
1014 : 0 : nNumBondPosTmp += 2;
1015 : : }
1016 [ # # ]: 0 : for (i = 1; i <= nLenDfsPath; i++)
1017 : : {
1018 : 0 : bond_type = DfsPath[i].bond_type;
1019 : 0 : path_bonds[i] = bond_type;
1020 [ # # # # : 0 : if (REPLACE_THE_BOND( bond_type ))
# # # # ]
1021 : : {
1022 : 0 : BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no;
1023 : 0 : BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos;
1024 : 0 : nNumBondPosTmp += 2;
1025 : : }
1026 : : }
1027 : 0 : bond_type = ( atom[(int) DfsPath[0].at_no].bond_type[nStartAtomNeighbor2] & ~BOND_MARK_ALL );
1028 : : #if ( FIX_BOND23_IN_TAUT == 1 )
1029 : : bond_type = ACTUAL_ORDER( pBNS, (int) DfsPath[0].at_no, nStartAtomNeighbor2, bond_type );
1030 : : #endif
1031 : 0 : path_bonds[i++] = bond_type;
1032 [ # # # # : 0 : if (REPLACE_THE_BOND( bond_type ))
# # # # ]
1033 : : {
1034 : 0 : BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[0].at_no;
1035 : 0 : BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighbor2;
1036 : 0 : nNumBondPosTmp += 2;
1037 : : }
1038 : :
1039 [ # # ]: 0 : if (!are_alt_bonds( path_bonds, i ))
1040 : : {
1041 : 0 : return 0;
1042 : : }
1043 : :
1044 : : /* path_bonds is from at_n1 to at_n2 */
1045 [ # # ]: 0 : if (!( j = are_alt_bonds( path_bonds, i ) ))
1046 : : {
1047 : 0 : return 0;
1048 : : }
1049 : : /* j is a bond type of the last bond to o2_at, the first bond from o1_at is 2-j if j=1 or 2 */
1050 : :
1051 : : /* single bond at o2_at: it should have a mobile atom, o1_at should not */
1052 [ # # # # : 0 : if ((j == BOND_SINGLE && ( (!atom[o2_at].endpoint && !eif2.cDonor) || (!atom[o1_at].endpoint && !eif1.cAcceptor) )) ||
# # # # #
# # # ]
1053 : : /* double bond at o2_at: it should not have a mobile atom, o1_at should */
1054 [ # # # # : 0 : (j == BOND_DOUBLE && ( (!atom[o2_at].endpoint && !eif2.cAcceptor) || (!atom[o1_at].endpoint && !eif1.cDonor) ))) /* djb-rwth: addressing LLVM warnings */
# # # # ]
1055 : : {
1056 : 0 : return 0; /* bond pattern does not fit */
1057 : : }
1058 : :
1059 : 0 : nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
1060 : 0 : nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint );
1061 : :
1062 [ # # # # ]: 0 : if (nNumBondPos >= 0 && nNumEndPoint >= 0)
1063 : : {
1064 [ # # # # : 0 : if ((ret = ( nNumBondPos > *pnNumBondPos ) || ( nNumEndPoint > *pnNumEndPoint ))) /* djb-rwth: addressing LLVM warning */
# # ]
1065 : : {
1066 : 0 : *pnNumBondPos = nNumBondPos;
1067 : 0 : *pnNumEndPoint = nNumEndPoint;
1068 : : }
1069 : : }
1070 : :
1071 [ # # ]: 0 : if (ret)
1072 : : {
1073 : : /* finally check whether the bonds allow moving the hydrogens */
1074 [ # # # # ]: 0 : if (( atom[o1_at].endpoint != atom[o2_at].endpoint || !atom[o1_at].endpoint ))
1075 : : {
1076 : :
1077 : 0 : nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, o1_at, o2_at, ALT_PATH_MODE_TAUTOM );
1078 : :
1079 [ # # ]: 0 : if (nErr <= 0)
1080 : : {
1081 : 0 : return nErr;
1082 : : }
1083 : : }
1084 : : }
1085 : :
1086 : 0 : return ret;
1087 : :
1088 : :
1089 : : #undef PATH_LEN
1090 : : }
1091 : :
1092 : :
1093 : : /****************************************************************************/
1094 : : /*
1095 : : 1,5 Tautomerism in 6-member alt ring:
1096 : :
1097 : : /=\ /==\ N = DfsPath[0].at_no
1098 : : HN C=O <-> N C-OH C = DfsPath[3].at_no
1099 : : \=/ \\-//
1100 : :
1101 : : */
1102 : : /****************************************************************************/
1103 : :
1104 : : /****************************************************************************
1105 : : Check if a tautomeric 6-member ring has been found
1106 : : *****************************************************************************/
1107 : 0 : int Check6MembTautRing( struct tagCANON_GLOBALS *pCG,
1108 : : inp_ATOM *atom,
1109 : : DFS_PATH *DfsPath,
1110 : : int nLenDfsPath,
1111 : : int nStartAtomNeighbor,
1112 : : int nStartAtomNeighbor2,
1113 : : int nStartAtomNeighborNeighbor,
1114 : : T_ENDPOINT *EndPoint,
1115 : : int nMaxNumEndPoint,
1116 : : T_BONDPOS *BondPos,
1117 : : int nMaxNumBondPos,
1118 : : int *pnNumEndPoint,
1119 : : int *pnNumBondPos,
1120 : : struct BalancedNetworkStructure *pBNS,
1121 : : struct BalancedNetworkData *pBD,
1122 : : int num_atoms )
1123 : : {
1124 : : #define PATH_LEN 4
1125 : : int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint, ept, eptn;
1126 : 0 : int nNumEndPointTmp, nNumBondPosTmp, o_at = 0, ret;
1127 : : /* int num_taut_endpoints, num_H; */
1128 : : int middle_pos;
1129 : : int nMobile, endpoint, endpoint_valence, chem_bonds_valence;
1130 : : int nMobile1, endpoint_valence1; /* o_at */
1131 : : int nMobile2, endpoint_valence2; /* n_at */
1132 : : int nxt_at;
1133 : : int n_at;
1134 : : U_CHAR path_bonds[2][PATH_LEN + 1], bond_type;
1135 : : T_ENDPOINT EndPointTmp[2];
1136 : : T_BONDPOS BondPosTmp[4 * PATH_LEN];
1137 : : ENDPOINT_INFO eif1, eif2;
1138 : :
1139 [ # # # # : 0 : if (nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0)
# # ]
1140 : : {
1141 : 0 : return -1; /* wrong call */
1142 : : }
1143 [ # # ]: 0 : if (nLenDfsPath != 5)
1144 : : {
1145 : 0 : return -1; /* wrong call */
1146 : : }
1147 : :
1148 : 0 : nNumBondPos = *pnNumBondPos;
1149 : 0 : nNumEndPoint = *pnNumEndPoint;
1150 : : /* djb-rwth: removing redundant code */
1151 : 0 : nNumEndPointTmp = 0;
1152 : 0 : ret = 0;
1153 [ # # ]: 0 : for (ept = 0; ept < 2; ept++) /* djb-rwth: initialisation needed for num array */
1154 [ # # ]: 0 : for (eptn = 0; eptn < T_NUM_NO_ISOTOPIC + T_NUM_ISOTOPIC; eptn++)
1155 : 0 : EndPointTmp[ept].num[eptn] = 0;
1156 : :
1157 : 0 : n_at = (int) DfsPath[0].at_no; /* -N= or -NH- atom */
1158 : 0 : nxt_at = DfsPath[middle_pos = ( nLenDfsPath + 1 ) / 2].at_no; /* must have tautomeric neighbor -OH or =O or -NH2 or =NH */
1159 : :
1160 [ # # ]: 0 : if (atom[nxt_at].valence != 3
1161 : : #if ( TAUT_RINGS_ATTACH_CHAIN == 1 )
1162 [ # # ]: 0 : || !atom[nxt_at].bCutVertex
1163 : : #endif
1164 : : )
1165 : : {
1166 : 0 : return 0;
1167 : : }
1168 : :
1169 [ # # ]: 0 : for (i = 0; i < atom[nxt_at].valence; i++)
1170 : : {
1171 : 0 : o_at = atom[nxt_at].neighbor[i];
1172 [ # # # # ]: 0 : if (o_at != DfsPath[middle_pos - 1].at_no && o_at != DfsPath[middle_pos + 1].at_no)
1173 : : {
1174 : 0 : break; /* >=O or />-OH has been found */
1175 : : }
1176 : : }
1177 [ # # ]: 0 : if (i == atom[nxt_at].valence)
1178 : : {
1179 : 0 : return 0; /* no neighboring atom >=O or />-OH */
1180 : : }
1181 : 0 : bond_type = ( atom[nxt_at].bond_type[i] & ~BOND_MARK_ALL );
1182 : : #if ( FIX_BOND23_IN_TAUT == 1 )
1183 : : bond_type = ACTUAL_ORDER( pBNS, nxt_at, i, bond_type );
1184 : : #endif
1185 [ # # # # ]: 0 : if (bond_type != BOND_SINGLE &&
1186 [ # # ]: 0 : bond_type != BOND_DOUBLE &&
1187 [ # # ]: 0 : bond_type != BOND_TAUTOM &&
1188 [ # # ]: 0 : bond_type != BOND_ALT12NS &&
1189 : : bond_type != BOND_ALTERN)
1190 : : {
1191 : 0 : return 0;
1192 : : }
1193 : :
1194 : : /* check whether the two atoms already belong to one tautomeric group */
1195 : : #if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 )
1196 : : if (atom[n_at].endpoint && atom[n_at].endpoint == atom[o_at].endpoint)
1197 : : {
1198 : : return 0;
1199 : : }
1200 : : #endif
1201 : : /* check =O valence; must be 2 for O, S, Se or 3 for N */
1202 [ # # ]: 0 : if (!( endpoint_valence1 = nGetEndpointInfo( atom, o_at, &eif1 ) ))
1203 : : {
1204 : 0 : return 0; /* n_at has been checked in MarkTautomerGroups(...) */
1205 : : }
1206 : : /*
1207 : : if ( 2 != endpoint_valence1 )
1208 : : return 0; // accept only O, S, Se
1209 : : */
1210 : : /* check hydrogens/endpoints */
1211 : 0 : nMobile1 = atom[o_at].num_H + ( atom[o_at].charge == -1 );
1212 [ # # # # : 0 : if (bond_type == BOND_SINGLE && !eif1.cDonor && !atom[o_at].endpoint)
# # ]
1213 : : {
1214 : 0 : return 0;
1215 : : }
1216 : : /* not needed since nGetEndpointInfo returned non-zero
1217 : : if ( nMobile1 + atom[o_at].chem_bonds_valence != endpoint_valence1 )
1218 : : return 0;
1219 : : */
1220 : :
1221 [ # # ]: 0 : if (!( endpoint_valence2 = nGetEndpointInfo( atom, n_at, &eif2 ) ))
1222 : : {
1223 : 0 : return 0; /* should not happen here */
1224 : : }
1225 : 0 : nMobile2 = atom[n_at].num_H + ( atom[n_at].charge == -1 );
1226 : :
1227 : 0 : nMobile = 0;
1228 : :
1229 : : /* can mobile group move from o_at to n_at? */
1230 [ # # # # ]: 0 : nMobile += ( atom[o_at].endpoint || eif1.cDonor ) && /* from o_at */
1231 [ # # ]: 0 : bond_type != BOND_DOUBLE &&
1232 [ # # ]: 0 : ( atom[n_at].endpoint || /* to n_at */
1233 [ # # ]: 0 : eif2.cNeutralBondsValence > atom[n_at].valence );
1234 : : /* can mobile group move from n_at to o_at? */
1235 [ # # ]: 0 : nMobile += ( atom[n_at].endpoint || eif2.cDonor ) && /* from n_at */
1236 [ # # ]: 0 : ( atom[o_at].endpoint || /* to o_at */
1237 [ # # # # : 0 : eif1.cNeutralBondsValence > atom[o_at].valence ) &&
# # ]
1238 : : bond_type != BOND_SINGLE;
1239 : :
1240 : :
1241 [ # # ]: 0 : if (!nMobile)
1242 : : {
1243 : 0 : return 0;
1244 : : }
1245 : :
1246 : : /*
1247 : : num_H = atom[n_at].num_H + atom[o_at].num_H;
1248 : : num_taut_endpoints = (0!=atom[n_at].endpoint) + (0!=atom[o_at].endpoint); // if O, N already are endpoints
1249 : : if ( num_H != 1 && num_taut_endpoints != 2 && !(num_H==2 && num_taut_endpoints >= 1) ) {
1250 : : return 0;
1251 : : }
1252 : : */
1253 : : /* extract -OH bond */
1254 : 0 : nNumBondPosTmp = 0;
1255 : :
1256 : 0 : path_bonds[0][0] = path_bonds[1][0] = bond_type;
1257 [ # # # # : 0 : if (REPLACE_THE_BOND( bond_type ))
# # # # ]
1258 : : {
1259 : 0 : BondPosTmp[nNumBondPosTmp].nAtomNumber = nxt_at; /* accumulate bonds to be */
1260 : 0 : BondPosTmp[nNumBondPosTmp].neighbor_index = i; /* marked as tautomeric */
1261 : 0 : nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */
1262 : : }
1263 : :
1264 : : /* extract other bonds */
1265 : : /* path_bonds[] contents:
1266 : :
1267 : :
1268 : : O OH OH
1269 : : || | |
1270 : : / \ // \ / \\
1271 : : || || <--> | || <--> || |
1272 : : \ / \\ / \ //
1273 : : NH N N
1274 : :
1275 : : path[0]: O=NH-=- OH-N... OH.N...
1276 : : path[1] O=NH-=- OH-N... OH.N...
1277 : : bonds are all bonds all bonds
1278 : : single and are either are either
1279 : : double alt or taut alt or taut
1280 : : */
1281 [ # # ]: 0 : for (j = 0; j < middle_pos; j++)
1282 : : {
1283 [ # # ]: 0 : for (i = 0; i < 2; i++)
1284 : : {
1285 : : /* k = i? j : middle_pos-1-j; */
1286 [ # # ]: 0 : k = i ? middle_pos + j : middle_pos - 1 - j;
1287 : : /* i=0: from O neighbor i=0: down to N, i=1: up to N */
1288 : 0 : bond_type = DfsPath[k].bond_type;
1289 : :
1290 : 0 : path_bonds[i][j + 1] = bond_type;
1291 [ # # # # : 0 : if (REPLACE_THE_BOND( bond_type ))
# # # # ]
1292 : : {
1293 : 0 : BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */
1294 : 0 : BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */
1295 : 0 : nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */
1296 : : }
1297 : : }
1298 : : }
1299 : :
1300 [ # # # # ]: 0 : if (!are_alt_bonds( path_bonds[0], middle_pos + 1 ) || !are_alt_bonds( path_bonds[1], middle_pos + 1 ))
1301 : : {
1302 : 0 : return 0;
1303 : : }
1304 : :
1305 : : /* finally check whether the bonds allow moving the hydrogens */
1306 [ # # # # ]: 0 : if (( atom[o_at].endpoint != atom[n_at].endpoint || !atom[o_at].endpoint ))
1307 : : {
1308 : : int nErr;
1309 : 0 : nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, n_at, o_at, ALT_PATH_MODE_TAUTOM );
1310 [ # # ]: 0 : if (nErr <= 0)
1311 : : {
1312 : 0 : return nErr;
1313 : : }
1314 : : }
1315 : :
1316 : : /* save endpoints */
1317 [ # # ]: 0 : for (j = 0; j < 2; j++)
1318 : : {
1319 [ # # ]: 0 : endpoint = j ? n_at : /* =N- 2 */
1320 : : o_at; /* -OH 1 */
1321 [ # # ]: 0 : if (!atom[endpoint].endpoint)
1322 : : { /* not a known endpoint */
1323 [ # # ]: 0 : endpoint_valence = j ? endpoint_valence2 : endpoint_valence1;
1324 [ # # ]: 0 : chem_bonds_valence = j ? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence;
1325 : : /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */
1326 [ # # ]: 0 : nMobile = j ? nMobile2 : nMobile1;
1327 : : /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */
1328 : : /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/
1329 [ # # ]: 0 : if (nMobile + chem_bonds_valence != endpoint_valence)
1330 : 0 : return 0; /* abnormal endpoint valence; ignore. */
1331 : 0 : AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
1332 : 0 : AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
1333 : : /*
1334 : : EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1);
1335 : : EndPointTmp[nNumEndPointTmp].num[0] = nMobile;
1336 : : for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) {
1337 : : EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1];
1338 : : }
1339 : : */
1340 : : }
1341 : : else
1342 : : { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */
1343 : 0 : memset( EndPointTmp + nNumEndPointTmp, 0, sizeof( EndPointTmp[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
1344 : : }
1345 : 0 : EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
1346 : 0 : EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
1347 : 0 : EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
1348 : :
1349 : 0 : nNumEndPointTmp++;
1350 : : }
1351 : : /* add collected tautomeric bonds and endpoints to the input/output data */
1352 : 0 : nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
1353 : 0 : nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint );
1354 : :
1355 [ # # # # ]: 0 : if (nNumBondPos >= 0 && nNumEndPoint >= 0)
1356 : : {
1357 [ # # # # : 0 : if ((ret = ( nNumBondPos > *pnNumBondPos ) || ( nNumEndPoint > *pnNumEndPoint ))) /* djb-rwth: addressing LLVM warning */
# # ]
1358 : : {
1359 : 0 : *pnNumBondPos = nNumBondPos;
1360 : 0 : *pnNumEndPoint = nNumEndPoint;
1361 : : }
1362 : : }
1363 : :
1364 : 0 : return ret;
1365 : : #undef PATH_LEN
1366 : : }
1367 : :
1368 : :
1369 : : #if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */
1370 : :
1371 : : /****************************************************************************
1372 : : Check (1,5) taut alt path centerpoint (unfinished) [add path checking]
1373 : : ****************************************************************************/
1374 : 0 : int Check15TautPathCenterpoint( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh,
1375 : : struct BalancedNetworkStructure *pBNS,
1376 : : struct BalancedNetworkData *pBD, int num_atoms )
1377 : : {
1378 : 0 : int nxt_at = atom[DfsPath[nLenDfsPath].at_no].neighbor[jNxtNeigh];
1379 : : /* atom[nxt_at].endpoint below allows for keto-enol -CH< or -CH2- endpoints */
1380 [ # # # # ]: 0 : return atom[nxt_at].endpoint || bIsCenterPointStrict( atom, nxt_at );
1381 : : }
1382 : :
1383 : : /****************************************************************************
1384 : :
1385 : : 1,5 Tautomerism in general (unfinished) [just a copy from 6-memb case]
1386 : :
1387 : : AH--B==C--D==E C may be carbon exhibiting keto-enol tautomerism
1388 : : 0 1 2 3 4 as well as A or E may be previously detected such a carbon
1389 : : ^ nxt_at
1390 : : |
1391 : : +-- = nLenDfsPath
1392 : :
1393 : : ****************************************************************************/
1394 : :
1395 : :
1396 : : /****************************************************************************
1397 : : check if 1,5 tautomeric path has been found
1398 : : ****************************************************************************/
1399 : 0 : int Check15TautPath( struct tagCANON_GLOBALS *pCG,
1400 : : inp_ATOM *atom, DFS_PATH *DfsPath,
1401 : : int nLenDfsPath,
1402 : : int jNxtNeigh,
1403 : : int nStartAtomNeighbor,
1404 : : int nStartAtomNeighbor2,
1405 : : int nStartAtomNeighborNeighbor,
1406 : : T_ENDPOINT *EndPoint,
1407 : : int nMaxNumEndPoint,
1408 : : T_BONDPOS *BondPos,
1409 : : int nMaxNumBondPos,
1410 : : int *pnNumEndPoint,
1411 : : int *pnNumBondPos,
1412 : : struct BalancedNetworkStructure *pBNS,
1413 : : struct BalancedNetworkData *pBD,
1414 : : int num_atoms )
1415 : : {
1416 : : #define PATH_LEN 4
1417 : : int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint, cur_at, prv_at, at1, at2 /*, at3, step_at*/;
1418 : : int nNumEndPointTmp, nNumBondPosTmp, ret;
1419 : : /* int num_taut_endpoints, num_H; */
1420 : : int nMobile, endpoint, endpoint_valence, chem_bonds_valence;
1421 : : int nMobile1, endpoint_valence1; /* start atom, at1 */
1422 : : int nMobile2, endpoint_valence2; /* end atom, at2 */
1423 : : /*int nMobile3, endpoint_valence3=-1;*/ /* middle atom, at3 */
1424 : : /*int nxt_at;*/
1425 : : int alt_bonds[2];
1426 : : U_CHAR /*path_bonds[2][PATH_LEN+1],*/ bond_type;
1427 : : T_ENDPOINT EndPointTmp[2];
1428 : : T_BONDPOS BondPosTmp[4 * PATH_LEN];
1429 : : ENDPOINT_INFO eif1, eif2/*, eif3*/;
1430 : :
1431 [ # # # # : 0 : if (nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0)
# # ]
1432 : : {
1433 : 0 : return -1; /* wrong call */
1434 : : }
1435 [ # # ]: 0 : if (nLenDfsPath != 3)
1436 : : {
1437 : 0 : return -1; /* wrong call */
1438 : : }
1439 : :
1440 : 0 : nNumBondPos = *pnNumBondPos;
1441 : 0 : nNumEndPoint = *pnNumEndPoint;
1442 : : /* djb-rwth: removing redundant code */
1443 : 0 : nNumEndPointTmp = 0;
1444 : 0 : ret = 0;
1445 : :
1446 : : /*-------add the last atom, nLenDfsPath=4 --*/
1447 : 0 : j = jNxtNeigh;
1448 : 0 : prv_at = DfsPath[nLenDfsPath].at_no;
1449 : 0 : cur_at = atom[prv_at].neighbor[j];
1450 : 0 : DfsPath[nLenDfsPath].bond_type = ( atom[prv_at].bond_type[j] & ~BOND_MARK_ALL );
1451 : : #if ( FIX_BOND23_IN_TAUT == 1 )
1452 : : DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER( pBNS, prv_at, j, DfsPath[nLenDfsPath].bond_type );
1453 : : #endif
1454 : 0 : DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */
1455 : :
1456 : 0 : nLenDfsPath++;
1457 : :
1458 : 0 : DfsPath[nLenDfsPath].at_no = cur_at;
1459 : 0 : DfsPath[nLenDfsPath].bond_type = 0;
1460 : 0 : DfsPath[nLenDfsPath].bond_pos = -1;
1461 : : /*nDfsPathPos[cur_at] = nLenDfsPath+1;*/ /* mark with distance + 1 */
1462 : : /*------------------------------------------*/
1463 : 0 : at1 = (int) DfsPath[0].at_no;
1464 : 0 : at2 = (int) DfsPath[nLenDfsPath].at_no;
1465 : : /*at3 = (int)DfsPath[2].at_no;*/
1466 [ # # # # ]: 0 : if (atom[at1].endpoint && atom[at1].endpoint == atom[at2].endpoint)
1467 : : {
1468 : : /* start & end already belong to the same taut group */
1469 : 0 : goto exit_function; /* nothing to do */
1470 : : }
1471 : :
1472 : : /* check bond types along alt path */
1473 : 0 : alt_bonds[0] = alt_bonds[1] = 0;
1474 [ # # ]: 0 : for (i = 0; i < nLenDfsPath; i++)
1475 : : {
1476 [ # # # # : 0 : alt_bonds[i % 2] |= IS_ALT_OR_DBLBOND( DfsPath[i].bond_type );
# # # # #
# ]
1477 : : }
1478 [ # # ]: 0 : if (( alt_bonds[0] & alt_bonds[1] & ( BOND_SINGLE | BOND_DOUBLE ) ) ||
1479 [ # # # # ]: 0 : ( alt_bonds[0] & BOND_WRONG ) || ( alt_bonds[1] & BOND_WRONG ))
1480 : : {
1481 : 0 : goto exit_function; /* incompatible with alt path or wrong bonds */\
1482 : : }
1483 : : /* check possibly tautomeric endpoints at the ends */
1484 : 0 : endpoint_valence1 = nGetEndpointInfo( atom, at1, &eif1 );
1485 : 0 : endpoint_valence2 = nGetEndpointInfo( atom, at2, &eif2 );
1486 : : #ifdef NEVER /* do not use C-endpoint of keto-enol tautomer to find 1,5 the taut path */
1487 : : if (!endpoint_valence1 && !atom[at1].endpoint ||
1488 : : !endpoint_valence2 && !atom[at2].endpoint)
1489 : : goto exit_function; /* at least one of the end atoms cannot be an endpoint */
1490 : : #endif
1491 [ # # # # ]: 0 : if (!endpoint_valence1 || !endpoint_valence2)
1492 : 0 : goto exit_function; /* require both endpoints be heteroatoms */
1493 : : /* check hydrogens/endpoints */
1494 : 0 : nMobile1 = atom[at1].num_H + ( atom[at1].charge == -1 );
1495 [ # # ]: 0 : if (!atom[at1].endpoint)
1496 : : {
1497 [ # # # # ]: 0 : if (( alt_bonds[0] & BOND_SINGLE ) && !eif1.cDonor)
1498 : : {
1499 : 0 : goto exit_function;
1500 : : }
1501 [ # # # # ]: 0 : if (( alt_bonds[0] & BOND_DOUBLE ) && !eif1.cAcceptor)
1502 : : {
1503 : 0 : goto exit_function;
1504 : : }
1505 : : }
1506 : 0 : nMobile2 = atom[at2].num_H + ( atom[at2].charge == -1 );
1507 [ # # ]: 0 : if (!atom[at2].endpoint)
1508 : : {
1509 [ # # # # ]: 0 : if (( alt_bonds[1] & BOND_SINGLE ) && !eif2.cDonor)
1510 : : {
1511 : 0 : goto exit_function;
1512 : : }
1513 [ # # # # ]: 0 : if (( alt_bonds[1] & BOND_DOUBLE ) && !eif2.cAcceptor)
1514 : : {
1515 : 0 : goto exit_function;
1516 : : }
1517 : : }
1518 : :
1519 : 0 : nMobile = 0;
1520 : :
1521 : : /* can mobile group move from at1=o_at to at2=n_at? */
1522 [ # # ]: 0 : nMobile += ( atom[at1].endpoint || eif1.cDonor ) && /* from o_at */
1523 [ # # # # ]: 0 : !( alt_bonds[0] & BOND_DOUBLE ) &&
1524 [ # # ]: 0 : ( atom[at2].endpoint || /* to n_at */
1525 [ # # ]: 0 : eif2.cNeutralBondsValence > atom[at2].valence );
1526 : : /* can mobile group move from at2=n_at to at1=o_at? */
1527 [ # # ]: 0 : nMobile += ( atom[at2].endpoint || eif2.cDonor ) && /* from n_at */
1528 [ # # # # ]: 0 : !( alt_bonds[1] & BOND_DOUBLE ) &&
1529 [ # # ]: 0 : ( atom[at1].endpoint || /* to o_at */
1530 [ # # ]: 0 : eif1.cNeutralBondsValence > atom[at1].valence );
1531 : :
1532 [ # # ]: 0 : if (!nMobile)
1533 : : {
1534 : 0 : goto exit_function;
1535 : : }
1536 : :
1537 : : /* check whether the bonds allow moving the hydrogens between at1 and at2 */
1538 [ # # # # ]: 0 : if (( atom[at1].endpoint != atom[at2].endpoint || !atom[at1].endpoint ))
1539 : : {
1540 : : int nErr;
1541 : 0 : nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, at1, at2, ALT_PATH_MODE_TAUTOM );
1542 [ # # ]: 0 : if (nErr <= 0)
1543 : : {
1544 : 0 : ret = nErr;
1545 : 0 : goto exit_function;
1546 : : }
1547 : : }
1548 : :
1549 : : /* save tautomeric bonds */
1550 : 0 : nNumBondPosTmp = 0;
1551 [ # # ]: 0 : for (k = 0; k < nLenDfsPath; k++)
1552 : : {
1553 : 0 : bond_type = DfsPath[k].bond_type;
1554 [ # # # # : 0 : if (REPLACE_THE_BOND( bond_type ))
# # # # ]
1555 : : {
1556 : 0 : BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */
1557 : 0 : BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */
1558 : 0 : nNumBondPosTmp += 2; /* leave room for the same bond in opposite direction */
1559 : : }
1560 : : }
1561 : : /* save endpoints */
1562 [ # # ]: 0 : for (j = 0; j < 2; j++)
1563 : : {
1564 [ # # ]: 0 : endpoint = j ? at2 : at1;
1565 [ # # ]: 0 : if (!atom[endpoint].endpoint)
1566 : : {
1567 : : /* not a known endpoint */
1568 [ # # ]: 0 : endpoint_valence = j ? endpoint_valence2 : endpoint_valence1;
1569 [ # # ]: 0 : chem_bonds_valence = j ? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence;
1570 : : /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */
1571 [ # # ]: 0 : nMobile = j ? nMobile2 : nMobile1;
1572 : : /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */
1573 : : /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/
1574 [ # # ]: 0 : if (nMobile + chem_bonds_valence != endpoint_valence)
1575 : : {
1576 : 0 : goto exit_function; /* abnormal endpoint valence; ignore. */
1577 : : }
1578 : 0 : AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
1579 : 0 : AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
1580 : : }
1581 : : else
1582 : : {
1583 : : /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */
1584 : 0 : memset( EndPointTmp + nNumEndPointTmp, 0, sizeof( EndPointTmp[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
1585 : : }
1586 : 0 : EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
1587 : 0 : EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
1588 : 0 : EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
1589 : :
1590 : 0 : nNumEndPointTmp++;
1591 : : }
1592 : : /* add collected tautomeric bonds and endpoints to the input/output data */
1593 : 0 : nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
1594 : 0 : nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint );
1595 : :
1596 [ # # # # ]: 0 : if (nNumBondPos >= 0 && nNumEndPoint >= 0)
1597 : : {
1598 [ # # # # : 0 : if ((ret = ( nNumBondPos > *pnNumBondPos ) || ( nNumEndPoint > *pnNumEndPoint ))) /* djb-rwth: addressing LLVM warning */
# # ]
1599 : : {
1600 : 0 : *pnNumBondPos = nNumBondPos;
1601 : 0 : *pnNumEndPoint = nNumEndPoint;
1602 : : }
1603 : : }
1604 : :
1605 : 0 : exit_function:
1606 : : /*nDfsPathPos[DfsPath[nLenDfsPath].at_no] = 0;*/
1607 : :
1608 : 0 : return ret;
1609 : :
1610 : : #undef PATH_LEN
1611 : : }
1612 : : #endif /* TAUT_15_NON_RING */
1613 : :
1614 : :
1615 : : /****************************************************************************
1616 : :
1617 : : 1,4 tautomerism in 5-member ring
1618 : :
1619 : :
1620 : : O=N2-C O-N2=C N1 = DfsPath[0].at_no
1621 : : | \\ | \ N2 = DfsPath[1].at_no
1622 : : | D <-> | D
1623 : : | / | //
1624 : : HO-N1=E HO=N1-E
1625 : :
1626 : : ****************************************************************************/
1627 : :
1628 : : /****************************************************************************
1629 : : check if a tautomeric 5-member ring (pyrazole derivatives) has been found
1630 : : ****************************************************************************/
1631 : 0 : int Check5MembTautRing( struct tagCANON_GLOBALS *pCG,
1632 : : inp_ATOM *atom,
1633 : : DFS_PATH *DfsPath,
1634 : : int nLenDfsPath,
1635 : : int nStartAtomNeighbor,
1636 : : int nStartAtomNeighbor2,
1637 : : int nStartAtomNeighborNeighbor,
1638 : : T_ENDPOINT *EndPoint,
1639 : : int nMaxNumEndPoint,
1640 : : T_BONDPOS *BondPos,
1641 : : int nMaxNumBondPos,
1642 : : int *pnNumEndPoint,
1643 : : int *pnNumBondPos,
1644 : : struct BalancedNetworkStructure *pBNS,
1645 : : struct BalancedNetworkData *pBD,
1646 : : int num_atoms )
1647 : : {
1648 : : #define PATH_LEN 4
1649 : : int i, j, /*m,*/ nMobile, nMobile1, nMobile2, ept, eptn;
1650 : : int num_taut_endpoints, nNumBondPos, nNumBondPosTmp, nNumEndPoint, nNumEndPointTmp, ret;
1651 : : int endpoint;
1652 : 0 : int n1_at = (int) DfsPath[0].at_no;
1653 : 0 : int n2_at = (int) DfsPath[1].at_no;
1654 : : U_CHAR path_bonds[PATH_LEN + 1], bond_type;
1655 : : T_ENDPOINT EndPointTmp[2];
1656 : : T_BONDPOS BondPosTmp[2 * PATH_LEN];
1657 : : ENDPOINT_INFO eif1, eif2;
1658 : :
1659 : : /* the two root atoms (atom[n1_at] and atom[n2_at]) cannot belong */
1660 : : /* to one and the same tautomeric group: it has been verified in MarkTautomerGroups() */
1661 : :
1662 : : /* check hydrogens/endpoints */
1663 [ # # ]: 0 : if (nLenDfsPath != 4)
1664 : : {
1665 : 0 : return 0; /* program error */
1666 : : }
1667 [ # # # # ]: 0 : if (nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0)
1668 : : {
1669 : 0 : return 0; /* program error: wrong call */
1670 : : }
1671 : :
1672 : 0 : nNumBondPos = *pnNumBondPos;
1673 : 0 : nNumEndPoint = *pnNumEndPoint;
1674 : 0 : nNumEndPointTmp = 0;
1675 : : /* djb-rwth: removing redundant code */
1676 : 0 : ret = 0;
1677 [ # # ]: 0 : for (ept = 0; ept < 2; ept++) /* djb-rwth: initialisation needed for num array */
1678 [ # # ]: 0 : for (eptn = 0; eptn < T_NUM_NO_ISOTOPIC + T_NUM_ISOTOPIC; eptn++)
1679 : 0 : EndPointTmp[ept].num[eptn] = 0;
1680 : :
1681 [ # # # # ]: 0 : if (!nGetEndpointInfo( atom, n1_at, &eif1 ) ||
1682 : 0 : !nGetEndpointInfo( atom, n2_at, &eif2 ))
1683 : : {
1684 : 0 : return 0;
1685 : : }
1686 : :
1687 : 0 : nMobile1 = atom[n1_at].num_H + ( atom[n1_at].charge == -1 );
1688 : 0 : nMobile2 = atom[n2_at].num_H + ( atom[n2_at].charge == -1 );
1689 : 0 : nMobile = nMobile1 + nMobile2;
1690 : 0 : num_taut_endpoints = ( 0 != atom[n1_at].endpoint ) + ( 0 != atom[n2_at].endpoint ); /* if both N atoms already are endpoints */
1691 : : /*
1692 : : if ( !(nMobile == 1 || num_taut_endpoints == 2) && !(nMobile>1 && num_taut_endpoints >= 1) ) {
1693 : : return 0;
1694 : : }
1695 : : */
1696 [ # # # # ]: 0 : if (num_taut_endpoints == 0 && nMobile != 1)
1697 : : {
1698 : 0 : return 0;
1699 : : }
1700 : :
1701 : : /* finally check whether the bonds allow moving the hydrogens */
1702 [ # # # # ]: 0 : if (( atom[n1_at].endpoint != atom[n2_at].endpoint || !atom[n1_at].endpoint ))
1703 : : {
1704 : : int nErr;
1705 : 0 : nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, n1_at, n2_at, ALT_PATH_MODE_TAUTOM );
1706 [ # # ]: 0 : if (nErr <= 0)
1707 : : {
1708 : 0 : return nErr;
1709 : : }
1710 : : }
1711 : :
1712 : : /* save endpoints */
1713 [ # # ]: 0 : for (j = 0; j < 2; j++)
1714 : : {
1715 [ # # ]: 0 : endpoint = j ? n1_at : n2_at;
1716 [ # # ]: 0 : if (!atom[endpoint].endpoint)
1717 : : {
1718 : : /* not a known endpoint */
1719 : : /*
1720 : : nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H;
1721 : : } else {
1722 : : nMobile = 0;
1723 : : }
1724 : : if ( nMobile ) {
1725 : : */
1726 : 0 : AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
1727 : 0 : AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
1728 : : /*
1729 : : EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1);
1730 : : EndPointTmp[nNumEndPointTmp].num[0] = nMobile;
1731 : : for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) {
1732 : : EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1];
1733 : : }
1734 : : */
1735 : : }
1736 : : else
1737 : : {
1738 : 0 : memset( EndPointTmp + nNumEndPointTmp, 0, sizeof( EndPointTmp[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
1739 : : }
1740 : 0 : EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
1741 : 0 : EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
1742 : 0 : EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
1743 : :
1744 : 0 : nNumEndPointTmp++;
1745 : : }
1746 : :
1747 : : /* extract bonds */
1748 : 0 : nNumBondPosTmp = 0;
1749 [ # # ]: 0 : for (i = 1; i <= nLenDfsPath; i++)
1750 : : {
1751 : 0 : bond_type = DfsPath[i].bond_type;
1752 : 0 : path_bonds[i - 1] = bond_type;
1753 [ # # # # : 0 : if (REPLACE_THE_BOND( bond_type ))
# # # # ]
1754 : : {
1755 : 0 : BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no;
1756 : 0 : BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos;
1757 : 0 : nNumBondPosTmp += 2;
1758 : : }
1759 : : }
1760 : : /* path_bonds is from at_n2 to at_n1 */
1761 [ # # ]: 0 : if (!( i = are_alt_bonds( path_bonds, nLenDfsPath ) ))
1762 : : {
1763 : 0 : return 0;
1764 : : }
1765 : : /* i is a bond type of the last bond to at_n1, the first bond from at_n2 is 2-i if i=1 or 2 */
1766 : :
1767 : : /* single bond at n1_at: it should have a mobile atom, n2_at should not */
1768 [ # # # # : 0 : if ((i == BOND_SINGLE && ( (!atom[n1_at].endpoint && !eif1.cDonor) || (!atom[n2_at].endpoint && !eif2.cAcceptor) )) ||
# # # # #
# # # ]
1769 : : /* double bond at n1_at: it should not have a mobile atom, n2_at should */
1770 [ # # # # : 0 : (i == BOND_DOUBLE && ( (!atom[n1_at].endpoint && !eif1.cAcceptor) || (!atom[n2_at].endpoint && !eif2.cDonor) ))) /* djb-rwth: addressing LLVM warnings */
# # # # ]
1771 : : {
1772 : 0 : return 0; /* bond pattern does not fit */
1773 : : }
1774 : :
1775 : 0 : nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
1776 : 0 : nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint );
1777 : :
1778 [ # # # # ]: 0 : if (nNumBondPos >= 0 && nNumEndPoint >= 0)
1779 : : {
1780 [ # # # # : 0 : if ((ret = ( nNumBondPos > *pnNumBondPos ) || ( nNumEndPoint > *pnNumEndPoint ))) /* djb-rwth: addressing LLVM warning */
# # ]
1781 : : {
1782 : 0 : *pnNumBondPos = nNumBondPos;
1783 : 0 : *pnNumEndPoint = nNumEndPoint;
1784 : : }
1785 : : }
1786 : 0 : return ret;
1787 : :
1788 : : #undef PATH_LEN
1789 : : }
1790 : : #endif /* } */
|