~ubuntu-branches/debian/stretch/openbabel/stretch

« back to all changes in this revision

Viewing changes to src/formats/inchi102/ichiqueu.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Leidert (dale)
  • Date: 2009-07-17 00:18:06 UTC
  • mfrom: (6.1.1 sid)
  • Revision ID: james.westby@ubuntu.com-20090717001806-sy3mzs3e1d1adbs9
Tags: 2.2.2-2
* debian/control (Uploaders): Removed LI Daobing. Thanks for your work!
  (Standards-Version): Bumped to 3.8.2.
  (Vcs-Svn): Fixed vcs-field-uses-not-recommended-uri-format.
* debian/patches/537102_fix_tr1_memory_detection.patch: Added.
  - configure.in, configure, src/config.h.in: Fix detection of tr1/memory to
    prevent building the package with boost (closes: #537102).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * International Chemical Identifier (InChI)
 
3
 * Version 1
 
4
 * Software version 1.02
 
5
 * October 31, 2008
 
6
 * Developed at NIST
 
7
 *
 
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 
 
12
 * Foundation:
 
13
 * http://www.opensource.org/licenses/lgpl-license.php
 
14
 */
 
15
 
 
16
 
 
17
#include <stdio.h>
 
18
#include <string.h>
 
19
 
 
20
/*^^^ */
 
21
#include "mode.h"
 
22
 
 
23
#include "inpdef.h"
 
24
#include "extr_ct.h"
 
25
#include "ichitaut.h"
 
26
#include "ichi_bns.h"
 
27
/*^^^ */
 
28
 
 
29
/*******************************************************************/
 
30
 
 
31
#if( FIND_RING_SYSTEMS == 1 ) /* { */
 
32
 
 
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);
 
37
 
 
38
 
 
39
 
 
40
 
 
41
 
 
42
/******************************************
 
43
 *
 
44
 *  Tautomerism in 5- and 6-member rings
 
45
 *
 
46
 ******************************************/
 
47
 
 
48
const int NONE = (AT_RANK)~0;
 
49
 
 
50
 
 
51
/* 
 
52
  1,5 Tautomerism in 6-member alt ring:
 
53
 
 
54
   /=\          /==\
 
55
 HN   C=O  <-> N    C-OH
 
56
   \=/          \\-//
 
57
    
 
58
 
 
59
   1,2 Tautomerism in 5-member ring:
 
60
 
 
61
 
 
62
  HN--X           N==X  
 
63
   |   \\         |   \
 
64
   |    Z  <->    |    Z
 
65
   |   /          |   //
 
66
   N==Y          HN--Y
 
67
   
 
68
 
 
69
  1,4 tautomerism in 7-member ring
 
70
 
 
71
      /C==D             //C-D  
 
72
   O=B     \        HO-B     \\
 
73
     |      E  <->     |      E
 
74
  HO-A     //        O=A     / 
 
75
      \\G-F             \\G-F  
 
76
 
 
77
 
 
78
  1,4 tautomerism in 5-member ring
 
79
 
 
80
             
 
81
   O=B--C            O-B==C   
 
82
     |   \\            |   \  
 
83
     |     D  <->      |     D
 
84
     |   /             |   // 
 
85
  HO-A==E           HO=A--E   
 
86
 
 
87
*/
 
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 );
 
94
 
 
95
typedef int CHECK_CENTERPOINT ( inp_ATOM *atom, int iat );
 
96
 
 
97
CHECK_DFS_RING Check7MembTautRing;
 
98
CHECK_DFS_RING Check6MembTautRing;
 
99
CHECK_DFS_RING Check5MembTautRing;
 
100
 
 
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 );
 
109
 
 
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 );
 
113
 
 
114
 
 
115
CHECK_DFS_PATH        Check15TautPath;
 
116
CHECK_DFS_CENTERPOINT Check15TautPathCenterpoint;
 
117
 
 
118
int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
 
119
                      int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
 
120
                      int nCycleLen,
 
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 );
 
128
 
 
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 : \
 
132
                              BOND_WRONG);
 
133
 
 
134
#endif /* TAUT_15_NON_RING */
 
135
 
 
136
int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
 
137
                      int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
 
138
                      int nCycleLen,
 
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 );
 
145
 
 
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 )
 
148
#else
 
149
#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE )
 
150
#endif
 
151
 
 
152
 
 
153
int bIsCenterPointStrict( inp_ATOM *atom, int iat )
 
154
{
 
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 */
 
162
        }
 
163
        return 0;
 
164
    }
 
165
    if (atom[iat].valence+1 == atom[iat].chem_bonds_valence &&
 
166
        is_centerpoint_elem_strict( atom[iat].el_number ) ) {
 
167
                return 1;
 
168
    }
 
169
    return 0;
 
170
}
 
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 )
 
180
{   
 
181
    int nRet;
 
182
 
 
183
    *pnNumEndPoint = 0;
 
184
    *pnNumBondPos  = 0;
 
185
 
 
186
    if ( nMaxLenDfsPath <= 7 ) {
 
187
        return -1; /*  path is too short */
 
188
    }
 
189
 
 
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, 
 
197
                                 pBNS, pBD, num_atoms
 
198
                      );
 
199
 
 
200
 
 
201
    return nRet;
 
202
}
 
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 )
 
212
{   
 
213
    int nRet;
 
214
 
 
215
    *pnNumEndPoint = 0;
 
216
    *pnNumBondPos  = 0;
 
217
 
 
218
    if ( nMaxLenDfsPath <= 5 ) {
 
219
        return -1; /*  path is too short */
 
220
    }
 
221
 
 
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, 
 
229
                                 pBNS, pBD, num_atoms
 
230
                      );
 
231
 
 
232
 
 
233
    return nRet;
 
234
}
 
235
 
 
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 )
 
244
{
 
245
    int nRet;
 
246
 
 
247
    *pnNumEndPoint = 0;
 
248
    *pnNumBondPos  = 0;
 
249
 
 
250
    if ( nMaxLenDfsPath <= 5 ) {
 
251
        return -1; /*  path is too short */
 
252
    }
 
253
 
 
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, 
 
260
                                 pBNS, pBD, num_atoms
 
261
                      );
 
262
    return nRet;
 
263
}
 
264
 
 
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 )
 
273
{
 
274
    int nRet;
 
275
 
 
276
    *pnNumEndPoint = 0;
 
277
    *pnNumBondPos  = 0;
 
278
 
 
279
    if ( nMaxLenDfsPath <= 7 ) {
 
280
        return -1; /*  path is too short */
 
281
    }
 
282
 
 
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,
 
290
                                 pBNS, pBD, num_atoms
 
291
                      );
 
292
    return nRet;
 
293
}
 
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 )
 
303
{
 
304
    int nRet;
 
305
 
 
306
    *pnNumEndPoint = 0;
 
307
    *pnNumBondPos  = 0;
 
308
 
 
309
    if ( nMaxLenDfsPath <= 7 ) {
 
310
        return -1; /*  path is too short */
 
311
    }
 
312
 
 
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,
 
320
                                 pBNS, pBD, num_atoms
 
321
                      );
 
322
    return nRet;
 
323
}
 
324
#endif
 
325
/********************************************************************************/
 
326
/*  DFS version */
 
327
#define MAX_DFS_DEPTH 16
 
328
 
 
329
/********************************************************************************/
 
330
int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor,
 
331
                      int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor,
 
332
                      int nCycleLen,
 
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 )
 
340
{
 
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;
 
346
    AT_RANK      nRingSystem;
 
347
    int          nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1;
 
348
 
 
349
    nLenDfsPath=0;
 
350
    nNumFound=0;
 
351
 
 
352
    nCycleLen --;
 
353
    
 
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;
 
359
    nMinLenDfsPath                 = 0;
 
360
    if ( nStartAtomNeighbor2 >= 0 ) {
 
361
        nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2];
 
362
    }
 
363
 
 
364
    
 
365
    /*  add the first neighbor to the 2nd tree position if required */
 
366
    if ( nStartAtomNeighbor >= 0 ) {
 
367
        j = nStartAtomNeighbor;
 
368
        prv_at = cur_at;
 
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);
 
373
#endif
 
374
        DfsPath[nLenDfsPath].bond_pos      = j;
 
375
 
 
376
        nLenDfsPath ++;
 
377
 
 
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;
 
382
        nMinLenDfsPath ++;
 
383
        if ( nStartAtomNeighborNeighbor >= 0 ) {
 
384
            nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor];
 
385
        }
 
386
    }
 
387
 
 
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);
 
396
#endif
 
397
            nxt_at = (int)atom[cur_at].neighbor[j];
 
398
            if ( nxt_at == nDoNotTouchAtom1 ||
 
399
                 nxt_at == nDoNotTouchAtom2  ) {
 
400
                ; /*  ignore */
 
401
            } else
 
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, 
 
410
                                           pBNS, pBD, num_atoms
 
411
                      );
 
412
                    if ( ret < 0 ) {
 
413
                        nNumFound = ret;
 
414
                        goto clear_path;
 
415
                    }
 
416
                    nNumFound += ret;
 
417
 
 
418
                }
 
419
            } else
 
420
            if ( !(*CheckCenterPoint)( atom, nxt_at ) ) {
 
421
                ; /*  cannot advance to a non-centerpoint; ignore */
 
422
            } else
 
423
            if ( nLenDfsPath < nCycleLen ) {
 
424
                /*  advance */
 
425
                nLenDfsPath ++;
 
426
                cur_at = nxt_at;
 
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 */
 
431
            }
 
432
        } else {
 
433
            /*  retract */
 
434
            nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
 
435
            nLenDfsPath --;
 
436
        }
 
437
    }
 
438
clear_path:
 
439
    while ( 0 <= nLenDfsPath ) {
 
440
        nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
 
441
        nLenDfsPath --;
 
442
    }
 
443
    return nNumFound;
 
444
}
 
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,
 
449
                      int nCycleLen,
 
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 )
 
457
{
 
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;
 
463
    AT_RANK      nRingSystem;
 
464
    int          nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1;
 
465
 
 
466
    nLenDfsPath=0;
 
467
    nNumFound=0;
 
468
 
 
469
    nCycleLen --; /* indef of the last atom in the alt path, statring from 0 */
 
470
    
 
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];
 
479
    }
 
480
 
 
481
    
 
482
    /*  add the first neighbor to the 2nd tree position if required */
 
483
    if ( nStartAtomNeighbor >= 0 ) {
 
484
        j = nStartAtomNeighbor;
 
485
        prv_at = cur_at;
 
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);
 
490
#endif
 
491
        DfsPath[nLenDfsPath].bond_pos      = j; /* fix index of the bond to the next atom */
 
492
 
 
493
        nLenDfsPath ++;
 
494
 
 
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];
 
502
        }
 
503
    }
 
504
 
 
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);
 
513
#endif
 
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 */
 
519
                 ) {
 
520
                ; /* ignore nxt_at */
 
521
            } else
 
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 ) ) ) {
 
532
                if ( ret < 0 ) {
 
533
                    nNumFound = ret;
 
534
                    goto clear_path; /* program error */
 
535
                }
 
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 */
 
541
            } else
 
542
            if ( nLenDfsPath < nCycleLen ) {
 
543
                /*  advance */
 
544
                nLenDfsPath ++;
 
545
                cur_at = nxt_at;
 
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 */
 
550
            }
 
551
        } else {
 
552
            /*  retract */
 
553
            nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
 
554
            nLenDfsPath --;
 
555
        }
 
556
    }
 
557
clear_path:
 
558
    while ( 0 <= nLenDfsPath ) {
 
559
        nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0;
 
560
        nLenDfsPath --;
 
561
    }
 
562
    return nNumFound;
 
563
}
 
564
#endif /* TAUT_15_NON_RING */     
 
565
/*******************************************
 
566
 *      check if bonds are alternating     */
 
567
int are_alt_bonds( U_CHAR *bonds, int len )
 
568
{
 
569
    U_CHAR next_bond;
 
570
    int           i, bAnyBond, bTautBondPresent=BOND_ALTERN;
 
571
    if ( len < 2 || bonds[0] == BOND_TRIPLE || bonds[0] == BOND_ALT_13 ) {
 
572
        return 0;
 
573
    }
 
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;
 
577
        next_bond = 0;
 
578
    } else {
 
579
        next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0;
 
580
    }
 
581
 
 
582
    for ( i = 1; i < len; i ++ ) {
 
583
        if ( bonds[i] == BOND_TAUTOM ) {
 
584
            bTautBondPresent = BOND_TAUTOM;
 
585
            bAnyBond         = 1;
 
586
        } else {
 
587
            bAnyBond = (bonds[i] == BOND_ALTERN || bonds[i] == BOND_ALT12NS);
 
588
        }
 
589
        if ( next_bond ) {
 
590
            if ( bonds[i] == next_bond || bAnyBond ) {
 
591
                next_bond = (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE;
 
592
                continue;
 
593
            }
 
594
            return 0;
 
595
        } else
 
596
        if ( bonds[i] == BOND_SINGLE ) {
 
597
            next_bond = BOND_DOUBLE;
 
598
            continue;
 
599
        } else
 
600
        if ( bonds[i] == BOND_DOUBLE ) {
 
601
            next_bond = BOND_SINGLE;
 
602
            continue;
 
603
        } else
 
604
        if ( !bAnyBond ) {
 
605
            return 0;
 
606
        }
 
607
    }
 
608
    return !next_bond? bTautBondPresent :
 
609
           (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; /* bond to the end atom */
 
610
}
 
611
 
 
612
/********************************************************************************/
 
613
int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos,
 
614
                 int nMaxNumBondPos, int nNumBondPos )
 
615
{
 
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;
 
625
                break;
 
626
            }
 
627
        }
 
628
    }
 
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 */
 
637
            }
 
638
        }
 
639
        if ( i == nNumBondPos ) {
 
640
            if ( i > nMaxNumBondPos ) {
 
641
                return -1; /*  overflow */
 
642
            }
 
643
            BondPos[nNumBondPos ++]    = BondPosTmp[j];
 
644
        }
 
645
    }
 
646
    return nNumBondPos;
 
647
}
 
648
/********************************************************************************/
 
649
int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint,
 
650
                  int nMaxNumEndPoint, int nNumEndPoint)
 
651
{
 
652
    int i, j;
 
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 ) {
 
657
                break;
 
658
            }
 
659
        }
 
660
        if ( i == nNumEndPoint ) {
 
661
            if ( i > nMaxNumEndPoint ) {
 
662
                return -1; /*  overflow */
 
663
            }
 
664
            EndPoint[nNumEndPoint ++] = EndPointTmp[j];
 
665
        }
 
666
    }
 
667
    return nNumEndPoint;
 
668
}
 
669
/********************************************************************************/
 
670
/* 
 
671
 
 
672
  1,4 tautomerism in 7-member ring
 
673
 
 
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
 
678
      \\G-F             \\G-F  
 
679
 
 
680
 
 
681
  1,4 tautomerism in 5-member ring
 
682
 
 
683
             
 
684
   O=B--C            O-B==C   
 
685
     |   \\            |   \  
 
686
     |     D  <->      |     D
 
687
     |   /             |   // 
 
688
  HO-A==E           HO=A--E   
 
689
 
 
690
*/
 
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 )
 
699
{
 
700
#define PATH_LEN 8
 
701
 
 
702
    int i, j, k, /*m,*/ nNumEndPoint, nNumEndPointTmp, nNumBondPos, nNumBondPosTmp;
 
703
    int endpoint, /*nMobile, nMobile1, nMobile2,*/ o1_at, o2_at;
 
704
    int ret;
 
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;
 
709
    int nErr=0;
 
710
 
 
711
 
 
712
    if ( nLenDfsPath + 2 > PATH_LEN ) {
 
713
        return -1; /*  too long path */
 
714
    }
 
715
    if ( nLenDfsPath != 6 && nLenDfsPath != 4 ) {
 
716
        return -1; /*  wrong call */
 
717
    }
 
718
 
 
719
 
 
720
    nNumBondPos  = *pnNumBondPos;
 
721
    nNumEndPoint = *pnNumEndPoint;
 
722
    nNumBondPosTmp  = 0;
 
723
    nNumEndPointTmp = 0;
 
724
    ret             = 0;
 
725
 
 
726
    o1_at = atom[(int)DfsPath[1].at_no].neighbor[nStartAtomNeighborNeighbor];
 
727
    o2_at = atom[(int)DfsPath[0].at_no].neighbor[nStartAtomNeighbor2];
 
728
    /*
 
729
    nMobile1 = (atom[o1_at].charge == -1) + atom[o1_at].num_H;
 
730
    nMobile2 = (atom[o2_at].charge == -1) + atom[o2_at].num_H;
 
731
    */
 
732
    if ( !nGetEndpointInfo( atom, o1_at, &eif1 ) ||
 
733
         !nGetEndpointInfo( atom, o2_at, &eif2 ) ) {
 
734
        return 0;
 
735
    }
 
736
 
 
737
    /*  save endpoints */
 
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 );
 
743
     /*
 
744
            nMobile  = j? nMobile2 : nMobile1;
 
745
        } else {
 
746
            nMobile  = 0;
 
747
        }
 
748
        if ( nMobile ) {
 
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];
 
753
            }
 
754
     */
 
755
        } else {
 
756
            memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) );
 
757
        }
 
758
        EndPointTmp[nNumEndPointTmp].nAtomNumber  = endpoint;
 
759
        EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
 
760
        EndPointTmp[nNumEndPointTmp].nEquNumber   = 0;
 
761
        nNumEndPointTmp ++;
 
762
    }
 
763
    
 
764
 
 
765
    /*  extract bonds */
 
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);
 
770
#endif
 
771
    path_bonds[0] = bond_type;
 
772
    if ( REPLACE_THE_BOND( bond_type ) ) {
 
773
        BondPosTmp[nNumBondPosTmp].nAtomNumber = k;
 
774
        BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighborNeighbor;
 
775
        nNumBondPosTmp += 2;
 
776
    }
 
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;
 
783
            nNumBondPosTmp += 2;
 
784
        }
 
785
    }
 
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);
 
789
#endif
 
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;
 
794
        nNumBondPosTmp += 2;
 
795
    }
 
796
 
 
797
    if ( !are_alt_bonds( path_bonds, i ) ) {
 
798
        return 0;
 
799
    }
 
800
 
 
801
    /* path_bonds is from at_n1 to at_n2 */
 
802
    if ( !(j=are_alt_bonds( path_bonds, i )) ) {
 
803
        return 0;
 
804
    }
 
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 */
 
806
 
 
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 */
 
812
    }
 
813
 
 
814
 
 
815
    nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
 
816
    nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint);
 
817
 
 
818
    if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
 
819
        if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
 
820
            *pnNumBondPos  = nNumBondPos  ;
 
821
            *pnNumEndPoint = nNumEndPoint ;
 
822
        }
 
823
    }
 
824
 
 
825
    if ( ret ) {
 
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 );
 
829
            if ( nErr <= 0 )
 
830
                return nErr;
 
831
        }
 
832
    }
 
833
 
 
834
    return ret;
 
835
 
 
836
    
 
837
#undef PATH_LEN
 
838
}
 
839
 
 
840
/********************************************************************************/
 
841
/* 
 
842
  1,5 Tautomerism in 6-member alt ring:
 
843
 
 
844
   /=\          /==\          N = DfsPath[0].at_no
 
845
 HN   C=O  <-> N    C-OH      C = DfsPath[3].at_no
 
846
   \=/          \\-//
 
847
    
 
848
*/
 
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 )
 
858
{
 
859
#define PATH_LEN 4
 
860
    int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint;
 
861
    int nNumEndPointTmp, nNumBondPosTmp, o_at, ret;
 
862
    /* int num_taut_endpoints, num_H; */
 
863
    int middle_pos;
 
864
    int nMobile, endpoint, endpoint_valence, chem_bonds_valence;
 
865
    int nMobile1, endpoint_valence1;  /*  o_at */
 
866
    int nMobile2, endpoint_valence2;  /*  n_at */
 
867
    int nxt_at;
 
868
    int 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;
 
873
 
 
874
    if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 )
 
875
        return -1; /*  wrong call */
 
876
 
 
877
    if ( nLenDfsPath != 5 )
 
878
        return -1; /*  wrong call */
 
879
 
 
880
    nNumBondPos  = *pnNumBondPos;
 
881
    nNumEndPoint = *pnNumEndPoint;
 
882
    nNumBondPosTmp  = 0;
 
883
    nNumEndPointTmp = 0;
 
884
    ret             = 0;
 
885
    
 
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 */
 
888
 
 
889
    if ( atom[nxt_at].valence != 3 
 
890
#if( TAUT_RINGS_ATTACH_CHAIN == 1 )        
 
891
         || !atom[nxt_at].bCutVertex
 
892
#endif        
 
893
       ) {
 
894
        return 0;
 
895
    }
 
896
 
 
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 */
 
901
        }
 
902
    }
 
903
    if ( i == atom[nxt_at].valence ) {
 
904
        return 0; /*  no neighboring atom >=O or />-OH */
 
905
    }
 
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);
 
909
#endif
 
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 ) {
 
915
        return 0;
 
916
    }
 
917
 
 
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 ) {
 
921
        return 0;
 
922
    }
 
923
#endif
 
924
    /*  check =O valence; must be 2 for O, S, Se or 3 for N */
 
925
    if ( !(endpoint_valence1=nGetEndpointInfo( atom, o_at, &eif1 )) )
 
926
    {
 
927
        return 0; /*  n_at has been checked in MarkTautomerGroups(...) */
 
928
    }
 
929
/*
 
930
    if ( 2 != endpoint_valence1 )
 
931
        return 0; // accept only O, S, Se
 
932
*/
 
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 )
 
936
        return 0;
 
937
    /* not needed since nGetEndpointInfo returned non-zero 
 
938
    if ( nMobile1 + atom[o_at].chem_bonds_valence != endpoint_valence1 )
 
939
        return 0;
 
940
    */
 
941
    
 
942
    if ( !(endpoint_valence2=nGetEndpointInfo( atom, n_at, &eif2 ) ) ) {
 
943
        return 0; /* should not happen here */
 
944
    }
 
945
    nMobile2 = atom[n_at].num_H + (atom[n_at].charge==-1);
 
946
 
 
947
    nMobile = 0;
 
948
 
 
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; 
 
959
 
 
960
 
 
961
    if ( !nMobile )
 
962
        return 0;
 
963
    /*
 
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) ) {
 
967
        return 0;
 
968
    }
 
969
    */
 
970
    /*  extract -OH bond */
 
971
    nNumBondPosTmp = 0;
 
972
 
 
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 */
 
978
    }
 
979
 
 
980
    /*  extract other bonds */
 
981
    /* path_bonds[] contents:
 
982
    
 
983
                                                         
 
984
                    O              OH            OH   
 
985
                    ||             |             |    
 
986
                   /  \          //  \          /  \\ 
 
987
                  ||   ||  <-->  |   ||  <-->  ||   | 
 
988
                   \  /          \\  /          \  // 
 
989
                    NH             N             N    
 
990
         
 
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
 
996
    */
 
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;
 
1003
 
 
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 */
 
1009
            }
 
1010
        }
 
1011
    }
 
1012
    if ( !are_alt_bonds( path_bonds[0], middle_pos+1 ) || !are_alt_bonds( path_bonds[1], middle_pos+1 ) ) {
 
1013
        return 0;
 
1014
    }
 
1015
 
 
1016
    /* finally check whether the bonds allow moving the hydrogens */
 
1017
    if ( (atom[o_at].endpoint != atom[n_at].endpoint || !atom[o_at].endpoint) ) {
 
1018
        int nErr;
 
1019
        nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n_at, o_at, ALT_PATH_MODE_TAUTOM );
 
1020
        if ( nErr <= 0 )
 
1021
            return nErr;
 
1022
    }
 
1023
    /*  save endpoints */
 
1024
    for ( j = 0; j < 2; j ++ ) {
 
1025
        endpoint = j? n_at :      /*  =N-  2 */
 
1026
                      o_at;       /*  -OH  1 */
 
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 );
 
1038
/*
 
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];
 
1043
            }
 
1044
*/
 
1045
        } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */
 
1046
            memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) );
 
1047
        }
 
1048
        EndPointTmp[nNumEndPointTmp].nAtomNumber  = endpoint;
 
1049
        EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
 
1050
        EndPointTmp[nNumEndPointTmp].nEquNumber   = 0;
 
1051
 
 
1052
        nNumEndPointTmp ++;
 
1053
    }
 
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);
 
1057
 
 
1058
    if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
 
1059
        if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
 
1060
            *pnNumBondPos  = nNumBondPos  ;
 
1061
            *pnNumEndPoint = nNumEndPoint ;
 
1062
        }
 
1063
    }
 
1064
    return ret;
 
1065
    
 
1066
#undef PATH_LEN
 
1067
}
 
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 )
 
1075
{
 
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 );
 
1079
}
 
1080
 
 
1081
/********************************************************************************/
 
1082
/* 
 
1083
  1,5 Tautomerism in general (unfinished) [just a copy from 6-memb case]
 
1084
 
 
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
 
1087
            ^   nxt_at
 
1088
            |
 
1089
            +-- = nLenDfsPath
 
1090
 
 
1091
*/
 
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 )
 
1101
{
 
1102
#define PATH_LEN 4
 
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 */
 
1110
    /*int nxt_at;*/
 
1111
    int alt_bonds[2];
 
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*/;
 
1116
 
 
1117
    if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 )
 
1118
        return -1; /*  wrong call */
 
1119
 
 
1120
    if ( nLenDfsPath != 3 )
 
1121
        return -1; /*  wrong call */
 
1122
 
 
1123
    nNumBondPos  = *pnNumBondPos;
 
1124
    nNumEndPoint = *pnNumEndPoint;
 
1125
    nNumBondPosTmp  = 0;
 
1126
    nNumEndPointTmp = 0;
 
1127
    ret             = 0;
 
1128
 
 
1129
/*-------add the last atom, nLenDfsPath=4 --*/
 
1130
    j = jNxtNeigh;
 
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);
 
1136
#endif
 
1137
    DfsPath[nLenDfsPath].bond_pos      = j; /* fix index of the bond to the next atom */
 
1138
 
 
1139
    nLenDfsPath ++;
 
1140
 
 
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 */
 
1152
    }
 
1153
 
 
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);
 
1158
    }
 
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 */\
 
1162
    }
 
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 */
 
1170
#endif
 
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 )
 
1177
            goto exit_function;
 
1178
        if ( (alt_bonds[0] & BOND_DOUBLE) && !eif1.cAcceptor )
 
1179
            goto exit_function;
 
1180
    }
 
1181
    nMobile2 = atom[at2].num_H + (atom[at2].charge==-1);
 
1182
    if ( !atom[at2].endpoint ) {
 
1183
        if ( (alt_bonds[1] & BOND_SINGLE) && !eif2.cDonor )
 
1184
            goto exit_function;
 
1185
        if ( (alt_bonds[1] & BOND_DOUBLE) && !eif2.cAcceptor )
 
1186
            goto exit_function;
 
1187
    }
 
1188
 
 
1189
    nMobile = 0;
 
1190
 
 
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 ); 
 
1201
 
 
1202
 
 
1203
    if ( !nMobile )
 
1204
        goto exit_function;
 
1205
 
 
1206
    /* check whether the bonds allow moving the hydrogens between at1 and at2 */
 
1207
    if ( (atom[at1].endpoint != atom[at2].endpoint || !atom[at1].endpoint) ) {
 
1208
        int nErr;
 
1209
        nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, at1, at2, ALT_PATH_MODE_TAUTOM );
 
1210
        if ( nErr <= 0 ) {
 
1211
            ret = nErr;
 
1212
            goto exit_function;
 
1213
        }
 
1214
    }
 
1215
 
 
1216
    /* save tautomeric bonds */
 
1217
    nNumBondPosTmp = 0;
 
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 */
 
1224
        }
 
1225
    }
 
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]) );
 
1242
        }
 
1243
        EndPointTmp[nNumEndPointTmp].nAtomNumber  = endpoint;
 
1244
        EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
 
1245
        EndPointTmp[nNumEndPointTmp].nEquNumber   = 0;
 
1246
 
 
1247
        nNumEndPointTmp ++;
 
1248
    }
 
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);
 
1252
 
 
1253
    if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
 
1254
        if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
 
1255
            *pnNumBondPos  = nNumBondPos  ;
 
1256
            *pnNumEndPoint = nNumEndPoint ;
 
1257
        }
 
1258
    }
 
1259
 
 
1260
exit_function:
 
1261
    /*nDfsPathPos[DfsPath[nLenDfsPath].at_no] = 0;*/
 
1262
 
 
1263
    return ret;
 
1264
    
 
1265
#undef PATH_LEN
 
1266
}
 
1267
#endif  /* TAUT_15_NON_RING */
 
1268
 
 
1269
/********************************************************************************/
 
1270
/* 
 
1271
 
 
1272
  1,4 tautomerism in 5-member ring
 
1273
 
 
1274
             
 
1275
   O=N2-C            O-N2=C          N1 = DfsPath[0].at_no
 
1276
     |   \\            |   \         N2 = DfsPath[1].at_no
 
1277
     |     D  <->      |     D
 
1278
     |   /             |   // 
 
1279
  HO-N1=E           HO=N1-E   
 
1280
 
 
1281
*/
 
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 )
 
1291
{
 
1292
#define PATH_LEN 4
 
1293
    int i, j, /*m,*/ nMobile, nMobile1, nMobile2;
 
1294
    int num_taut_endpoints, nNumBondPos, nNumBondPosTmp, nNumEndPoint, nNumEndPointTmp, ret;
 
1295
    int endpoint;
 
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;
 
1302
 
 
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() */
 
1305
 
 
1306
    /*  check hydrogens/endpoints */
 
1307
    if ( nLenDfsPath != 4 ) {
 
1308
        return 0; /*  program error */
 
1309
    }
 
1310
    if ( nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 )
 
1311
        return 0; /*  program error: wrong call */
 
1312
 
 
1313
    nNumBondPos  = *pnNumBondPos;
 
1314
    nNumEndPoint = *pnNumEndPoint;
 
1315
    nNumEndPointTmp = 0;
 
1316
    nNumBondPosTmp  = 0;
 
1317
    ret             = 0;
 
1318
 
 
1319
    if ( !nGetEndpointInfo( atom, n1_at, &eif1 ) ||
 
1320
         !nGetEndpointInfo( atom, n2_at, &eif2 ) ) {
 
1321
        return 0;
 
1322
    }
 
1323
 
 
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 */
 
1328
    /*
 
1329
    if ( !(nMobile == 1 || num_taut_endpoints == 2) && !(nMobile>1 && num_taut_endpoints >= 1) ) {
 
1330
        return 0;
 
1331
    }
 
1332
    */
 
1333
    if ( num_taut_endpoints == 0 && nMobile != 1 ) {
 
1334
        return 0;
 
1335
    }        
 
1336
 
 
1337
    /* finally check whether the bonds allow moving the hydrogens */
 
1338
    if ( (atom[n1_at].endpoint != atom[n2_at].endpoint || !atom[n1_at].endpoint) ) {
 
1339
        int nErr;
 
1340
        nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n1_at, n2_at, ALT_PATH_MODE_TAUTOM );
 
1341
        if ( nErr <= 0 )
 
1342
            return nErr;
 
1343
    }
 
1344
 
 
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 */
 
1349
/*
 
1350
            nMobile  = (atom[endpoint].charge == -1) + atom[endpoint].num_H;
 
1351
        } else {
 
1352
            nMobile  = 0;
 
1353
        }
 
1354
        if ( nMobile ) {
 
1355
*/
 
1356
            AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */
 
1357
            AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 );
 
1358
            /*
 
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];
 
1363
            }
 
1364
            */
 
1365
        } else {
 
1366
            memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) );
 
1367
        }
 
1368
        EndPointTmp[nNumEndPointTmp].nAtomNumber  = endpoint;
 
1369
        EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint;
 
1370
        EndPointTmp[nNumEndPointTmp].nEquNumber   = 0;
 
1371
 
 
1372
        nNumEndPointTmp ++;
 
1373
    }
 
1374
 
 
1375
    /*  extract bonds */
 
1376
    nNumBondPosTmp = 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;
 
1384
        }
 
1385
    }
 
1386
    /* path_bonds is from at_n2 to at_n1 */
 
1387
    if ( !(i=are_alt_bonds( path_bonds, nLenDfsPath )) ) {
 
1388
        return 0;
 
1389
    }
 
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 */
 
1391
 
 
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 */
 
1397
    }
 
1398
                     
 
1399
    nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos );
 
1400
    nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint);
 
1401
 
 
1402
    if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) {
 
1403
        if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) {
 
1404
            *pnNumBondPos  = nNumBondPos  ;
 
1405
            *pnNumEndPoint = nNumEndPoint ;
 
1406
        }
 
1407
    }
 
1408
    return ret;
 
1409
    
 
1410
#undef PATH_LEN
 
1411
}
 
1412
#endif /* } */