2
* International Chemical Identifier (InChI)
4
* Software version 1.02
8
* The InChI library and programs are free software developed under the
9
* auspices of the International Union of Pure and Applied Chemistry (IUPAC);
10
* you can redistribute this software and/or modify it under the terms of
11
* the GNU Lesser General Public License as published by the Free Software
13
* http://www.opensource.org/licenses/lgpl-license.php
29
/*******************************************************************/
31
#if( FIND_RING_SYSTEMS == 1 ) /* { */
33
/* local prototypes */
34
int are_alt_bonds( U_CHAR *bonds, int len );
35
int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, int nMaxNumBondPos, int nNumBondPos );
36
int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, int nNumEndPoint);
42
/******************************************
44
* Tautomerism in 5- and 6-member rings
46
******************************************/
48
const int NONE = (AT_RANK)~0;
52
1,5 Tautomerism in 6-member alt ring:
59
1,2 Tautomerism in 5-member ring:
69
1,4 tautomerism in 7-member ring
78
1,4 tautomerism in 5-member ring
88
typedef int CHECK_DFS_RING( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor,
89
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
90
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
91
T_BONDPOS *BondPos, int nMaxNumBondPos,
92
int *pnNumEndPoint, int *pnNumBondPos,
93
struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms );
95
typedef int CHECK_CENTERPOINT ( inp_ATOM *atom, int iat );
97
CHECK_DFS_RING Check7MembTautRing;
98
CHECK_DFS_RING Check6MembTautRing;
99
CHECK_DFS_RING Check5MembTautRing;
101
#if( TAUT_15_NON_RING == 1 ) /* post v.1 feature */
102
/* DFS simple alt path for 1,5 tautomerism, post v.1 feature */
103
typedef int CHECK_DFS_PATH( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor,
104
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
105
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
106
T_BONDPOS *BondPos, int nMaxNumBondPos,
107
int *pnNumEndPoint, int *pnNumBondPos,
108
struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms );
110
typedef int CHECK_DFS_CENTERPOINT( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh,
111
struct BalancedNetworkStructure *pBNS,
112
struct BalancedNetworkData *pBD, int num_atoms );
115
CHECK_DFS_PATH Check15TautPath;
116
CHECK_DFS_CENTERPOINT Check15TautPathCenterpoint;
118
int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
119
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
121
AT_RANK *nDfsPathPos, DFS_PATH *DfsPath,
122
CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPoint,
123
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
124
T_BONDPOS *BondPos, int nMaxNumBondPos,
125
int *pnNumEndPoint, int *pnNumBondPos,
126
struct BalancedNetworkStructure *pBNS,
127
struct BalancedNetworkData *pBD, int num_atoms );
129
#define BOND_WRONG 64
130
#define IS_ALT_OR_DBLBOND(X) (((X) == BOND_SINGLE || (X) == BOND_DOUBLE)? (X) : \
131
((X) == BOND_ALTERN || (X) == BOND_TAUTOM || (X) == BOND_ALT12NS)? BOND_ALTERN : \
134
#endif /* TAUT_15_NON_RING */
136
int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
137
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
139
AT_RANK *nDfsPathPos, DFS_PATH *DfsPath,
140
CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint,
141
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
142
T_BONDPOS *BondPos, int nMaxNumBondPos,
143
int *pnNumEndPoint, int *pnNumBondPos,
144
struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms );
146
#if( REPLACE_ALT_WITH_TAUT == 1 )
147
#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE || (X) == BOND_ALTERN || (X) == BOND_ALT12NS )
149
#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE )
153
int bIsCenterPointStrict( inp_ATOM *atom, int iat )
155
if ( atom[iat].valence == atom[iat].chem_bonds_valence ) {
156
int endpoint_valence = get_endpoint_valence(atom[iat].el_number);
157
if ( endpoint_valence && (endpoint_valence > atom[iat].valence && /* added a check for negative charge or H 3-31-03 */
158
(atom[iat].num_H || atom[iat].charge == -1) ||
159
!atom[iat].charge && atom[iat].c_point) ) {
160
return 1; /* may appear to be tautomeric or chargable
161
(this increases chem_bonds_valence), should be explored */
165
if (atom[iat].valence+1 == atom[iat].chem_bonds_valence &&
166
is_centerpoint_elem_strict( atom[iat].el_number ) ) {
171
/********************************************************************************/
172
int nGet14TautIn7MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
173
int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint,
174
AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath,
175
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
176
T_BONDPOS *BondPos, int nMaxNumBondPos,
177
int *pnNumEndPoint, int *pnNumBondPos,
178
struct BalancedNetworkStructure *pBNS,
179
struct BalancedNetworkData *pBD, int num_atoms )
186
if ( nMaxLenDfsPath <= 7 ) {
187
return -1; /* path is too short */
190
nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor,
191
nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 7,
192
nDfsPathPos, DfsPath,
193
Check7MembTautRing, bIsCenterPointStrict,
194
EndPoint, nMaxNumEndPoint,
195
BondPos, nMaxNumBondPos,
196
pnNumEndPoint, pnNumBondPos,
203
/********************************************************************************/
204
int nGet14TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
205
int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint,
206
AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath,
207
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
208
T_BONDPOS *BondPos, int nMaxNumBondPos,
209
int *pnNumEndPoint, int *pnNumBondPos,
210
struct BalancedNetworkStructure *pBNS,
211
struct BalancedNetworkData *pBD, int num_atoms )
218
if ( nMaxLenDfsPath <= 5 ) {
219
return -1; /* path is too short */
222
nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor,
223
nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 5,
224
nDfsPathPos, DfsPath,
225
Check7MembTautRing, bIsCenterPointStrict,
226
EndPoint, nMaxNumEndPoint,
227
BondPos, nMaxNumBondPos,
228
pnNumEndPoint, pnNumBondPos,
236
/********************************************************************************/
237
int nGet12TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
238
AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath,
239
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
240
T_BONDPOS *BondPos, int nMaxNumBondPos,
241
int *pnNumEndPoint, int *pnNumBondPos,
242
struct BalancedNetworkStructure *pBNS,
243
struct BalancedNetworkData *pBD, int num_atoms )
250
if ( nMaxLenDfsPath <= 5 ) {
251
return -1; /* path is too short */
254
nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, -1, -1, 5,
255
nDfsPathPos, DfsPath,
256
Check5MembTautRing, bIsCenterPointStrict,
257
EndPoint, nMaxNumEndPoint,
258
BondPos, nMaxNumBondPos,
259
pnNumEndPoint, pnNumBondPos,
265
/********************************************************************************/
266
int nGet15TautIn6MembAltRing( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos,
267
DFS_PATH *DfsPath, int nMaxLenDfsPath,
268
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
269
T_BONDPOS *BondPos, int nMaxNumBondPos,
270
int *pnNumEndPoint, int *pnNumBondPos,
271
struct BalancedNetworkStructure *pBNS,
272
struct BalancedNetworkData *pBD, int num_atoms )
279
if ( nMaxLenDfsPath <= 7 ) {
280
return -1; /* path is too short */
283
nRet = DFS_FindTautInARing( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/,
284
-1/*nStartAtomNeighborNeighbor*/, 6 /* nCycleLen*/,
285
nDfsPathPos, DfsPath,
286
Check6MembTautRing, bIsCenterPointStrict,
287
EndPoint, nMaxNumEndPoint,
288
BondPos, nMaxNumBondPos,
289
pnNumEndPoint, pnNumBondPos,
294
#if( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/
295
/********************************************************************************/
296
int nGet15TautInAltPath( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos,
297
DFS_PATH *DfsPath, int nMaxLenDfsPath,
298
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
299
T_BONDPOS *BondPos, int nMaxNumBondPos,
300
int *pnNumEndPoint, int *pnNumBondPos,
301
struct BalancedNetworkStructure *pBNS,
302
struct BalancedNetworkData *pBD, int num_atoms )
309
if ( nMaxLenDfsPath <= 7 ) {
310
return -1; /* path is too short */
313
nRet = DFS_FindTautAltPath( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/,
314
-1/*nStartAtomNeighborNeighbor*/, 4 /* nCycleLen*/,
315
nDfsPathPos, DfsPath,
316
Check15TautPath, Check15TautPathCenterpoint,
317
EndPoint, nMaxNumEndPoint,
318
BondPos, nMaxNumBondPos,
319
pnNumEndPoint, pnNumBondPos,
325
/********************************************************************************/
327
#define MAX_DFS_DEPTH 16
329
/********************************************************************************/
330
int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
331
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
333
AT_RANK *nDfsPathPos, DFS_PATH *DfsPath,
334
CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint,
335
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
336
T_BONDPOS *BondPos, int nMaxNumBondPos,
337
int *pnNumEndPoint, int *pnNumBondPos,
338
struct BalancedNetworkStructure *pBNS,
339
struct BalancedNetworkData *pBD, int num_atoms )
341
/* Depth First Search */
342
/* Ignore all atoms not belonging to the current ring system (=biconnected component) */
343
AT_RANK nMinLenDfsPath;
344
int j, cur_at, nxt_at, prv_at;
345
int nLenDfsPath, nNumFound, ret;
347
int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1;
354
DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom;
355
DfsPath[nLenDfsPath].bond_type = 0;
356
DfsPath[nLenDfsPath].bond_pos = -1;
357
nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */
358
nRingSystem = atom[nStartAtom].nRingSystem;
360
if ( nStartAtomNeighbor2 >= 0 ) {
361
nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2];
365
/* add the first neighbor to the 2nd tree position if required */
366
if ( nStartAtomNeighbor >= 0 ) {
367
j = nStartAtomNeighbor;
369
cur_at = atom[prv_at].neighbor[j];
370
DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL);
371
#if( FIX_BOND23_IN_TAUT == 1 )
372
DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type);
374
DfsPath[nLenDfsPath].bond_pos = j;
378
DfsPath[nLenDfsPath].at_no = cur_at;
379
DfsPath[nLenDfsPath].bond_type = 0;
380
DfsPath[nLenDfsPath].bond_pos = -1;
381
nDfsPathPos[cur_at] = nLenDfsPath+1;
383
if ( nStartAtomNeighborNeighbor >= 0 ) {
384
nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor];
388
/* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */
389
/* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */
390
while ( nLenDfsPath >= nMinLenDfsPath ) {
391
j = ++DfsPath[nLenDfsPath].bond_pos;
392
if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) {
393
DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL);
394
#if( FIX_BOND23_IN_TAUT == 1 )
395
DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type);
397
nxt_at = (int)atom[cur_at].neighbor[j];
398
if ( nxt_at == nDoNotTouchAtom1 ||
399
nxt_at == nDoNotTouchAtom2 ) {
402
if ( nDfsPathPos[nxt_at] ) {
403
/* found a ring closure or a step backwards */
404
if ( 1 == nDfsPathPos[nxt_at] && nLenDfsPath == nCycleLen ) {
405
/* we have found the cycle; check it */
406
ret = (*CheckDfsRing)( atom, DfsPath, nLenDfsPath, nStartAtomNeighbor,
407
nStartAtomNeighbor2, nStartAtomNeighborNeighbor,
408
EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos,
409
pnNumEndPoint, pnNumBondPos,
420
if ( !(*CheckCenterPoint)( atom, nxt_at ) ) {
421
; /* cannot advance to a non-centerpoint; ignore */
423
if ( nLenDfsPath < nCycleLen ) {
427
DfsPath[nLenDfsPath].at_no = cur_at;
428
DfsPath[nLenDfsPath].bond_type = 0;
429
DfsPath[nLenDfsPath].bond_pos = -1;
430
nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */
434
nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
439
while ( 0 <= nLenDfsPath ) {
440
nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
445
#if( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/
446
/********************************************************************************/
447
int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
448
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
450
AT_RANK *nDfsPathPos, DFS_PATH *DfsPath,
451
CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPointPath,
452
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
453
T_BONDPOS *BondPos, int nMaxNumBondPos,
454
int *pnNumEndPoint, int *pnNumBondPos,
455
struct BalancedNetworkStructure *pBNS,
456
struct BalancedNetworkData *pBD, int num_atoms )
458
/* Naive Depth First Search: same atom may be approached along different alt paths */
459
/* Ignore all atoms not belonging to the current ring system (=biconnected component) */
460
AT_RANK nMinLenDfsPath;
461
int j, cur_at, nxt_at, prv_at;
462
int nLenDfsPath, nNumFound, ret;
464
int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1;
469
nCycleLen --; /* indef of the last atom in the alt path, statring from 0 */
471
DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom;
472
DfsPath[nLenDfsPath].bond_type = 0;
473
DfsPath[nLenDfsPath].bond_pos = -1; /* initialize index of the bond to the next atom */
474
nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */
475
nRingSystem = atom[nStartAtom].nRingSystem;
476
nMinLenDfsPath = 0; /* allow to restart from nStartAtom */
477
if ( nStartAtomNeighbor2 >= 0 ) {
478
nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2];
482
/* add the first neighbor to the 2nd tree position if required */
483
if ( nStartAtomNeighbor >= 0 ) {
484
j = nStartAtomNeighbor;
486
cur_at = atom[prv_at].neighbor[j];
487
DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL);
488
#if( FIX_BOND23_IN_TAUT == 1 )
489
DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type);
491
DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */
495
DfsPath[nLenDfsPath].at_no = cur_at;
496
DfsPath[nLenDfsPath].bond_type = 0;
497
DfsPath[nLenDfsPath].bond_pos = -1;
498
nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */
499
nMinLenDfsPath ++; /* allow to restart from nStartAtom's neighbor */
500
if ( nStartAtomNeighborNeighbor >= 0 ) {
501
nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor];
505
/* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */
506
/* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */
507
while ( nLenDfsPath >= nMinLenDfsPath ) {
508
j = ++DfsPath[nLenDfsPath].bond_pos;
509
if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) {
510
DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL);
511
#if( FIX_BOND23_IN_TAUT == 1 )
512
DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type);
514
nxt_at = (int)atom[cur_at].neighbor[j];
515
if ( nxt_at == nDoNotTouchAtom1 || /* forbidden */
516
nxt_at == nDoNotTouchAtom2 || /* forbidden */
517
nDfsPathPos[nxt_at] || /* ring closure */
518
nLenDfsPath && nxt_at == (int)DfsPath[nLenDfsPath-1].at_no /* step backwards */
520
; /* ignore nxt_at */
522
if ( nLenDfsPath == nCycleLen &&
523
/* 1,5 and at least one of the endpoints is not in a ring */
524
(atom[nxt_at].nNumAtInRingSystem == 1 || atom[nStartAtom].nNumAtInRingSystem == 1) &&
525
/* we have found the alt path of the requested length; check it */
526
/* calling Check15TautPath() */
527
(ret = (*CheckDfsPath)( atom, DfsPath, nLenDfsPath, j, nStartAtomNeighbor,
528
nStartAtomNeighbor2, nStartAtomNeighborNeighbor,
529
EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos,
530
pnNumEndPoint, pnNumBondPos,
531
pBNS, pBD, num_atoms ) ) ) {
534
goto clear_path; /* program error */
536
nNumFound += ret; /* success */
537
} else /* calling Check15TautPathCenterpoint() */
538
if ( !(*CheckCenterPointPath)( atom, DfsPath, nLenDfsPath, j,
539
pBNS, pBD, num_atoms ) ) {
540
; /* cannot advance to a non-centerpoint; ignore */
542
if ( nLenDfsPath < nCycleLen ) {
546
DfsPath[nLenDfsPath].at_no = cur_at;
547
DfsPath[nLenDfsPath].bond_type = 0;
548
DfsPath[nLenDfsPath].bond_pos = -1;
549
nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */
553
nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
558
while ( 0 <= nLenDfsPath ) {
559
nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
564
#endif /* TAUT_15_NON_RING */
565
/*******************************************
566
* check if bonds are alternating */
567
int are_alt_bonds( U_CHAR *bonds, int len )
570
int i, bAnyBond, bTautBondPresent=BOND_ALTERN;
571
if ( len < 2 || bonds[0] == BOND_TRIPLE || bonds[0] == BOND_ALT_13 ) {
574
next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0;
575
if ( bonds[0] == BOND_TAUTOM ) {
576
bTautBondPresent= BOND_TAUTOM;
579
next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0;
582
for ( i = 1; i < len; i ++ ) {
583
if ( bonds[i] == BOND_TAUTOM ) {
584
bTautBondPresent = BOND_TAUTOM;
587
bAnyBond = (bonds[i] == BOND_ALTERN || bonds[i] == BOND_ALT12NS);
590
if ( bonds[i] == next_bond || bAnyBond ) {
591
next_bond = (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE;
596
if ( bonds[i] == BOND_SINGLE ) {
597
next_bond = BOND_DOUBLE;
600
if ( bonds[i] == BOND_DOUBLE ) {
601
next_bond = BOND_SINGLE;
608
return !next_bond? bTautBondPresent :
609
(next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; /* bond to the end atom */
612
/********************************************************************************/
613
int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos,
614
int nMaxNumBondPos, int nNumBondPos )
616
int i, j, k, cur_at, nxt_at;
617
/* add opposite direction bonds to BondPosTmp */
618
for ( j = 0; j < nNumBondPosTmp; j += 2 ) {
619
cur_at = BondPosTmp[j].nAtomNumber;
620
nxt_at = atom[cur_at].neighbor[(int)BondPosTmp[j].neighbor_index];
621
for ( k = 0; k < atom[nxt_at].valence; k ++ ) {
622
if ( cur_at == atom[nxt_at].neighbor[k] ) {
623
BondPosTmp[j+1].nAtomNumber = nxt_at;
624
BondPosTmp[j+1].neighbor_index = k;
629
/* add new tautomeric bonds */
630
for ( j = 0; j < nNumBondPosTmp; j += 2 ) {
631
for ( i = 0; i < nNumBondPos; i ++ ) {
632
if ( BondPos[i].nAtomNumber == BondPosTmp[j].nAtomNumber &&
633
BondPos[i].neighbor_index == BondPosTmp[j].neighbor_index ||
634
BondPos[i].nAtomNumber == BondPosTmp[j+1].nAtomNumber &&
635
BondPos[i].neighbor_index == BondPosTmp[j+1].neighbor_index ) {
636
break; /* bond has already been added */
639
if ( i == nNumBondPos ) {
640
if ( i > nMaxNumBondPos ) {
641
return -1; /* overflow */
643
BondPos[nNumBondPos ++] = BondPosTmp[j];
648
/********************************************************************************/
649
int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint,
650
int nMaxNumEndPoint, int nNumEndPoint)
653
/* add new endpoints */
654
for ( j = 0; j < nNumNewEndPoint; j ++ ) {
655
for ( i = 0; i < nNumEndPoint; i ++ ) {
656
if ( EndPoint[i].nAtomNumber == EndPointTmp[j].nAtomNumber ) {
660
if ( i == nNumEndPoint ) {
661
if ( i > nMaxNumEndPoint ) {
662
return -1; /* overflow */
664
EndPoint[nNumEndPoint ++] = EndPointTmp[j];
669
/********************************************************************************/
672
1,4 tautomerism in 7-member ring
674
/C==D //C-D A=DfsPath[0].at_no
675
O=B \ HO-B \\ B=DfsPath[1].at_no
676
| E <-> | E nStartAtomNeighbor2: from A to HO
677
HO-A // O=A / nStartAtomNeighborNeighbor: from B to O
681
1,4 tautomerism in 5-member ring
691
/********************************************************************************/
692
int Check7MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor,
693
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
694
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
695
T_BONDPOS *BondPos, int nMaxNumBondPos,
696
int *pnNumEndPoint, int *pnNumBondPos,
697
struct BalancedNetworkStructure *pBNS,
698
struct BalancedNetworkData *pBD, int num_atoms )
702
int i, j, k, /*m,*/ nNumEndPoint, nNumEndPointTmp, nNumBondPos, nNumBondPosTmp;
703
int endpoint, /*nMobile, nMobile1, nMobile2,*/ o1_at, o2_at;
705
U_CHAR path_bonds[PATH_LEN+1], bond_type;
706
T_ENDPOINT EndPointTmp[2];
707
T_BONDPOS BondPosTmp[2*PATH_LEN];
708
ENDPOINT_INFO eif1, eif2;
712
if ( nLenDfsPath + 2 > PATH_LEN ) {
713
return -1; /* too long path */
715
if ( nLenDfsPath != 6 && nLenDfsPath != 4 ) {
716
return -1; /* wrong call */
720
nNumBondPos = *pnNumBondPos;
721
nNumEndPoint = *pnNumEndPoint;
726
o1_at = atom[(int)DfsPath[1].at_no].neighbor[nStartAtomNeighborNeighbor];
727
o2_at = atom[(int)DfsPath[0].at_no].neighbor[nStartAtomNeighbor2];
729
nMobile1 = (atom[o1_at].charge == -1) + atom[o1_at].num_H;
730
nMobile2 = (atom[o2_at].charge == -1) + atom[o2_at].num_H;
732
if ( !nGetEndpointInfo( atom, o1_at, &eif1 ) ||
733
!nGetEndpointInfo( atom, o2_at, &eif2 ) ) {
738
for ( j = 0; j < 2; j ++ ) {
739
endpoint = j? o2_at : o1_at;
740
if ( !atom[endpoint].endpoint ) {
741
AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
742
AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
744
nMobile = j? nMobile2 : nMobile1;
749
EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1);
750
EndPointTmp[nNumEndPointTmp].num[0] = nMobile;
751
for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) {
752
EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1];
756
memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) );
758
EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
759
EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
760
EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
766
k = (int)DfsPath[1].at_no;
767
bond_type = (atom[k].bond_type[nStartAtomNeighborNeighbor] & ~BOND_MARK_ALL);
768
#if( FIX_BOND23_IN_TAUT == 1 )
769
bond_type = ACTUAL_ORDER(pBNS,k,nStartAtomNeighborNeighbor,bond_type);
771
path_bonds[0] = bond_type;
772
if ( REPLACE_THE_BOND( bond_type ) ) {
773
BondPosTmp[nNumBondPosTmp].nAtomNumber = k;
774
BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighborNeighbor;
777
for ( i = 1; i <= nLenDfsPath; i ++ ) {
778
bond_type = DfsPath[i].bond_type;
779
path_bonds[i] = bond_type;
780
if ( REPLACE_THE_BOND( bond_type ) ) {
781
BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no;
782
BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos;
786
bond_type = (atom[(int)DfsPath[0].at_no].bond_type[nStartAtomNeighbor2] & ~BOND_MARK_ALL);
787
#if( FIX_BOND23_IN_TAUT == 1 )
788
bond_type = ACTUAL_ORDER(pBNS,(int)DfsPath[0].at_no,nStartAtomNeighbor2,bond_type);
790
path_bonds[i++] = bond_type;
791
if ( REPLACE_THE_BOND( bond_type ) ) {
792
BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[0].at_no;
793
BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighbor2;
797
if ( !are_alt_bonds( path_bonds, i ) ) {
801
/* path_bonds is from at_n1 to at_n2 */
802
if ( !(j=are_alt_bonds( path_bonds, i )) ) {
805
/* 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 */
807
/* single bond at o2_at: it should have a mobile atom, o1_at should not */
808
if ( j == BOND_SINGLE && (!atom[o2_at].endpoint && !eif2.cDonor || !atom[o1_at].endpoint && !eif1.cAcceptor) ||
809
/* double bond at o2_at: it should not have a mobile atom, o1_at should */
810
j == BOND_DOUBLE && (!atom[o2_at].endpoint && !eif2.cAcceptor || !atom[o1_at].endpoint && !eif1.cDonor) ) {
811
return 0; /* bond pattern does not fit */
815
nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
816
nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint);
818
if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
819
if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
820
*pnNumBondPos = nNumBondPos ;
821
*pnNumEndPoint = nNumEndPoint ;
826
/* finally check whether the bonds allow moving the hydrogens */
827
if ( (atom[o1_at].endpoint != atom[o2_at].endpoint || !atom[o1_at].endpoint) ) {
828
nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, o1_at, o2_at, ALT_PATH_MODE_TAUTOM );
840
/********************************************************************************/
842
1,5 Tautomerism in 6-member alt ring:
844
/=\ /==\ N = DfsPath[0].at_no
845
HN C=O <-> N C-OH C = DfsPath[3].at_no
849
/********************************************************************************/
850
/* check if a tautomeric 6-member ring has been found */
851
int Check6MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor,
852
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
853
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
854
T_BONDPOS *BondPos, int nMaxNumBondPos,
855
int *pnNumEndPoint, int *pnNumBondPos,
856
struct BalancedNetworkStructure *pBNS,
857
struct BalancedNetworkData *pBD, int num_atoms )
860
int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint;
861
int nNumEndPointTmp, nNumBondPosTmp, o_at, ret;
862
/* int num_taut_endpoints, num_H; */
864
int nMobile, endpoint, endpoint_valence, chem_bonds_valence;
865
int nMobile1, endpoint_valence1; /* o_at */
866
int nMobile2, endpoint_valence2; /* n_at */
869
U_CHAR path_bonds[2][PATH_LEN+1], bond_type;
870
T_ENDPOINT EndPointTmp[2];
871
T_BONDPOS BondPosTmp[4*PATH_LEN];
872
ENDPOINT_INFO eif1, eif2;
874
if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 )
875
return -1; /* wrong call */
877
if ( nLenDfsPath != 5 )
878
return -1; /* wrong call */
880
nNumBondPos = *pnNumBondPos;
881
nNumEndPoint = *pnNumEndPoint;
886
n_at = (int)DfsPath[0].at_no; /* -N= or -NH- atom */
887
nxt_at = DfsPath[middle_pos = (nLenDfsPath+1)/2].at_no; /* must have tautomeric neighbor -OH or =O or -NH2 or =NH */
889
if ( atom[nxt_at].valence != 3
890
#if( TAUT_RINGS_ATTACH_CHAIN == 1 )
891
|| !atom[nxt_at].bCutVertex
897
for ( i = 0; i < atom[nxt_at].valence; i ++ ) {
898
o_at = atom[nxt_at].neighbor[i];
899
if ( o_at != DfsPath[middle_pos-1].at_no && o_at != DfsPath[middle_pos+1].at_no ) {
900
break; /* >=O or />-OH has been found */
903
if ( i == atom[nxt_at].valence ) {
904
return 0; /* no neighboring atom >=O or />-OH */
906
bond_type = (atom[nxt_at].bond_type[i] & ~BOND_MARK_ALL);
907
#if( FIX_BOND23_IN_TAUT == 1 )
908
bond_type = ACTUAL_ORDER(pBNS,nxt_at,i,bond_type);
910
if ( bond_type != BOND_SINGLE &&
911
bond_type != BOND_DOUBLE &&
912
bond_type != BOND_TAUTOM &&
913
bond_type != BOND_ALT12NS &&
914
bond_type != BOND_ALTERN ) {
918
/* check whether the two atoms already belong to one tautomeric group */
919
#if( TAUT_IGNORE_EQL_ENDPOINTS == 1 )
920
if ( atom[n_at].endpoint && atom[n_at].endpoint == atom[o_at].endpoint ) {
924
/* check =O valence; must be 2 for O, S, Se or 3 for N */
925
if ( !(endpoint_valence1=nGetEndpointInfo( atom, o_at, &eif1 )) )
927
return 0; /* n_at has been checked in MarkTautomerGroups(...) */
930
if ( 2 != endpoint_valence1 )
931
return 0; // accept only O, S, Se
933
/* check hydrogens/endpoints */
934
nMobile1 = atom[o_at].num_H + (atom[o_at].charge==-1);
935
if ( bond_type == BOND_SINGLE && !eif1.cDonor && !atom[o_at].endpoint )
937
/* not needed since nGetEndpointInfo returned non-zero
938
if ( nMobile1 + atom[o_at].chem_bonds_valence != endpoint_valence1 )
942
if ( !(endpoint_valence2=nGetEndpointInfo( atom, n_at, &eif2 ) ) ) {
943
return 0; /* should not happen here */
945
nMobile2 = atom[n_at].num_H + (atom[n_at].charge==-1);
949
/* can mobile group move from o_at to n_at? */
950
nMobile += (atom[o_at].endpoint || eif1.cDonor) && /* from o_at */
951
bond_type != BOND_DOUBLE &&
952
( atom[n_at].endpoint || /* to n_at */
953
eif2.cNeutralBondsValence > atom[n_at].valence );
954
/* can mobile group move from n_at to o_at? */
955
nMobile += (atom[n_at].endpoint || eif2.cDonor) && /* from n_at */
956
(atom[o_at].endpoint || /* to o_at */
957
eif1.cNeutralBondsValence > atom[o_at].valence ) &&
958
bond_type != BOND_SINGLE;
964
num_H = atom[n_at].num_H + atom[o_at].num_H;
965
num_taut_endpoints = (0!=atom[n_at].endpoint) + (0!=atom[o_at].endpoint); // if O, N already are endpoints
966
if ( num_H != 1 && num_taut_endpoints != 2 && !(num_H==2 && num_taut_endpoints >= 1) ) {
970
/* extract -OH bond */
973
path_bonds[0][0] = path_bonds[1][0] = bond_type;
974
if ( REPLACE_THE_BOND( bond_type ) ) {
975
BondPosTmp[nNumBondPosTmp].nAtomNumber = nxt_at; /* accumulate bonds to be */
976
BondPosTmp[nNumBondPosTmp].neighbor_index = i; /* marked as tautomeric */
977
nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */
980
/* extract other bonds */
981
/* path_bonds[] contents:
987
|| || <--> | || <--> || |
991
path[0]: O=NH-=- OH-N... OH.N...
992
path[1] O=NH-=- OH-N... OH.N...
993
bonds are all bonds all bonds
994
single and are either are either
995
double alt or taut alt or taut
997
for ( j = 0; j < middle_pos; j ++ ) {
998
for ( i = 0; i < 2; i ++ ) {
999
/* k = i? j : middle_pos-1-j; */
1000
k = i? middle_pos+j : middle_pos-1-j;
1001
/* i=0: from O neighbor i=0: down to N, i=1: up to N */
1002
bond_type = DfsPath[k].bond_type;
1004
path_bonds[i][j+1] = bond_type;
1005
if ( REPLACE_THE_BOND( bond_type ) ) {
1006
BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */
1007
BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */
1008
nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */
1012
if ( !are_alt_bonds( path_bonds[0], middle_pos+1 ) || !are_alt_bonds( path_bonds[1], middle_pos+1 ) ) {
1016
/* finally check whether the bonds allow moving the hydrogens */
1017
if ( (atom[o_at].endpoint != atom[n_at].endpoint || !atom[o_at].endpoint) ) {
1019
nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n_at, o_at, ALT_PATH_MODE_TAUTOM );
1023
/* save endpoints */
1024
for ( j = 0; j < 2; j ++ ) {
1025
endpoint = j? n_at : /* =N- 2 */
1027
if ( !atom[endpoint].endpoint ) { /* not a known endpoint */
1028
endpoint_valence = j? endpoint_valence2 : endpoint_valence1;
1029
chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence;
1030
/* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */
1031
nMobile = j? nMobile2 : nMobile1;
1032
/* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */
1033
/* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/
1034
if ( nMobile + chem_bonds_valence != endpoint_valence )
1035
return 0; /* abnormal endpoint valence; ignore. */
1036
AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
1037
AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
1039
EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1);
1040
EndPointTmp[nNumEndPointTmp].num[0] = nMobile;
1041
for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) {
1042
EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1];
1045
} else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */
1046
memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) );
1048
EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
1049
EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
1050
EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
1054
/* add collected tautomeric bonds and endpoints to the input/output data */
1055
nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
1056
nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint);
1058
if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
1059
if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
1060
*pnNumBondPos = nNumBondPos ;
1061
*pnNumEndPoint = nNumEndPoint ;
1068
#if( TAUT_15_NON_RING == 1 ) /* post v.1 feature */
1069
/********************************************************************************
1070
Check (1,5) taut alt path centerpoint (unfinished) [add path checking]
1071
*********************************************************************************/
1072
int Check15TautPathCenterpoint( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh,
1073
struct BalancedNetworkStructure *pBNS,
1074
struct BalancedNetworkData *pBD, int num_atoms )
1076
int nxt_at = atom[DfsPath[nLenDfsPath].at_no].neighbor[jNxtNeigh];
1077
/* atom[nxt_at].endpoint below allows for keto-enol -CH< or -CH2- endpoints */
1078
return atom[nxt_at].endpoint || bIsCenterPointStrict( atom, nxt_at );
1081
/********************************************************************************/
1083
1,5 Tautomerism in general (unfinished) [just a copy from 6-memb case]
1085
AH--B==C--D==E C may be carbon exhibiting keto-enol tautomerism
1086
0 1 2 3 4 as well as A or E may be previously detected such a carbon
1092
/********************************************************************************/
1093
/* check if 1,5 tautomeric path has been found */
1094
int Check15TautPath( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor,
1095
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
1096
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
1097
T_BONDPOS *BondPos, int nMaxNumBondPos,
1098
int *pnNumEndPoint, int *pnNumBondPos,
1099
struct BalancedNetworkStructure *pBNS,
1100
struct BalancedNetworkData *pBD, int num_atoms )
1103
int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint, cur_at, prv_at, at1, at2 /*, at3, step_at*/;
1104
int nNumEndPointTmp, nNumBondPosTmp, ret;
1105
/* int num_taut_endpoints, num_H; */
1106
int nMobile, endpoint, endpoint_valence, chem_bonds_valence;
1107
int nMobile1, endpoint_valence1; /* start atom, at1 */
1108
int nMobile2, endpoint_valence2; /* end atom, at2 */
1109
/*int nMobile3, endpoint_valence3=-1;*/ /* middle atom, at3 */
1112
U_CHAR /*path_bonds[2][PATH_LEN+1],*/ bond_type;
1113
T_ENDPOINT EndPointTmp[2];
1114
T_BONDPOS BondPosTmp[4*PATH_LEN];
1115
ENDPOINT_INFO eif1, eif2/*, eif3*/;
1117
if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 )
1118
return -1; /* wrong call */
1120
if ( nLenDfsPath != 3 )
1121
return -1; /* wrong call */
1123
nNumBondPos = *pnNumBondPos;
1124
nNumEndPoint = *pnNumEndPoint;
1126
nNumEndPointTmp = 0;
1129
/*-------add the last atom, nLenDfsPath=4 --*/
1131
prv_at = DfsPath[nLenDfsPath].at_no;
1132
cur_at = atom[prv_at].neighbor[j];
1133
DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL);
1134
#if( FIX_BOND23_IN_TAUT == 1 )
1135
DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type);
1137
DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */
1141
DfsPath[nLenDfsPath].at_no = cur_at;
1142
DfsPath[nLenDfsPath].bond_type = 0;
1143
DfsPath[nLenDfsPath].bond_pos = -1;
1144
/*nDfsPathPos[cur_at] = nLenDfsPath+1;*/ /* mark with distance + 1 */
1145
/*------------------------------------------*/
1146
at1 = (int)DfsPath[0].at_no;
1147
at2 = (int)DfsPath[nLenDfsPath].at_no;
1148
/*at3 = (int)DfsPath[2].at_no;*/
1149
if ( atom[at1].endpoint && atom[at1].endpoint == atom[at2].endpoint ) {
1150
/* start & end already belong to the same taut group */
1151
goto exit_function; /* nothing to do */
1154
/* check bond types along alt path */
1155
alt_bonds[0] = alt_bonds[1] = 0;
1156
for( i = 0; i < nLenDfsPath; i ++ ) {
1157
alt_bonds[i%2] |= IS_ALT_OR_DBLBOND(DfsPath[i].bond_type);
1159
if ( (alt_bonds[0] & alt_bonds[1] & (BOND_SINGLE | BOND_DOUBLE)) ||
1160
(alt_bonds[0] & BOND_WRONG) || (alt_bonds[1] & BOND_WRONG ) ) {
1161
goto exit_function; /* incompatible with alt path or wrong bonds */\
1163
/* check possibly tautomeric endpoints at the ends */
1164
endpoint_valence1 = nGetEndpointInfo( atom, at1, &eif1 );
1165
endpoint_valence2 = nGetEndpointInfo( atom, at2, &eif2 );
1166
#ifdef NEVER /* do not use C-endpoint of keto-enol tautomer to find 1,5 the taut path */
1167
if ( !endpoint_valence1 && !atom[at1].endpoint ||
1168
!endpoint_valence2 && !atom[at2].endpoint )
1169
goto exit_function; /* at least one of the end atoms cannot be an endpoint */
1171
if ( !endpoint_valence1 || !endpoint_valence2 )
1172
goto exit_function; /* require both endpoints be heteroatoms */
1173
/* check hydrogens/endpoints */
1174
nMobile1 = atom[at1].num_H + (atom[at1].charge==-1);
1175
if ( !atom[at1].endpoint ) {
1176
if ( (alt_bonds[0] & BOND_SINGLE) && !eif1.cDonor )
1178
if ( (alt_bonds[0] & BOND_DOUBLE) && !eif1.cAcceptor )
1181
nMobile2 = atom[at2].num_H + (atom[at2].charge==-1);
1182
if ( !atom[at2].endpoint ) {
1183
if ( (alt_bonds[1] & BOND_SINGLE) && !eif2.cDonor )
1185
if ( (alt_bonds[1] & BOND_DOUBLE) && !eif2.cAcceptor )
1191
/* can mobile group move from at1=o_at to at2=n_at? */
1192
nMobile += (atom[at1].endpoint || eif1.cDonor) && /* from o_at */
1193
!(alt_bonds[0] & BOND_DOUBLE) &&
1194
( atom[at2].endpoint || /* to n_at */
1195
eif2.cNeutralBondsValence > atom[at2].valence );
1196
/* can mobile group move from at2=n_at to at1=o_at? */
1197
nMobile += (atom[at2].endpoint || eif2.cDonor) && /* from n_at */
1198
!(alt_bonds[1] & BOND_DOUBLE) &&
1199
( atom[at1].endpoint || /* to o_at */
1200
eif1.cNeutralBondsValence > atom[at1].valence );
1206
/* check whether the bonds allow moving the hydrogens between at1 and at2 */
1207
if ( (atom[at1].endpoint != atom[at2].endpoint || !atom[at1].endpoint) ) {
1209
nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, at1, at2, ALT_PATH_MODE_TAUTOM );
1216
/* save tautomeric bonds */
1218
for ( k = 0; k < nLenDfsPath; k ++ ) {
1219
bond_type = DfsPath[k].bond_type;
1220
if ( REPLACE_THE_BOND( bond_type ) ) {
1221
BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */
1222
BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */
1223
nNumBondPosTmp += 2; /* leave room for the same bond in opposite direction */
1226
/* save endpoints */
1227
for ( j = 0; j < 2; j ++ ) {
1228
endpoint = j? at2 : at1;
1229
if ( !atom[endpoint].endpoint ) { /* not a known endpoint */
1230
endpoint_valence = j? endpoint_valence2 : endpoint_valence1;
1231
chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence;
1232
/* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */
1233
nMobile = j? nMobile2 : nMobile1;
1234
/* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */
1235
/* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/
1236
if ( nMobile + chem_bonds_valence != endpoint_valence )
1237
goto exit_function; /* abnormal endpoint valence; ignore. */
1238
AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
1239
AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
1240
} else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */
1241
memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) );
1243
EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
1244
EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
1245
EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
1249
/* add collected tautomeric bonds and endpoints to the input/output data */
1250
nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
1251
nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint);
1253
if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
1254
if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
1255
*pnNumBondPos = nNumBondPos ;
1256
*pnNumEndPoint = nNumEndPoint ;
1261
/*nDfsPathPos[DfsPath[nLenDfsPath].at_no] = 0;*/
1267
#endif /* TAUT_15_NON_RING */
1269
/********************************************************************************/
1272
1,4 tautomerism in 5-member ring
1275
O=N2-C O-N2=C N1 = DfsPath[0].at_no
1276
| \\ | \ N2 = DfsPath[1].at_no
1282
/********************************************************************************/
1283
/* check if a tautomeric 5-member ring (pyrazole derivatives) has been found */
1284
int Check5MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor,
1285
int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
1286
T_ENDPOINT *EndPoint, int nMaxNumEndPoint,
1287
T_BONDPOS *BondPos, int nMaxNumBondPos,
1288
int *pnNumEndPoint, int *pnNumBondPos,
1289
struct BalancedNetworkStructure *pBNS,
1290
struct BalancedNetworkData *pBD, int num_atoms )
1293
int i, j, /*m,*/ nMobile, nMobile1, nMobile2;
1294
int num_taut_endpoints, nNumBondPos, nNumBondPosTmp, nNumEndPoint, nNumEndPointTmp, ret;
1296
int n1_at = (int)DfsPath[0].at_no;
1297
int n2_at = (int)DfsPath[1].at_no;
1298
U_CHAR path_bonds[PATH_LEN+1], bond_type;
1299
T_ENDPOINT EndPointTmp[2];
1300
T_BONDPOS BondPosTmp[2*PATH_LEN];
1301
ENDPOINT_INFO eif1, eif2;
1303
/* the two root atoms (atom[n1_at] and atom[n2_at]) cannot belong */
1304
/* to one and the same tautomeric group: it has been verified in MarkTautomerGroups() */
1306
/* check hydrogens/endpoints */
1307
if ( nLenDfsPath != 4 ) {
1308
return 0; /* program error */
1310
if ( nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 )
1311
return 0; /* program error: wrong call */
1313
nNumBondPos = *pnNumBondPos;
1314
nNumEndPoint = *pnNumEndPoint;
1315
nNumEndPointTmp = 0;
1319
if ( !nGetEndpointInfo( atom, n1_at, &eif1 ) ||
1320
!nGetEndpointInfo( atom, n2_at, &eif2 ) ) {
1324
nMobile1 = atom[n1_at].num_H + (atom[n1_at].charge==-1);
1325
nMobile2 = atom[n2_at].num_H + (atom[n2_at].charge==-1);
1326
nMobile = nMobile1 + nMobile2;
1327
num_taut_endpoints = (0!=atom[n1_at].endpoint) + (0!=atom[n2_at].endpoint); /* if both N atoms already are endpoints */
1329
if ( !(nMobile == 1 || num_taut_endpoints == 2) && !(nMobile>1 && num_taut_endpoints >= 1) ) {
1333
if ( num_taut_endpoints == 0 && nMobile != 1 ) {
1337
/* finally check whether the bonds allow moving the hydrogens */
1338
if ( (atom[n1_at].endpoint != atom[n2_at].endpoint || !atom[n1_at].endpoint) ) {
1340
nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n1_at, n2_at, ALT_PATH_MODE_TAUTOM );
1345
/* save endpoints */
1346
for ( j = 0; j < 2; j ++ ) {
1347
endpoint = j? n1_at : n2_at;
1348
if ( !atom[endpoint].endpoint ) { /* not a known endpoint */
1350
nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H;
1356
AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
1357
AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
1359
EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1);
1360
EndPointTmp[nNumEndPointTmp].num[0] = nMobile;
1361
for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) {
1362
EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1];
1366
memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) );
1368
EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint;
1369
EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
1370
EndPointTmp[nNumEndPointTmp].nEquNumber = 0;
1377
for ( i = 1; i <= nLenDfsPath; i ++ ) {
1378
bond_type = DfsPath[i].bond_type;
1379
path_bonds[i-1] = bond_type;
1380
if ( REPLACE_THE_BOND( bond_type ) ) {
1381
BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no;
1382
BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos;
1383
nNumBondPosTmp += 2;
1386
/* path_bonds is from at_n2 to at_n1 */
1387
if ( !(i=are_alt_bonds( path_bonds, nLenDfsPath )) ) {
1390
/* 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 */
1392
/* single bond at n1_at: it should have a mobile atom, n2_at should not */
1393
if ( i == BOND_SINGLE && (!atom[n1_at].endpoint && !eif1.cDonor || !atom[n2_at].endpoint && !eif2.cAcceptor ) ||
1394
/* double bond at n1_at: it should not have a mobile atom, n2_at should */
1395
i == BOND_DOUBLE && (!atom[n1_at].endpoint && !eif1.cAcceptor || !atom[n2_at].endpoint && !eif2.cDonor) ) {
1396
return 0; /* bond pattern does not fit */
1399
nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
1400
nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint);
1402
if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
1403
if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
1404
*pnNumBondPos = nNumBondPos ;
1405
*pnNumEndPoint = nNumEndPoint ;