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