Coverage Report

Created: 2023-03-26 06:36

/src/INCHI-1-SRC/INCHI_BASE/src/ichitaut.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
* International Chemical Identifier (InChI)
3
* Version 1
4
* Software version 1.06
5
* December 15, 2020
6
*
7
* The InChI library and programs are free software developed under the
8
* auspices of the International Union of Pure and Applied Chemistry (IUPAC).
9
* Originally developed at NIST.
10
* Modifications and additions by IUPAC and the InChI Trust.
11
* Some portions of code were developed/changed by external contributors
12
* (either contractor or volunteer) which are listed in the file
13
* 'External-contributors' included in this distribution.
14
*
15
* IUPAC/InChI-Trust Licence No.1.0 for the
16
* International Chemical Identifier (InChI)
17
* Copyright (C) IUPAC and InChI Trust
18
*
19
* This library is free software; you can redistribute it and/or modify it
20
* under the terms of the IUPAC/InChI Trust InChI Licence No.1.0,
21
* or any later version.
22
*
23
* Please note that this library is distributed WITHOUT ANY WARRANTIES
24
* whatsoever, whether expressed or implied.
25
* See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details.
26
*
27
* You should have received a copy of the IUPAC/InChI Trust InChI
28
* Licence No. 1.0 with this library; if not, please e-mail:
29
*
30
* info@inchi-trust.org
31
*
32
*/
33
34
35
#include <stdlib.h>
36
#include <string.h>
37
38
#include "mode.h"
39
40
#include "ichitaut.h"
41
#include "ichicomn.h"
42
#include "ichitime.h"
43
#include "ichicant.h"
44
#include "util.h"
45
46
/* Local prototypes */
47
48
int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos );
49
int CompRankTautomer( const void* a1, const void* a2, void * );
50
int RegisterEndPoints( CANON_GLOBALS *pCG,
51
                       T_GROUP_INFO *t_group_info,
52
                       /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/
53
                       T_ENDPOINT *EndPoint,
54
                       int nNumEndPoints,
55
                       inp_ATOM *at,
56
                       int num_atoms,
57
                       C_GROUP_INFO *cgi,
58
                       struct BalancedNetworkStructure *pBNS );
59
int cmpTGroupNumber( const void *a1, const void *a2 );
60
int comp_candidates( const void *a1, const void *a2 );
61
int MoveEndpoint( inp_ATOM *at,
62
                  S_CANDIDATE *s_candidate,
63
                  AT_NUMB endpoint,
64
                  AT_NUMB *nTGroupNewNumbers,
65
                  AT_NUMB *nTGroupPosition,
66
                  int nNewTGroupOrd,
67
                  T_GROUP_INFO *t_group_info );
68
int FindAccessibleEndPoints( CANON_GLOBALS *pCG,
69
                             T_ENDPOINT *EndPoint,
70
                             int *nNumEndPoints,
71
                             T_BONDPOS *BondPos,
72
                             int *nNumBondPos,
73
                             struct BalancedNetworkStructure *pBNS,
74
                             struct BalancedNetworkData *pBD,
75
                             inp_ATOM *at,
76
                             int num_atoms,
77
                             C_GROUP_INFO *cgi,
78
                             int taut_mode );
79
int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype );
80
int GetNeutralRepsIfNeeded( AT_NUMB *pri,
81
                            AT_NUMB *prj,
82
                            inp_ATOM *at,
83
                            int num_atoms,
84
                            T_ENDPOINT *EndPoint,
85
                            int nNumEndPoints,
86
                            C_GROUP_INFO *cgi );
87
int bCanBeACPoint( inp_ATOM *at,
88
                   S_CHAR cCharge,
89
                   S_CHAR cChangeValence,
90
                   S_CHAR neutral_bonds_valence,
91
                   S_CHAR neutral_valence,
92
                   S_CHAR nEndpointValence, S_CHAR *cChargeSubtype );
93
int CmpCCandidates( const void *a1, const void *a2 );
94
int RegisterCPoints( C_GROUP *c_group,
95
                     int *pnum_c,
96
                     int max_num_c,
97
                     T_GROUP_INFO *t_group_info,
98
                     int point1,
99
                     int point2,
100
                     int ctype,
101
                     inp_ATOM *at,
102
                     int num_atoms );
103
int GetSaltChargeType( inp_ATOM *at,
104
                       int at_no,
105
                       T_GROUP_INFO *t_group_info,
106
                       int *s_subtype );
107
int GetOtherSaltChargeType( inp_ATOM *at,
108
                            int at_no,
109
                            T_GROUP_INFO *t_group_info,
110
                            int *s_subtype,
111
                            int bAccept_O );
112
int MergeSaltTautGroupsBlind( inp_ATOM *at,
113
                              int s_type, int num_atoms,
114
                              S_GROUP_INFO *s_group_info,
115
                              int nNumCandidates,
116
                              T_GROUP_INFO *t_group_info,
117
                              C_GROUP_INFO *c_group_info,
118
                              struct BalancedNetworkStructure *pBNS );
119
int ConnectSaltTGroups2SuperTGroup( inp_ATOM *at,
120
                                    int num_atoms,
121
                                    S_GROUP_INFO *s_group_info,
122
                                    int nNumCandidates,
123
                                    T_GROUP_INFO *t_group_info,
124
                                    C_GROUP_INFO *c_group_info,
125
                                    struct BalancedNetworkStructure *pBNS,
126
                                    int *nNewTGroupNumber, int *vertSuperTGroup );
127
int bDoNotMergeNonTautAtom( inp_ATOM *at, int at_no );
128
int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype );
129
130
/* Bits for GetChargeType */
131
#define C_SUBTYPE_CHARGED     0
132
#define C_SUBTYPE_p_DONOR     1  /* new */
133
#define C_SUBTYPE_p_ACCEPT    2  /* new */
134
0
#define C_SUBTYPE_H_ACCEPT    4
135
0
#define C_SUBTYPE_H_DONOR     8
136
#define C_SUBTYPE_NEUTRAL    16
137
138
/* Internal stack array size */
139
4
#define MAX_STACK_ARRAY_LEN 127
140
#define MAX_TGROUP_ARRAY_LEN 127
141
142
143
/*
144
is_centerpoint... functions
145
*/
146
147
148
/****************************************************************************/
149
int is_centerpoint_elem( U_CHAR el_number )
150
112k
{
151
112k
    static U_CHAR el_numb[12];
152
112k
    static int len;
153
112k
    int len2;
154
112k
    int i;
155
112k
    if (!len)
156
1
    {
157
1
        len2 = 0;
158
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "C" );
159
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "N" );
160
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "P" );
161
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "S" );
162
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "I" );
163
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "As" );
164
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "Sb" );
165
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "Se" );
166
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "Te" );
167
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "Cl" );
168
1
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "Br" );
169
1
        len = len2;
170
1
    }
171
1.34M
    for (i = 0; i < len; i++)
172
1.23M
    {
173
1.23M
        if (el_numb[i] == el_number)
174
1.63k
        {
175
1.63k
            return 1;
176
1.63k
        }
177
1.23M
    }
178
179
111k
    return 0;
180
112k
}
181
182
183
#if ( KETO_ENOL_TAUT == 1 )  /* post v.1 feature */
184
185
186
/****************************************************************************/
187
int is_centerpoint_elem_KET( U_CHAR el_number )
188
0
{
189
0
    static U_CHAR el_numb[1];
190
0
    static int len;
191
0
    int i;
192
0
    if (!el_numb[0] && !len)
193
0
    {
194
0
        el_numb[len++] = (U_CHAR) get_periodic_table_number( "C" );
195
0
    }
196
0
    for (i = 0; i < len; i++)
197
0
    {
198
0
        if (el_numb[i] == el_number)
199
0
        {
200
0
            return 1;
201
0
        }
202
0
    }
203
204
0
    return 0;
205
0
}
206
#endif
207
208
209
/****************************************************************************/
210
int is_centerpoint_elem_strict( U_CHAR el_number )
211
0
{
212
0
    static U_CHAR el_numb[6];
213
0
    static int len;
214
0
    int len2;
215
0
    int i;
216
0
    if (!len)
217
0
    {
218
0
        len2 = 0;
219
0
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "C" );
220
0
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "N" );
221
0
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "P" );
222
0
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "As" );
223
0
        el_numb[len2++] = (U_CHAR) get_periodic_table_number( "Sb" );
224
0
        len = len2;
225
0
    }
226
0
    for (i = 0; i < len; i++)
227
0
    {
228
0
        if (el_numb[i] == el_number)
229
0
        {
230
0
            return 1;
231
0
        }
232
0
    }
233
0
    return 0;
234
0
}
235
236
237
/*
238
AddAtom2...
239
*/
240
241
242
/****************************************************************************/
243
int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract )
244
8
{  /*  bSubtract: 0=> add, 1=>subtract, 2=> fill */
245
8
    inp_ATOM *at = atom + at_no;
246
8
    int       k;
247
8
    int       nMobile = ( at->charge == -1 );
248
8
    if (bSubtract == 1)
249
0
    {
250
        /* 1: subtract */
251
0
        num[1] -= nMobile;
252
0
        nMobile += at->num_H;
253
0
        num[0] -= nMobile;
254
0
        for (k = 0; k < T_NUM_ISOTOPIC; k++)
255
0
        {
256
            /*  T (3H isotope) first because it has higher weight */
257
0
            num[T_NUM_NO_ISOTOPIC + k] -= at->num_iso_H[NUM_H_ISOTOPES - k - 1];
258
0
        }
259
0
    }
260
8
    else
261
8
    {
262
8
        if (bSubtract == 2)
263
8
        {
264
            /* fill */
265
8
            memset( num, 0, ( T_NUM_NO_ISOTOPIC + T_NUM_ISOTOPIC ) * sizeof( num[0] ) );
266
8
        }
267
        /* else (0): add */
268
8
        num[1] += nMobile;
269
8
        nMobile += at->num_H;
270
8
        num[0] += nMobile;
271
32
        for (k = 0; k < T_NUM_ISOTOPIC; k++)
272
24
        {
273
            /*  T (3H isotope) first because it has higher weight */
274
24
            num[T_NUM_NO_ISOTOPIC + k] += at->num_iso_H[NUM_H_ISOTOPES - k - 1];
275
24
        }
276
8
    }
277
8
    return nMobile;
278
8
}
279
280
281
/****************************************************************************/
282
void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract )
283
8
{   /*  bSubtract: 0=> add, 1=>subtract, 2=> fill */
284
8
    inp_ATOM *at = atom + at_no;
285
8
    int       nDelta, nAcidic_O;
286
287
8
    if (at->charge < -1 || at->charge == 1 && !at->c_point || at->charge > 1)
288
0
    {
289
0
        return;
290
0
    }
291
292
8
    nDelta = ( bSubtract == 1 ) ? -1 : 1;
293
294
    /* "Acidic" O, S, Se, Te recognition */
295
8
    if (at->at_type & ATT_ACIDIC_CO)
296
0
    {
297
0
        nAcidic_O = nDelta;
298
0
    }
299
8
    else
300
8
    {
301
8
        nAcidic_O = 0;
302
8
    }
303
304
8
    if (bSubtract == 2)
305
8
    {
306
        /* 2: fill, otherwise add */
307
8
        memset( num_DA, 0, TG_NUM_DA * sizeof( num_DA[0] ) );
308
8
    }
309
8
    if (at->charge <= 0 && at->valence == at->chem_bonds_valence ||
310
         /* neutral or negative donor */
311
8
         at->charge > 0 && at->valence + 1 == at->chem_bonds_valence
312
         /* positively charged donor */
313
8
         )
314
8
    {
315
8
        if (at->charge < 0)
316
4
        {
317
4
            num_DA[TG_Num_dM] += nDelta;
318
4
            num_DA[TG_Num_dO] += nAcidic_O;
319
4
        }
320
4
        else
321
4
        {
322
4
            if (at->num_H)
323
4
            {
324
4
                num_DA[TG_Num_dH] += nDelta;
325
4
                num_DA[TG_Num_dO] += nAcidic_O;
326
4
            }
327
4
        }
328
8
    }
329
0
    else
330
0
    {
331
0
        if (at->charge <= 0 && at->valence + 1 == at->chem_bonds_valence ||
332
0
             at->charge  > 0 && at->valence + 2 == at->chem_bonds_valence)
333
0
        {
334
            /* acceptor */
335
0
            if (at->charge < 0)
336
0
            {
337
0
                num_DA[TG_Num_aM] += nDelta;
338
0
            }
339
0
            else
340
0
            {
341
0
                if (at->num_H)
342
0
                {
343
0
                    num_DA[TG_Num_aH] += nDelta;
344
0
                }
345
0
                else
346
0
                {
347
0
                    num_DA[TG_Num_aO] += nAcidic_O; /* acidic O-acceptor has no H or charge */
348
0
                }
349
0
            }
350
0
        }
351
0
    }
352
8
    return;
353
8
}
354
355
356
/*
357
358
*/
359
360
361
/****************************************************************************/
362
int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat )
363
8
{
364
8
    pEndPoint->nAtomNumber = iat;
365
8
    pEndPoint->nEquNumber = 0;
366
8
    pEndPoint->nGroupNumber = at[iat].endpoint;
367
8
    if (at[iat].endpoint)
368
0
    {
369
        /* already an endpoint */
370
0
        memset( pEndPoint->num, 0, sizeof( pEndPoint->num ) );
371
0
    }
372
8
    else
373
8
    {
374
        /* not an endpoint yet, make it an endpoint */
375
8
        AddAtom2num( pEndPoint->num, at, iat, 2 );  /* fill */
376
8
        AddAtom2DA( pEndPoint->num_DA, at, iat, 2 );
377
        /*
378
        nMobile  = pEndPoint->num[1] = (at[iat].charge == -1);
379
        nMobile  = pEndPoint->num[0] = at[iat].num_H + nMobile;
380
        for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) {
381
        pEndPoint->num[T_NUM_NO_ISOTOPIC+k] = at[iat].num_iso_H[NUM_H_ISOTOPES-k-1];
382
        }
383
        */
384
8
    }
385
386
8
    return 0;
387
8
}
388
389
390
/****************************************************************************/
391
int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif )
392
564k
{
393
564k
    int  nEndpointValence;
394
564k
    int  nMobile;
395
564k
    S_CHAR cChargeSubtype;
396
397
564k
    if (atom[iat].radical && atom[iat].radical != RADICAL_SINGLET)
398
30
    {
399
30
        return 0; /* a radical */
400
30
    }
401
564k
    if (!( nEndpointValence = get_endpoint_valence( atom[iat].el_number ) ))
402
560k
    {
403
560k
        return 0; /* not an endpoint */
404
560k
    }
405
3.92k
    if (nEndpointValence <= atom[iat].valence)
406
20
    {
407
20
        return 0; /* not an endpoint, for example >N(+)< or >N<  or >O(+)- or >O- or >N- or -O- */
408
20
    }
409
410
3.90k
    if (atom[iat].charge == -1 || atom[iat].charge == 0)
411
3.90k
    {
412
        /* not a positive charge-point */
413
3.90k
        if (nEndpointValence < atom[iat].chem_bonds_valence)
414
0
        {
415
0
            return 0; /* abnormal valence > standard endpoint valence */
416
0
        }
417
3.90k
        nMobile = atom[iat].num_H + ( atom[iat].charge == -1 );
418
3.90k
        if (nMobile + atom[iat].chem_bonds_valence != nEndpointValence)
419
3.83k
        {
420
3.83k
            return 0; /* non-standard endpoint valence */
421
3.83k
        }
422
68
        switch (atom[iat].chem_bonds_valence - atom[iat].valence)
423
68
        {
424
68
            case 0:
425
68
                eif->cDonor = 1;
426
68
                eif->cAcceptor = 0;
427
68
                break;
428
0
            case 1:
429
0
                eif->cDonor = 0;
430
0
                eif->cAcceptor = 1;
431
0
                break;
432
0
            default:
433
0
                return 0;
434
68
        }
435
68
        eif->cMobile = nMobile;
436
68
        eif->cNeutralBondsValence = nEndpointValence - nMobile;
437
68
        eif->cMoveableCharge = 0;
438
68
#if ( KETO_ENOL_TAUT == 1 )
439
68
        eif->cKetoEnolCode = 0;
440
68
#endif
441
68
        return nEndpointValence;
442
68
    }
443
0
    else
444
0
    {
445
0
        if (atom[iat].c_point &&
446
0
             0 <= GetChargeType( atom, iat, &cChargeSubtype ) &&
447
0
             ( (int) cChargeSubtype & ( C_SUBTYPE_H_ACCEPT | C_SUBTYPE_H_DONOR ) ))
448
0
        {
449
            /* charge-point */
450
0
            {
451
0
                if (cChargeSubtype & C_SUBTYPE_H_ACCEPT)
452
0
                {
453
0
                    eif->cDonor = 0;
454
0
                    eif->cAcceptor = 1;
455
0
                }
456
0
                else
457
0
                {
458
0
                    if (cChargeSubtype & C_SUBTYPE_H_DONOR)
459
0
                    {
460
0
                        eif->cDonor = 1;
461
0
                        eif->cAcceptor = 0;
462
0
                    }
463
0
                    else
464
0
                    {
465
0
                        return 0;
466
0
                    }
467
0
                }
468
0
            }
469
0
            eif->cMobile = atom[iat].num_H;
470
0
            eif->cNeutralBondsValence = nEndpointValence - atom[iat].num_H;
471
0
            eif->cMoveableCharge = atom[iat].charge;
472
0
#if ( KETO_ENOL_TAUT == 1 )
473
0
            eif->cKetoEnolCode = 0;
474
0
#endif
475
0
            return nEndpointValence;
476
0
        }
477
0
    }
478
479
0
    return 0;
480
3.90k
}
481
482
483
/*****************************************************************************/
484
#if ( KETO_ENOL_TAUT == 1 )  /* post v.1 feature */
485
486
487
/****************************************************************************/
488
int nGetEndpointInfo_KET( inp_ATOM *atom,
489
                          int iat,
490
                          ENDPOINT_INFO *eif )
491
0
{
492
0
    int  nEndpointValence;
493
0
    int  nMobile;
494
0
    S_CHAR cChargeSubtype;
495
496
0
    if (atom[iat].radical && atom[iat].radical != RADICAL_SINGLET)
497
0
    {
498
0
        return 0; /* a radical */
499
0
    }
500
0
    if (!( nEndpointValence = get_endpoint_valence_KET( atom[iat].el_number ) ))
501
0
    {
502
0
        return 0; /* not an endpoint; only O and C can be an endpoint for keto-enol tautomerism */
503
0
    }
504
0
    if (nEndpointValence <= atom[iat].valence)
505
0
    {
506
0
        return 0; /* not an endpoint, for example >N(+)< or >N<  or >O(+)- or >O- or >N- or -O- */
507
0
    }
508
0
    if (nEndpointValence == 4 && atom[iat].valence < 2)
509
0
    {
510
0
        return 0; /* exclude O==C--CH3  <=> HO--C==CH2 */
511
0
    }
512
0
    if (nEndpointValence == 2 && atom[iat].valence > 1)
513
0
    {
514
0
        return 0; /* exclude --O--C==CH-- */
515
0
    }
516
517
0
    if (atom[iat].charge == -1 || atom[iat].charge == 0)
518
0
    {
519
        /* not a positive charge-point */
520
0
        if (nEndpointValence < atom[iat].chem_bonds_valence)
521
0
        {
522
0
            return 0; /* abnormal valence > standard endpoint valence */
523
0
        }
524
0
        nMobile = atom[iat].num_H + ( atom[iat].charge == -1 );
525
0
        if (nMobile + atom[iat].chem_bonds_valence != nEndpointValence)
526
0
        {
527
0
            return 0; /* non-standard endpoint valence */
528
0
        }
529
530
0
        switch (atom[iat].chem_bonds_valence - atom[iat].valence)
531
0
        {
532
0
            case 0:
533
0
                eif->cDonor = 1;
534
0
                eif->cAcceptor = 0;
535
0
                break;
536
0
            case 1:
537
0
                eif->cDonor = 0;
538
0
                eif->cAcceptor = 1;
539
0
                break;
540
0
            default:
541
0
                return 0;
542
0
        }
543
0
        eif->cMobile = nMobile;
544
0
        eif->cNeutralBondsValence = nEndpointValence - nMobile;
545
0
        eif->cMoveableCharge = 0;
546
0
        eif->cKetoEnolCode = ( nEndpointValence == 2 ) ? 1 : ( nEndpointValence == 4 ) ? 2 : 0;
547
0
        return nEndpointValence;
548
0
    }
549
0
    else
550
0
    {
551
0
        if (atom[iat].c_point &&
552
0
             0 <= GetChargeType( atom, iat, &cChargeSubtype ) &&
553
0
             ( (int) cChargeSubtype & ( C_SUBTYPE_H_ACCEPT | C_SUBTYPE_H_DONOR ) ))
554
0
        {
555
            /* charge-point; currently only O for keto-enol tautomerism */
556
0
            if (cChargeSubtype & C_SUBTYPE_H_ACCEPT)
557
0
            {
558
0
                eif->cDonor = 0;
559
0
                eif->cAcceptor = 1;
560
0
            }
561
0
            else
562
0
            {
563
0
                if (cChargeSubtype & C_SUBTYPE_H_DONOR)
564
0
                {
565
0
                    eif->cDonor = 1;
566
0
                    eif->cAcceptor = 0;
567
0
                }
568
0
                else
569
0
                {
570
0
                    return 0;
571
0
                }
572
0
            }
573
0
            eif->cMobile = atom[iat].num_H;
574
0
            eif->cNeutralBondsValence = nEndpointValence - atom[iat].num_H;
575
0
            eif->cMoveableCharge = atom[iat].charge;
576
0
            eif->cKetoEnolCode = ( nEndpointValence == 2 ) ? 1 : ( nEndpointValence == 4 ) ? 2 : 0;
577
0
            return nEndpointValence;
578
0
        }
579
0
    }
580
581
0
    return 0;
582
0
}
583
#endif
584
585
586
/****************************************************************************
587
RegisterEndPoints
588
589
Returns >0    => new registration happened,
590
=0    => no changes,
591
-1    => program error (debug)
592
****************************************************************************/
593
int RegisterEndPoints( CANON_GLOBALS *pCG,
594
                       T_GROUP_INFO *t_group_info,
595
                       /*
596
                       T_GROUP *t_group,
597
                       int *pnum_t,
598
                       int max_num_t,
599
                       */
600
                       T_ENDPOINT *EndPoint,
601
                       int nNumEndPoints,
602
                       inp_ATOM *at,
603
                       int num_atoms,
604
                       C_GROUP_INFO *cgi,
605
                       struct BalancedNetworkStructure *pBNS )
606
4
{
607
4
    T_GROUP  *t_group = t_group_info->t_group;
608
4
    int      *pnum_t = &t_group_info->num_t_groups;
609
4
    int       max_num_t = t_group_info->max_num_t_groups;
610
4
    int       nNumZeroEqu, nNumNewTGroups;
611
4
    AT_NUMB   group, prev_group, prev_eqnum, nNextGroupNumber, nLeastGroupNumber;
612
4
    int       nNumGroups, num_t, difference;
613
4
    int       i, j, k, ret;
614
4
    AT_NUMB   nNewTgNumberStackArray[MAX_STACK_ARRAY_LEN + 1];
615
4
    AT_NUMB   nGroupNumberStackArray[MAX_STACK_ARRAY_LEN + 1];
616
4
    AT_NUMB   nGroupNewNumberStackArray[MAX_STACK_ARRAY_LEN + 1];
617
4
    AT_NUMB  *nNewTgNumber = nNewTgNumberStackArray;
618
4
    AT_NUMB  *nGroupNumber = nGroupNumberStackArray;
619
4
    AT_NUMB  *nGroupNewNumber = nGroupNewNumberStackArray;
620
621
4
    if (nNumEndPoints <= 0)
622
0
    {
623
0
        return 0; /* nothing to do */
624
0
    }
625
4
    num_t = *pnum_t;
626
4
    difference = 0;
627
4
    nNextGroupNumber = 0;
628
4
    nNumZeroEqu = 0;
629
4
    ret = 0;
630
    /* find max group number; increment it to obtain next available group number */
631
4
    for (i = 0; i < num_t; i++)
632
0
    {
633
0
        if (nNextGroupNumber < t_group[i].nGroupNumber)
634
0
        {
635
0
            nNextGroupNumber = t_group[i].nGroupNumber;
636
0
        }
637
0
    }
638
4
    nNextGroupNumber++;
639
640
    /* find min non-zero group number nLeastGroupNumber;
641
    count zero EndPoint[i].nEquNumber
642
    if all EndPoint[i].nGroupNumber are equal and non-zero then exit: nothing to do.
643
    */
644
4
    nLeastGroupNumber = nNextGroupNumber;
645
4
    prev_group = EndPoint[0].nGroupNumber;
646
4
    prev_eqnum = EndPoint[0].nEquNumber;
647
12
    for (i = j = k = 0; i < nNumEndPoints; i++)
648
8
    {
649
8
        if (group = EndPoint[i].nGroupNumber)
650
0
        {
651
0
            if (group < nLeastGroupNumber)
652
0
            {
653
0
                nLeastGroupNumber = group;
654
0
            }
655
0
        }
656
8
        j += ( prev_group == EndPoint[i].nGroupNumber ); /* count endpoints that belong to the 1st group */
657
8
        k += ( prev_eqnum == EndPoint[i].nEquNumber );   /* count endpoints that belongo to a group equivalent to the 1st group */
658
8
        nNumZeroEqu += !EndPoint[i].nEquNumber;        /* count endpoints that have been processed by FindAccessibleEndPoints() */
659
8
    }
660
4
    if (j == nNumEndPoints && prev_group && k == nNumEndPoints)
661
0
    {
662
        /* all endpoints already belong to one t-group;
663
        the last comparison is not needed for now
664
        because EndPoint[i].nEquNumber cannot make
665
        endpont partitioning finer
666
        */
667
0
        return 0;
668
0
    }
669
670
4
    nNumNewTGroups = 0;
671
672
4
    if (!nNumZeroEqu)
673
0
    {
674
        /* EndPoint[] has been processed by FindAccessibleEndPoints;
675
        * equal EndPoint[i].nEquNumber mark endpoints belonging to
676
        * the same t-group
677
        * Since now the next available t-group number, nNextGroupNumber,
678
        * is known,replace fict. IDs assigned by FindAccessibleEndPoints
679
        * with correct new t-group numbers.
680
        */
681
0
        for (i = 0; i < nNumEndPoints; i++)
682
0
        {
683
0
            if (( group = EndPoint[i].nEquNumber ) >= nNextGroupNumber)
684
0
            {
685
                /* replace fict. IDs assigned by FindAccessibleEndPoints() with new t-group numbers */
686
                /* these fict. IDs have values = (num_atoms+1), (num_atoms+2),...; they may be non-contiguous */
687
0
                for (j = 0; j < nNumNewTGroups; j++)
688
0
                {
689
0
                    if (group == nGroupNewNumber[j])
690
0
                    {
691
0
                        break;
692
0
                    }
693
0
                }
694
0
                if (j == nNumNewTGroups)
695
0
                {
696
                    /* found new fict. ID = group */
697
0
                    if (j == MAX_STACK_ARRAY_LEN && nGroupNewNumber == nGroupNewNumberStackArray)
698
0
                    {
699
                        /* stack array overflow; allocate more memory than may be needed */
700
0
                        nGroupNewNumber = (AT_NUMB *) inchi_malloc( nNumEndPoints * sizeof( nGroupNewNumber[0] ) );
701
0
                        if (!nGroupNewNumber)
702
0
                        {
703
0
                            ret = -1;
704
0
                            goto exit_function;
705
0
                        }
706
0
                        memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumNewTGroups * sizeof( nGroupNewNumber[0] ) );
707
0
                    }
708
                    /* save newly found fict. t-group ID to compare to the next values of EndPoint[].nEquNumber  */
709
0
                    nGroupNewNumber[j] = group;
710
0
                    nNumNewTGroups++;
711
0
                }
712
0
                EndPoint[i].nEquNumber = nNextGroupNumber + j;
713
0
            }
714
0
        }   /* after this point the values just stored in nGroupNewNumber[] will not
715
            be used. However, the obtained nNumNewTGroups value will be used */
716
0
    }
717
4
    else
718
4
    {
719
4
        if (nNumZeroEqu == nNumEndPoints)
720
4
        {
721
            /* EndPoint[] has NOT been processed by FindAccessibleEndPoints;
722
            all atoms and t-groups to which endpoints belong should
723
            be merged into a single t-group                              */
724
4
            if (nLeastGroupNumber == nNextGroupNumber)
725
4
            {
726
                /* flag to create a new t-group: none of the found
727
                endpoints belong to an already known t-group             */
728
4
                nNumNewTGroups = 1; /* otherwise 0 */
729
4
            }
730
            /* All EndPoint[*].nEquNumber are zeroes. All endpoints will
731
            * belong to one new or old t-group; its ID is nLeastGroupNumber.
732
            * Set  EndPoint[i].nEquNumber = nLeastGroupNumber;             */
733
12
            for (i = 0; i < nNumEndPoints; i++)
734
8
            {
735
8
                EndPoint[i].nEquNumber = nLeastGroupNumber;
736
8
            }
737
4
        }
738
0
        else
739
0
        {
740
0
            ret = -1; /* program error: only some of EndPoint[i].nEquNumber are zero */ /*   <BRKPT> */
741
0
            goto exit_function;
742
0
        }
743
4
    }
744
745
4
    if (nNumNewTGroups)
746
4
    {
747
        /* create new nNumNewTGroups t-group(s) */
748
4
        if (num_t + nNumNewTGroups > max_num_t)
749
0
        {
750
0
            ret = -1; /* found too many t-groups */ /*   <BRKPT> */
751
0
            goto exit_function;
752
0
        }
753
        /* initialize new t-group(s) */
754
4
        memset( t_group + num_t, 0, nNumNewTGroups * sizeof( t_group[0] ) );
755
8
        for (i = 0; i < nNumNewTGroups; i++)
756
4
        {
757
4
            t_group[num_t + i].nGroupNumber = nNextGroupNumber + i;
758
4
        }
759
4
    }
760
761
    /* At this point:
762
    *   EndPoint[i].nGroupNumber == 0   => the endpoint atom does not belong to a t-group yet
763
    *   EndPoint[i].nGroupNumber  > 0   => current t-group ID of the endpoint atom
764
    *   EndPoint[i].nEquNumber      -->    new ID of a tautomeric group of this endpoint atom
765
    *   EndPoint[i].nAtomNumber     -->    number of the endpoint atom
766
    */
767
768
4
    nNumGroups = 0; /* counts the groups to be renumbered */
769
12
    for (i = j = 0; i < nNumEndPoints; i++)
770
8
    {
771
8
        if (group = EndPoint[i].nGroupNumber)
772
0
        {
773
0
            if (group == EndPoint[i].nEquNumber)
774
0
            {
775
0
                continue; /* ignore: the endpoint belongs to the same t-group as before */
776
0
            }
777
            /* save information for renumbering of the existing t-groups */
778
0
            for (j = 0; j < nNumGroups; j++)
779
0
            {
780
0
                if (group == nGroupNumber[j])
781
0
                {
782
0
                    if (EndPoint[i].nEquNumber != nGroupNewNumber[j])
783
0
                    {
784
0
                        ret = -1; /* program error */ /*   <BRKPT> */
785
0
                        goto exit_function;
786
0
                    }
787
0
                    break;
788
0
                }
789
0
            }
790
0
            if (j == nNumGroups)
791
0
            {
792
                /* discovered a new t-group number; store it together with its nEquNumber */
793
0
                if (j == MAX_STACK_ARRAY_LEN)
794
0
                {
795
0
                    if (nGroupNewNumber == nGroupNewNumberStackArray)
796
0
                    {
797
0
                        nGroupNewNumber = (AT_NUMB *) inchi_malloc( nNumEndPoints * sizeof( nGroupNewNumber[0] ) );
798
0
                        if (!nGroupNewNumber)
799
0
                        {
800
0
                            ret = -1;
801
0
                            goto exit_function;
802
0
                        }
803
0
                        memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumGroups * sizeof( nGroupNewNumber[0] ) );
804
0
                    }
805
0
                    if (nGroupNumber == nGroupNumberStackArray)
806
0
                    {
807
0
                        nGroupNumber = (AT_NUMB *) inchi_malloc( nNumEndPoints * sizeof( nGroupNumber[0] ) );
808
0
                        if (!nGroupNumber)
809
0
                        {
810
0
                            ret = -1;
811
0
                            goto exit_function;
812
0
                        }
813
0
                        memcpy( nGroupNumber, nGroupNumberStackArray, nNumGroups * sizeof( nGroupNumber[0] ) );
814
0
                    }
815
0
                }
816
817
0
                nGroupNumber[j] = group;  /* old t-group ID */
818
0
                nGroupNewNumber[j] = EndPoint[i].nEquNumber; /* new t-group ID */
819
0
                nNumGroups++;
820
0
            }
821
0
        }
822
8
        else
823
8
        {
824
            /* add a new endpoint to the newly created or previously existing t-groups */
825
8
            group = EndPoint[i].nEquNumber;
826
8
            if (group >= nNextGroupNumber)
827
8
            {
828
                /* get index of a new t-group from equ number */
829
8
                j = num_t + group - nNextGroupNumber; /* newly assigned IDs are contiguous */
830
8
            }
831
0
            else
832
0
            {
833
                /* old t-group */
834
0
                if (j >= num_t || group != t_group[j].nGroupNumber)
835
0
                {
836
                    /* search only if j is not a needed group index */
837
0
                    for (j = 0; j < num_t; j++)
838
0
                    {
839
0
                        if (group == t_group[j].nGroupNumber)
840
0
                        {
841
0
                            break;
842
0
                        }
843
0
                    }
844
0
                    if (j == num_t)
845
0
                    {
846
0
                        ret = -1; /* program error: t-group not found */ /*   <BRKPT> */
847
0
                        goto exit_function;
848
0
                    }
849
0
                }
850
0
            }
851
            /* add aton to existing or new t-group */
852
8
            t_group[j].nNumEndpoints++;
853
48
            for (k = 0; k < (int) ( sizeof( t_group->num ) / sizeof( t_group->num[0] ) ); k++)
854
40
            {
855
40
                t_group[j].num[k] += EndPoint[i].num[k];
856
40
            }
857
56
            for (k = 0; k < (int) ( sizeof( t_group->num_DA ) / sizeof( t_group->num_DA[0] ) ); k++)
858
48
            {
859
48
                t_group[j].num_DA[k] += EndPoint[i].num_DA[k];
860
48
            }
861
            /* mark endpoint */
862
8
            at[EndPoint[i].nAtomNumber].endpoint = group;
863
8
            difference++;
864
8
        }
865
8
    }
866
867
4
    difference += nNumGroups;
868
4
    num_t += nNumNewTGroups;
869
4
    if (!difference)
870
0
    {
871
0
        ret = 0; /* nothing to do. Not necessarily a program error: happens if all EndPoint[i].nGroupNumber==EndPoint[i].nEquNumber  */
872
0
        goto exit_function;
873
0
    }
874
875
4
    if (nNumGroups)
876
0
    {
877
        /* prepare for renumbering: find max t-group number */
878
0
        for (i = 0, nNextGroupNumber = 0; i < num_t; i++)
879
0
        {
880
0
            if (nNextGroupNumber < t_group[i].nGroupNumber)
881
0
            {
882
0
                nNextGroupNumber = t_group[i].nGroupNumber;
883
0
            }
884
0
        }
885
0
    }
886
    /* renumber and merge t-groups */
887
4
    for (i = 0; i < nNumGroups; i++)
888
0
    {
889
0
        int i1, i2;
890
0
        AT_NUMB group1 = nGroupNumber[i];
891
0
        AT_NUMB group2 = nGroupNewNumber[i];
892
        /* add group1 to group2, then delete group1. */
893
0
        for (j = 0, i1 = i2 = -1; j < num_t && ( i1 < 0 || i2 < 0 ); j++)
894
0
        {
895
0
            if (i1 < 0 && group1 == t_group[j].nGroupNumber)
896
0
            {
897
0
                i1 = j;
898
0
            }
899
0
            if (i2 < 0 && group2 == t_group[j].nGroupNumber)
900
0
            {
901
0
                i2 = j;
902
0
            }
903
0
        }
904
0
        if (i1 < 0 || i2 < 0)
905
0
        {
906
0
            ret = -1; /* program error */ /*   <BRKPT> */
907
0
            goto exit_function;
908
0
        }
909
        /*  add t_group[i1] to t_group[i2] and remove t_group[i1] */
910
0
        for (k = 0; k < (int) ( sizeof( t_group->num ) / sizeof( t_group->num[0] ) ); k++)
911
0
        {
912
0
            t_group[i2].num[k] += t_group[i1].num[k];
913
0
        }
914
0
        for (k = 0; k < (int) ( sizeof( t_group->num_DA ) / sizeof( t_group->num_DA[0] ) ); k++)
915
0
        {
916
0
            t_group[i2].num_DA[k] += t_group[i1].num_DA[k];
917
0
        }
918
0
        t_group[i2].nNumEndpoints += t_group[i1].nNumEndpoints;
919
0
        num_t--;
920
0
        if (num_t > i1)
921
0
        {
922
0
            memmove( t_group + i1, t_group + i1 + 1, ( num_t - i1 ) * sizeof( t_group[0] ) );
923
0
        }
924
0
    }
925
926
4
    if (nNumGroups)
927
0
    {
928
        /* there are groups to merge */
929
0
        if (nNextGroupNumber >= MAX_STACK_ARRAY_LEN)
930
0
        {
931
0
            nNewTgNumber = (AT_NUMB *) inchi_malloc( ( nNextGroupNumber + 1 ) * sizeof( *nNewTgNumber ) );
932
0
            if (!nNewTgNumber)
933
0
            {
934
0
                ret = -1;
935
0
                goto exit_function; /* error: out of RAM */
936
0
            }
937
0
        }
938
0
        memset( nNewTgNumber, 0, ( nNextGroupNumber + 1 ) * sizeof( *nNewTgNumber ) );
939
0
        for (i = 0; i < num_t; i++)
940
0
        {
941
0
            nNewTgNumber[t_group[i].nGroupNumber] = i + 1; /* new t-group numbers */
942
0
        }
943
0
        for (j = 0; j < nNumGroups; j++)
944
0
        {
945
0
            if (!nNewTgNumber[nGroupNumber[j]] && nNewTgNumber[nGroupNewNumber[j]])
946
0
            {
947
0
                nNewTgNumber[nGroupNumber[j]] = nNewTgNumber[nGroupNewNumber[j]];
948
0
            }
949
0
            else
950
0
            {
951
0
                ret = -1; /* program error: all new numbers must have been marked */
952
0
                goto exit_function;
953
0
            }
954
0
        }
955
        /* renumber t-groups */
956
0
        for (i = 0; i < num_t; i++)
957
0
        {
958
0
            t_group[i].nGroupNumber = nNewTgNumber[t_group[i].nGroupNumber];
959
0
        }
960
#if ( bRELEASE_VERSION != 1 )
961
        /* Check: debug only */
962
        for (i = 1; i < num_t; i++)
963
        {
964
            if (1 != t_group[i].nGroupNumber - t_group[i - 1].nGroupNumber)
965
            {
966
                ret = -1; /* debug */
967
                goto exit_function;
968
            }
969
        }
970
#endif
971
        /* renumber endpoints */
972
0
        for (i = 0; i < num_atoms; i++)
973
0
        {
974
0
            if (group = at[i].endpoint)
975
0
            {
976
0
                if (!( at[i].endpoint = nNewTgNumber[group] ) || nNextGroupNumber <= nNewTgNumber[group])
977
0
                {
978
0
                    ret = -1; /* program error */
979
0
                    goto exit_function;
980
0
                }
981
0
            }
982
0
        }
983
0
    }
984
4
    if (nNewTgNumber != nNewTgNumberStackArray)
985
0
    {
986
0
        inchi_free( nNewTgNumber );
987
0
        nNewTgNumber = nNewTgNumberStackArray;
988
0
    }
989
4
    if (nGroupNumber != nGroupNumberStackArray)
990
0
    {
991
0
        inchi_free( nGroupNumber );
992
0
        nGroupNumber = nGroupNumberStackArray;
993
0
    }
994
4
    if (nGroupNewNumber != nGroupNewNumberStackArray)
995
0
    {
996
0
        inchi_free( nGroupNewNumber );
997
0
        nGroupNewNumber = nGroupNewNumberStackArray;
998
0
    }
999
4
    if (!t_group_info->tGroupNumber)
1000
0
    {
1001
0
        t_group_info->tGroupNumber = (AT_NUMB *) inchi_malloc( 2 * max_num_t * sizeof( t_group_info->tGroupNumber[0] ) );
1002
0
        if (!t_group_info->tGroupNumber)
1003
0
        {
1004
0
            ret = -1;
1005
0
            goto exit_function;
1006
0
        }
1007
0
    }
1008
    /* fill out t-group index 2004-02-27 */
1009
4
    memset( t_group_info->tGroupNumber, 0, 2 * max_num_t * sizeof( t_group_info->tGroupNumber[0] ) );
1010
8
    for (i = 0; i < num_t; i++)
1011
4
    {
1012
4
        if (t_group[i].nNumEndpoints && t_group[i].nGroupNumber)
1013
4
        {
1014
4
            t_group_info->tGroupNumber[t_group[i].nGroupNumber] = i + 1;
1015
4
        }
1016
4
    }
1017
1018
4
    if (pBNS && ( pBNS->tot_st_cap == pBNS->tot_st_flow || ALWAYS_ADD_TG_ON_THE_FLY ))
1019
4
    {
1020
4
        T_GROUP_INFO tgi;
1021
4
        int ret_bns;
1022
4
        memset( &tgi, 0, sizeof( tgi ) );
1023
4
        tgi.num_t_groups = num_t;
1024
4
        tgi.t_group = t_group;
1025
4
#if ( KETO_ENOL_TAUT == 1 )
1026
4
        tgi.bTautFlags |= ( t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ); /* needed in AddTGroups2BnStruct() */
1027
4
#endif
1028
        /* reinitialize BN Structure */
1029
4
        ret_bns = ReInitBnStruct( pBNS, at, num_atoms, 0 );
1030
4
        if (IS_BNS_ERROR( ret_bns ))
1031
0
        {
1032
0
            return ret_bns;
1033
0
        }
1034
4
        if (*pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES)
1035
4
        {
1036
            /* set new charge groups */
1037
4
            ret_bns = AddCGroups2BnStruct( pCG, pBNS, at, num_atoms, cgi );
1038
4
            if (IS_BNS_ERROR( ret_bns ))
1039
0
            {
1040
0
                return ret_bns;
1041
0
            }
1042
4
        }
1043
        /* set new tautomeric groups */
1044
4
        ret_bns = AddTGroups2BnStruct( pCG, pBNS, at, num_atoms, &tgi );
1045
4
        if (IS_BNS_ERROR( ret_bns ))
1046
0
        {
1047
0
            return ret_bns;
1048
0
        }
1049
4
    }
1050
1051
4
    *pnum_t = num_t;
1052
4
    return difference;
1053
1054
0
exit_function:
1055
0
    if (nNewTgNumber != nNewTgNumberStackArray)
1056
0
    {
1057
0
        inchi_free( nNewTgNumber );
1058
0
    }
1059
0
    if (nGroupNumber != nGroupNumberStackArray)
1060
0
    {
1061
0
        inchi_free( nGroupNumber );
1062
0
    }
1063
0
    if (nGroupNewNumber != nGroupNewNumberStackArray)
1064
0
    {
1065
0
        inchi_free( nGroupNewNumber );
1066
0
    }
1067
1068
0
    return ret;
1069
4
}
1070
1071
1072
/****************************************************************************
1073
Change non-alternating and non-tautomeric bonds
1074
(that is, single and double bonds) to tautomeric
1075
****************************************************************************/
1076
int SetTautomericBonds( inp_ATOM *at,
1077
                        int nNumBondPos,
1078
                        T_BONDPOS *BondPos )
1079
0
{
1080
0
    int k, n;
1081
0
    for (k = n = 0; k < nNumBondPos; k++)
1082
0
    {
1083
0
        int neighbor_index = BondPos[k].neighbor_index;
1084
0
        int center = BondPos[k].nAtomNumber;
1085
0
        int bond_mark = at[center].bond_type[neighbor_index];
1086
0
        int bond_type = bond_mark & ~BOND_MARK_ALL;
1087
0
        int neighbor;
1088
0
#if ( REPLACE_ALT_WITH_TAUT == 1 )
1089
0
        if (bond_type != BOND_TAUTOM)
1090
#else
1091
        if (bond_type != BOND_ALTERN && bond_type != BOND_TAUTOM)
1092
#endif
1093
0
        {
1094
0
            int ii;
1095
            /*  change bond type to BOND_TAUTOM presering higher bits marks */
1096
0
            bond_type = ( bond_mark & BOND_MARK_ALL ) | BOND_TAUTOM;
1097
            /*  change center-neighbor bond */
1098
0
            at[center].bond_type[neighbor_index] = bond_type;
1099
0
            neighbor = at[center].neighbor[neighbor_index];
1100
0
            for (ii = 0; ii < at[neighbor].valence; ii++)
1101
0
            {
1102
0
                if (at[neighbor].neighbor[ii] == center)
1103
0
                {
1104
                    /*  neighbor-center bond found */
1105
0
                    at[neighbor].bond_type[ii] = bond_type;
1106
0
                    break;
1107
0
                }
1108
0
            }
1109
0
            n++;
1110
0
        }
1111
0
    }
1112
1113
0
    return n;
1114
0
}
1115
1116
1117
/****************************************************************************/
1118
int GetNeutralRepsIfNeeded( AT_NUMB      *pri,
1119
                            AT_NUMB      *prj,
1120
                            inp_ATOM     *at,
1121
                            int          num_atoms,
1122
                            T_ENDPOINT   *EndPoint,
1123
                            int          nNumEndPoints,
1124
                            C_GROUP_INFO *cgi )
1125
0
{
1126
0
    AT_NUMB ri = *pri;
1127
0
    AT_NUMB rj = *prj;
1128
0
    int     i, k;
1129
0
    AT_NUMB c_point, endpoint, r;
1130
1131
0
    if (( c_point = at[ri].c_point ) &&
1132
0
        ( at[rj].c_point == c_point ) &&
1133
0
         ( at[ri].charge == 1 || at[rj].charge == 1 ) &&
1134
0
         cgi                                        &&
1135
0
         cgi->num_c_groups > 0)
1136
0
    {
1137
        /* at[ri] and at[rj] belong to the same charge group, at least one is charged   */
1138
0
        for (k = 0; k < cgi->num_c_groups; k++) /* MS VC++ 2008 reports unreachable code here ??? */
1139
0
        {
1140
0
            if (cgi->c_group[k].nGroupNumber == c_point)
1141
0
            {
1142
                /* cgi->c_group[k] is found to be this charge group */
1143
0
                if (cgi->c_group[k].num_CPoints - cgi->c_group[k].num[0] < 2)
1144
0
                {
1145
                    /* Only one neutral in the c-group: we will not be able to neutralize both */
1146
                    /* when looking for the alt path to discover the tautomerism.              */
1147
                    /* Therefore we need to find a neutral t-group representative at[rj]       */
1148
0
                    if (endpoint = at[rj].endpoint)
1149
0
                    {
1150
0
                        for (i = 0; i < nNumEndPoints; i++)
1151
0
                        {
1152
0
                            if (( r = EndPoint[i].nAtomNumber ) == *prj)
1153
0
                            {
1154
0
                                continue; /* ignore at[*prj] */
1155
0
                            }
1156
0
                            if (at[r].endpoint != endpoint)
1157
0
                            {
1158
0
                                continue; /* at[r] does not belong to the same t-group as at[*prj]; ignore the atom */
1159
0
                            }
1160
0
                            if (!at[r].c_point)
1161
0
                            {
1162
0
                                rj = r; /* found a neutral t-group representative */
1163
0
                                break;
1164
0
                            }
1165
0
                            if (at[r].c_point != c_point && c_point == at[rj].c_point)
1166
0
                            {
1167
                                /* replace only once because of (c_point == at[rj].c_point) condition  */
1168
0
                                rj = r;
1169
0
                            }
1170
0
                        }
1171
0
                        if (rj == *prj /*&& at[ri].endpoint*/)
1172
0
                        {
1173
                            /* !!! "&& at[ri].endpoint": only between 2 t-groups 2004-02-27;
1174
                            the change disabled due to undiscovered yet possibility of ambiguity*/
1175
                            /* no replacement has been found in EndPoint[]; try all atoms in the t-group */
1176
0
                            for (i = 0; i < num_atoms; i++)
1177
0
                            {
1178
0
                                if (at[i].endpoint != endpoint)
1179
0
                                {
1180
0
                                    continue;
1181
0
                                }
1182
0
                                if (i == (int) *prj)
1183
0
                                {
1184
0
                                    continue;
1185
0
                                }
1186
0
                                if (!at[i].c_point)
1187
0
                                {
1188
0
                                    rj = (AT_NUMB) i; /* found neutral t-group representative */
1189
0
                                    break;
1190
0
                                }
1191
0
                                if (at[i].c_point != c_point && c_point == at[rj].c_point)
1192
0
                                {
1193
                                    /* replace only once */
1194
0
                                    rj = (AT_NUMB) i;
1195
0
                                }
1196
0
                            }
1197
0
                        }
1198
0
                    }
1199
                    /* at[ri] */
1200
0
                    if (endpoint = at[ri].endpoint)
1201
0
                    {
1202
0
                        for (i = 0; i < nNumEndPoints; i++)
1203
0
                        {
1204
0
                            if (( r = EndPoint[i].nAtomNumber ) == *pri)
1205
0
                            {
1206
0
                                continue;
1207
0
                            }
1208
0
                            if (at[r].endpoint != endpoint)
1209
0
                            {
1210
0
                                continue;
1211
0
                            }
1212
0
                            if (!at[r].c_point)
1213
0
                            {
1214
0
                                ri = r; /* found neutral t-group representative */
1215
0
                                break;
1216
0
                            }
1217
0
                            if (at[r].c_point != c_point && c_point == at[ri].c_point &&
1218
0
                                 at[r].c_point != at[rj].c_point)
1219
0
                            {
1220
                                /* replace only once */
1221
0
                                ri = r;
1222
0
                            }
1223
0
                        }
1224
0
                        if (ri == *pri && at[rj].endpoint)
1225
0
                        {
1226
                            /* !!! "&& at[rj].endpoint": only between 2 t-groups 2004-02-27;
1227
                            the change disabled due to undiscovered yet possibility of ambiguity */
1228
0
                            for (i = 0; i < num_atoms; i++)
1229
0
                            {
1230
0
                                if (at[i].endpoint != endpoint)
1231
0
                                {
1232
0
                                    continue;
1233
0
                                }
1234
0
                                if (i == (int) *pri)
1235
0
                                {
1236
0
                                    continue;
1237
0
                                }
1238
0
                                if (!at[i].c_point)
1239
0
                                {
1240
0
                                    ri = (AT_NUMB) i; /* found neutral t-group representative */
1241
0
                                    break;
1242
0
                                }
1243
0
                                if (at[i].c_point != c_point && c_point == at[ri].c_point &&
1244
0
                                     at[i].c_point != at[rj].c_point)
1245
0
                                {
1246
                                    /* replace only once */
1247
0
                                    ri = (AT_NUMB) i;
1248
0
                                }
1249
0
                            }
1250
0
                        }
1251
0
                    }
1252
0
                }
1253
0
            }
1254
0
            break;
1255
0
        }
1256
0
        *prj = rj;
1257
0
        *pri = ri;
1258
0
    }
1259
1260
0
    return 0;
1261
0
}
1262
1263
1264
/****************************************************************************/
1265
int FindAccessibleEndPoints( CANON_GLOBALS *pCG,
1266
                             T_ENDPOINT *EndPoint,
1267
                             int *nNumEndPoints,
1268
                             T_BONDPOS *BondPos,
1269
                             int *nNumBondPos,
1270
                             struct BalancedNetworkStructure *pBNS,
1271
                             struct BalancedNetworkData *pBD,
1272
                             inp_ATOM *at,
1273
                             int num_atoms,
1274
                             C_GROUP_INFO *cgi,
1275
                             int taut_mode )
1276
0
{
1277
0
    AT_NUMB nTGroupRepresenative[MAXVAL], nTGroupEqu[MAXVAL], nTGEndPointNo[MAXVAL], ri, rj;
1278
0
    AT_NUMB nCurTGroupNumber, nMaxTGroupNumber, nNumTgroupNumbers, nMaxEquNumber;
1279
0
    int   i, j, k, nNumDiffTGroupNumbers = 0, nNumFoundEqu, nErr;
1280
1281
0
    if (*nNumEndPoints != *nNumBondPos)
1282
0
        return 0;
1283
    /* collect all group numbers. Fill EndPoint[i].nEquNumber */
1284
0
    for (i = 0; i < *nNumEndPoints; i++)
1285
0
    {
1286
0
        nCurTGroupNumber = EndPoint[i].nEquNumber = EndPoint[i].nGroupNumber; /* initial equivalence */
1287
0
        if (nCurTGroupNumber)
1288
0
        {
1289
            /* found endpoint that already belongs to a t-group */
1290
0
            for (j = 0; j < nNumDiffTGroupNumbers; j++)
1291
0
            {
1292
0
                if (nTGroupEqu[j] == nCurTGroupNumber)
1293
0
                {
1294
0
                    break;
1295
0
                }
1296
0
            }
1297
0
            if (j == nNumDiffTGroupNumbers)
1298
0
            {
1299
0
                nTGroupRepresenative[nNumDiffTGroupNumbers] = EndPoint[i].nAtomNumber;
1300
0
                nTGroupEqu[nNumDiffTGroupNumbers] = EndPoint[i].nGroupNumber;
1301
0
                nTGEndPointNo[nNumDiffTGroupNumbers] = i;
1302
0
                nNumDiffTGroupNumbers++;
1303
0
            }
1304
0
        }
1305
0
    }
1306
1307
    /* check whether each pair belongs to the same t-group and establish the equivalence(s) */
1308
0
    for (i = 0, nNumFoundEqu = 0; i < nNumDiffTGroupNumbers; i++)
1309
0
    {
1310
0
        for (j = i + 1; j < nNumDiffTGroupNumbers; j++)
1311
0
        {
1312
0
            ri = nTGroupRepresenative[i];
1313
0
            rj = nTGroupRepresenative[j];
1314
            /* both at[ri] and at[rj] are known to belong to tautomeric groups */
1315
0
            GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi );
1316
0
            nErr = bExistsAnyAltPath( pCG, pBNS, pBD, at, num_atoms, ri, rj, taut_mode );
1317
0
            if (IS_BNS_ERROR( nErr ))
1318
0
            {
1319
0
                return nErr;
1320
0
            }
1321
0
            if (0 == nErr)
1322
0
            {
1323
0
                continue; /* alt path between at[ri] and at[rj] not found */
1324
0
            }
1325
0
            nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] );
1326
0
            nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] );
1327
0
            for (k = 0; k < nNumDiffTGroupNumbers; k++)
1328
0
            {
1329
0
                if (nTGroupEqu[k] == nMaxTGroupNumber)
1330
0
                {
1331
0
                    nTGroupEqu[k] = nCurTGroupNumber;
1332
0
                    nNumFoundEqu++;
1333
0
                }
1334
0
            }
1335
0
            for (k = 0; k < *nNumEndPoints; k++)
1336
0
            {
1337
0
                if (EndPoint[k].nEquNumber == nMaxTGroupNumber)
1338
0
                {
1339
0
                    EndPoint[k].nEquNumber = nCurTGroupNumber;
1340
0
                }
1341
0
            }
1342
0
        }
1343
0
    }
1344
1345
0
    if (nNumFoundEqu)
1346
0
    {
1347
        /* leave in only non-equivalent representatives */
1348
0
        for (i = 1, k = 0; i < nNumDiffTGroupNumbers; i++)
1349
0
        {
1350
0
            for (j = 0; j < i; j++)
1351
0
            {
1352
0
                if (nTGroupEqu[j] == nTGroupEqu[i])
1353
0
                {
1354
0
                    nTGroupEqu[i] = 0;  /* i > j; mark equivalent for removal*/
1355
0
                    break;
1356
0
                }
1357
0
            }
1358
0
        }
1359
0
        for (i = j = 0; i < nNumDiffTGroupNumbers; i++)
1360
0
        {
1361
0
            if (nTGroupEqu[i])
1362
0
            {
1363
0
                if (i != j)
1364
0
                { /* remove the marked */
1365
0
                    nTGroupEqu[j] = nTGroupEqu[i];
1366
0
                    nTGroupRepresenative[j] = nTGroupRepresenative[i];
1367
0
                    nTGEndPointNo[j] = nTGEndPointNo[i];
1368
0
                }
1369
0
                j++;
1370
0
            }
1371
0
        }
1372
0
        nNumDiffTGroupNumbers = j; /* number of known t-group representatives */
1373
0
    }
1374
1375
    /* collect endpoints that have not been assigned to t-groups */
1376
0
    for (i = 0, j = nNumDiffTGroupNumbers; i < *nNumEndPoints; i++)
1377
0
    {
1378
0
        if (EndPoint[i].nEquNumber)
1379
0
        {
1380
0
            continue;
1381
0
        }
1382
0
        nTGroupEqu[j] = 0;
1383
0
        nTGroupRepresenative[j] = EndPoint[i].nAtomNumber;
1384
0
        nTGEndPointNo[j] = i;
1385
0
        j++;
1386
0
    }
1387
0
    nNumTgroupNumbers = j;
1388
0
    nMaxEquNumber = num_atoms + 1; /* impossible atom or t-group number */
1389
1390
                                   /* check whether each pair belongs to the same group and establish the equivalence(s) */
1391
0
    for (i = 0, nNumFoundEqu = 0; i < nNumTgroupNumbers; i++)
1392
0
    {
1393
0
        for (j = i + 1; j < nNumTgroupNumbers; j++)
1394
0
        {
1395
0
            if (nTGroupEqu[i] != nTGroupEqu[j] && ( i >= nNumDiffTGroupNumbers || j >= nNumDiffTGroupNumbers ) ||
1396
                 /* equivalence of a t-group and a non-t-group atom */
1397
0
                 !nTGroupEqu[i] && !nTGroupEqu[j]
1398
                 /* equivalence of two non-t-group atoms */
1399
0
                 )
1400
0
            {
1401
0
                ri = nTGroupRepresenative[i];
1402
0
                rj = nTGroupRepresenative[j];
1403
                /*------------------------------!!!---------------------------------------------
1404
                Explanation why GetNeutralRepsIfNeeded() may need to be changed 2004-02-27
1405
                The change has been disabled due to undiscovered yet possibility of ambiguity
1406
                to search for neutral only among EndPoint[] in case taut-not_taut pairs
1407
1408
                Counterexample:   O=C-NH(+)=C-NH2
1409
                1   2       3
1410
1411
                Has already been found: 2-3 (+)-charge exchange
1412
                1-2 tautomerism (charge removed to 3)
1413
                Now testing: 2-3 tautomerism. If not commented out,
1414
                GetNeutralRepsIfNeeded() would replace 2-3 test with 1-3 test because:
1415
                o Charge group has only one neutral and both 2 and 3 belong to it,
1416
                therefore we cannot neutralize both; search for neutral representative;
1417
                o Since 1 and 2 belong to the same t-group and 1 is neutral,
1418
                test 1-3 instead of 2-3.
1419
                This breaks our condition:
1420
                Test tautomeric H movement only between neutral atoms.
1421
                -----------------------------------------------------------------------------*/
1422
0
                GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi );
1423
1424
0
                nErr = bExistsAnyAltPath( pCG, pBNS, pBD, at, num_atoms, ri, rj, taut_mode );
1425
0
                if (IS_BNS_ERROR( nErr ))
1426
0
                {
1427
0
                    return nErr;
1428
0
                }
1429
0
                if (nErr <= 0)
1430
0
                {
1431
0
                    continue;
1432
0
                }
1433
0
                if (nTGroupEqu[i] && nTGroupEqu[j])
1434
0
                {
1435
                    /* found equivalence of two t-groups; at least one of them must be a new one */
1436
0
                    nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] );
1437
0
                    nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] );
1438
0
                    for (k = 0; k < nNumTgroupNumbers; k++)
1439
0
                    {
1440
0
                        if (nTGroupEqu[k] == nMaxTGroupNumber)
1441
0
                        {
1442
0
                            nTGroupEqu[k] = nCurTGroupNumber;
1443
0
                            nNumFoundEqu++;
1444
0
                        }
1445
0
                    }
1446
0
                    for (k = 0; k < *nNumEndPoints; k++)
1447
0
                    {
1448
0
                        if (EndPoint[k].nEquNumber == nMaxTGroupNumber)
1449
0
                        {
1450
0
                            EndPoint[k].nEquNumber = nCurTGroupNumber;
1451
0
                        }
1452
0
                    }
1453
0
                }
1454
0
                else
1455
0
                {
1456
0
                    if (nTGroupEqu[i])
1457
0
                    { /* extend existing t-group */
1458
0
                        nTGroupEqu[j] = nTGroupEqu[i];
1459
0
                        EndPoint[nTGEndPointNo[j]].nEquNumber = nTGroupEqu[i];
1460
0
                    }
1461
0
                    else
1462
0
                    {
1463
0
                        if (nTGroupEqu[j])
1464
0
                        { /* extend existing t-group */
1465
0
                            nTGroupEqu[i] = nTGroupEqu[j];
1466
0
                            EndPoint[nTGEndPointNo[i]].nEquNumber = nTGroupEqu[j];
1467
0
                        }
1468
0
                        else
1469
0
                        { /* establis a new t-group */
1470
0
                            nTGroupEqu[i] =
1471
0
                                nTGroupEqu[j] = nMaxEquNumber; /* assign a fict. ID to establish equivalence */
1472
0
                            EndPoint[nTGEndPointNo[i]].nEquNumber =
1473
0
                                EndPoint[nTGEndPointNo[j]].nEquNumber = nMaxEquNumber;
1474
0
                            nMaxEquNumber++;
1475
0
                        }
1476
0
                    }
1477
0
                }
1478
0
            }
1479
0
        }
1480
0
    }
1481
1482
    /* eliminate endpoints and bonds that do not belong to t-group(s)
1483
    (they have not been found connected by an alt path to any other endpoint)
1484
    */
1485
0
    for (i = 0, j = 0; i < *nNumEndPoints; i++)
1486
0
    {
1487
0
        if (EndPoint[i].nEquNumber)
1488
0
        {
1489
0
#if ( IGNORE_SINGLE_ENDPOINTS == 1 )  /* 1-28-2003 */
1490
0
            for (k = 0, nNumFoundEqu = 0; k < *nNumEndPoints; k++)
1491
0
            {
1492
0
                nNumFoundEqu += ( EndPoint[i].nEquNumber == EndPoint[k].nEquNumber );
1493
0
            }
1494
0
            if (nNumFoundEqu <= 1)
1495
0
            {
1496
                /* one time it is equal to itself when i == k above */
1497
                /* if EndPoint[i] is not "equivalent" to any other EndPoint then ignore it */
1498
0
                continue;
1499
0
            }
1500
0
#endif
1501
0
            if (i != j)
1502
0
            {
1503
                /* save endpoints that are found to be connected to other endpoints by alt paths */
1504
0
                EndPoint[j] = EndPoint[i];
1505
0
                BondPos[j] = BondPos[i];
1506
0
            }
1507
0
            j++;
1508
0
        }
1509
0
    }
1510
1511
#if ( IGNORE_SINGLE_ENDPOINTS != 1 )  /* 1-28-2003 */
1512
    /* Do not allow a centerpoint to have only one tautomeric bond */
1513
    /* Hack: we may have only one centerpoint */
1514
    /* BondPos[*].nAtomNumber are centerpoints */
1515
    if (j == 1)
1516
    {
1517
        /* check if there exist other centerpoint neighbors
1518
        * connected to it by another tautomeric-bond
1519
        */
1520
        for (i = 0, k = 0; i < at[BondPos[0].nAtomNumber].valence; i++)
1521
        {
1522
            k += ( i != BondPos[0].neighbor_index &&
1523
                   BOND_TAUTOM == ( at[BondPos[0].nAtomNumber].bond_type[i] & ~BOND_MARK_ALL ) );
1524
        }
1525
        if (!k)
1526
        {
1527
            j = 0;
1528
        }
1529
    }
1530
#endif
1531
1532
0
    *nNumEndPoints = *nNumBondPos = j;
1533
1534
0
    return j;
1535
0
}
1536
1537
1538
/****************************************************************************/
1539
/*#if ( MOVE_CHARGES == 1 ) */  /* { */
1540
                                /****************************************************************************/
1541
1542
                                /**********************************************/
1543
                                /*                                            */
1544
                                /* definitions for positive ion recognition   */
1545
                                /*                                            */
1546
                                /**********************************************/
1547
1548
1549
                                /****************************************************************************/
1550
typedef struct tagChargeType
1551
{
1552
    /* meaning see in bCanBeACPoint() */
1553
    char    elname[3];
1554
    S_CHAR  charge;
1555
    S_CHAR  neutral_valence;
1556
    S_CHAR  neutral_bonds_valence; /* valence of a neutral atom */
1557
    S_CHAR  cChangeValence; /* charge increases valence by this value */
1558
    S_CHAR  cChargeType;    /* different types are treated separately */
1559
    S_CHAR  num_bonds;      /* added 02-06-2005 */
1560
} CHARGE_TYPE;
1561
1562
const CHARGE_TYPE CType[] =
1563
{
1564
    { "N\0",  1, 3, 3, 1, 0, 0 },
1565
    { "P\0",  1, 3, 3, 1, 1, 0 },
1566
#if ( ADD_MOVEABLE_O_PLUS == 1 )
1567
    { "O\0",  1, 2, 2, 1, 2, 2 }, /* added 02-06-2005 */
1568
    { "S\0",  1, 2, 2, 1, 3, 2 }, /* added 03-18-2005 */
1569
    { "Se",   1, 2, 2, 1, 4, 2 }, /* added 03-18-2005 */
1570
    { "Te",   1, 2, 2, 1, 5, 2 }, /* added 03-18-2005 */
1571
#endif
1572
};
1573
1574
/* bits */
1575
1576
0
#define C_SUBTYPE_CHARGED     0
1577
0
#define C_SUBTYPE_p_DONOR     1  /* new */
1578
0
#define C_SUBTYPE_p_ACCEPT    2  /* new */
1579
2
#define C_SUBTYPE_H_ACCEPT    4
1580
0
#define C_SUBTYPE_H_DONOR     8
1581
2
#define C_SUBTYPE_NEUTRAL    16
1582
1583
/* make sure any C_SUBTYPE_CHARGED_... < any C_SUBTYPE_NEUTRAL_... */
1584
/* charged */
1585
0
#define C_SUBTYPE_CHARGED_NON_TAUT          (C_SUBTYPE_CHARGED)
1586
0
#define C_SUBTYPE_CHARGED_p_DONOR           (C_SUBTYPE_CHARGED|C_SUBTYPE_p_DONOR)
1587
0
#define C_SUBTYPE_CHARGED_H_ACCEPT          (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT)
1588
0
#define C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR  (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_DONOR)
1589
0
#define C_SUBTYPE_CHARGED_H_DONOR           (C_SUBTYPE_CHARGED|C_SUBTYPE_H_DONOR |C_SUBTYPE_p_DONOR)
1590
/* neutral */
1591
0
#define C_SUBTYPE_NEUTRAL_NON_TAUT          (C_SUBTYPE_NEUTRAL)
1592
4
#define C_SUBTYPE_NEUTRAL_H_ACCEPT          (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT)
1593
0
#define C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_ACCEPT)
1594
0
#define C_SUBTYPE_NEUTRAL_H_DONOR           (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_DONOR)
1595
1596
25.7k
#define NUM_C_TYPES  (int)(sizeof( CType )/sizeof(CType[0]))
1597
1598
1599
/****************************************************************************/
1600
int bCanBeACPoint( inp_ATOM *at,
1601
                   S_CHAR cCharge,
1602
                   S_CHAR cChangeValence,
1603
                   S_CHAR neutral_bonds_valence,
1604
                   S_CHAR neutral_valence,
1605
                   S_CHAR nEndpointValence,
1606
                   S_CHAR *cChargeSubtype )
1607
480
{
1608
480
    int nChangeValence;
1609
480
    int nNumBonds;
1610
480
    int nBondsValence;
1611
480
    int bNegCharge = ( at->charge == -1 );  /* add fict. bonds to (-) 2004-02-24*/
1612
1613
480
    if (at->charge == cCharge && at->valence == at->chem_bonds_valence && at->num_H)
1614
0
    {
1615
        /* proton donors candidates >NH(+)-, >NH2(+), -NH3(+), >OH(+), -OH2(+) */
1616
        /* charged, added p-transfer -- 01-28-2004 */
1617
0
        nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */
1618
0
        nBondsValence = at->chem_bonds_valence + at->num_H;
1619
0
        if (nBondsValence == neutral_bonds_valence + nChangeValence && nEndpointValence)
1620
0
        {
1621
0
            *cChargeSubtype = C_SUBTYPE_CHARGED_p_DONOR; /* ignore Phosphorus p-donors for now */
1622
0
        }
1623
0
        return 0;
1624
0
    }
1625
1626
480
    else
1627
480
    {
1628
480
        if (at->charge == cCharge && at->valence < at->chem_bonds_valence)
1629
0
        {
1630
            /* the requirement at->valence < at->chem_bonds_valence rejects
1631
            candidates >NH(+)-, >NH2(+), -NH3(+), >N(+)<, >OH(+), -OH2(+), >O(+)-
1632
            Moveable charge requires double bonds; these ions have no double bonds
1633
            */
1634
1635
            /* charged */
1636
0
            nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */
1637
0
            nBondsValence = at->chem_bonds_valence + at->num_H;
1638
0
            nNumBonds = at->valence + at->num_H;
1639
0
            if (nBondsValence == neutral_bonds_valence + nChangeValence)
1640
0
            {
1641
                /* known valence */
1642
0
                if (nNumBonds == neutral_valence)
1643
0
                {
1644
                    /* non-tautomeric: >N(+)=, =O(+)-
1645
                    possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */
1646
0
                    if (at->valence == neutral_valence || !nEndpointValence)
1647
0
                    {
1648
                        /* non-tautomeric: >N(+)=, =O(+)-; any suitable P+: >P(+)=, =PH(+)-, =PH2(+) */
1649
0
                        *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT;
1650
0
                    }
1651
0
                    else
1652
0
                    {
1653
                        /* possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */
1654
0
                        *cChargeSubtype = C_SUBTYPE_CHARGED_H_DONOR;
1655
0
                    }
1656
0
                    return 1;
1657
0
                }
1658
0
                if (nNumBonds == neutral_valence - 1)
1659
0
                {
1660
                    /* possibly tutomeric acceptor: =N(+)=, #N(+)-, #NH(+), #O(+) */
1661
0
                    if (nEndpointValence)
1662
0
                    {
1663
0
                        *cChargeSubtype = at->num_H ? C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR : C_SUBTYPE_CHARGED_H_ACCEPT;
1664
0
                    }
1665
0
                    else
1666
0
                    {
1667
                        /* =P(+)=, #P(+)-, #PH(+) */
1668
0
                        *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT;
1669
0
                    }
1670
0
                    return 1; /* charge type, charged */
1671
0
                }
1672
0
            }
1673
0
        }
1674
480
        else
1675
480
        {
1676
480
            if (at->charge == 0 || bNegCharge)
1677
480
            {
1678
                /* neutral atom or anion, all bonds are single */
1679
480
                nBondsValence = at->chem_bonds_valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/
1680
480
                nNumBonds = at->valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/
1681
480
                if (nBondsValence == neutral_bonds_valence)
1682
2
                {
1683
2
                    if (nNumBonds == neutral_valence)
1684
2
                    {
1685
                        /* only single bonds: >N-, >NH, -NH2, -O-, -OH, >P- >PH -PH2 */
1686
                        /*                    >N(-), -NH(-), -O(-). >P(-) -PH(-) */
1687
2
                        {
1688
2
                            if (at->valence == neutral_valence || !nEndpointValence)
1689
0
                            {
1690
                                /* >N-, -O-, any P(3 single bonds): >P- >PH -PH2  */
1691
0
                                *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT;
1692
0
                            }
1693
2
                            else
1694
2
                            {
1695
2
                                if (at->valence < neutral_valence /*&& nEndpointValence */)
1696
2
                                {
1697
                                    /* num_H > 0: >NH -NH2 -OH */
1698
                                    /* num_H = 0: none C_SUBTYPE_NEUTRAL_H_ACCEPT for now */
1699
2
                                    *cChargeSubtype = at->num_H ? C_SUBTYPE_NEUTRAL_H_DONOR : C_SUBTYPE_NEUTRAL_H_ACCEPT;
1700
2
                                }
1701
0
                                else
1702
0
                                {
1703
0
                                    return 0;
1704
0
                                }
1705
2
                            }
1706
2
                        }
1707
2
                        return 1; /* charge type, neutral */
1708
2
                    }
1709
0
                    if (nNumBonds == neutral_valence - 1)
1710
0
                    {
1711
                        /* possibly tautomeric acceptor =N-, =NH, =O or non-taut =P-, =PH */
1712
0
                        if (nEndpointValence)
1713
0
                        {
1714
                            /* =N-,  =NH, =O  */
1715
0
                            *cChargeSubtype = C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT;
1716
0
                        }
1717
0
                        else
1718
0
                        {
1719
                            /* =P-, =PH */
1720
0
                            *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT;
1721
0
                        }
1722
0
                        return 1; /* charge type, (+) => neutral */
1723
0
                    }
1724
0
                }
1725
480
            }
1726
480
        }
1727
480
    }
1728
1729
478
    return 0;
1730
480
}
1731
1732
1733
/****************************************************************************/
1734
int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype )
1735
3.68k
{
1736
3.68k
    int i, n;
1737
3.68k
    S_CHAR    nEndpointValence;
1738
3.68k
    inp_ATOM *at = atom + iat;
1739
1740
3.68k
    *cChargeSubtype = 0;
1741
    /* ignore ion pairs and charges != 1 */
1742
3.68k
    if (abs( at->charge ) == 1)
1743
22
    {
1744
56
        for (i = 0; i < at->valence; i++)
1745
34
        {
1746
34
            n = at->neighbor[i];
1747
            /* allow negatively charged tautomeric neighbors 2004-02-26 */
1748
34
            if (abs( atom[n].charge + at->charge ) < abs( atom[n].charge - at->charge ) && !atom[n].endpoint)
1749
0
            {
1750
0
                return -1; /* charges have different signs */
1751
0
            }
1752
34
        }
1753
22
    }
1754
3.66k
    else
1755
3.66k
    {
1756
3.66k
        if (at->charge)
1757
8
        {
1758
8
            return -1; /* abs(charge) != 1 */
1759
8
        }
1760
3.66k
    }
1761
1762
    /* find candidates */
1763
25.7k
    for (i = 0; i < NUM_C_TYPES; i++)
1764
22.0k
    {
1765
22.0k
        if (!strcmp( at->elname, CType[i].elname ) &&
1766
22.0k
            ( !CType[i].num_bonds || CType[i].num_bonds == at->valence && at->nNumAtInRingSystem >= 5 ))
1767
480
        {
1768
480
            nEndpointValence = (S_CHAR) get_endpoint_valence( at->el_number );
1769
480
            if (bCanBeACPoint( at, CType[i].charge, CType[i].cChangeValence, CType[i].neutral_bonds_valence,
1770
480
                               CType[i].neutral_valence, nEndpointValence, cChargeSubtype ))
1771
2
            {
1772
2
                return CType[i].cChargeType;
1773
2
            }
1774
480
        }
1775
22.0k
    }
1776
1777
3.67k
    return -1;
1778
3.67k
}
1779
1780
1781
/****************************************************************************/
1782
int CmpCCandidates( const void *a1, const void *a2 )
1783
0
{
1784
0
    const C_CANDIDATE *c1 = (const C_CANDIDATE *) a1;
1785
0
    const C_CANDIDATE *c2 = (const C_CANDIDATE *) a2;
1786
0
    int ret;
1787
0
    if (ret = (int) c1->type - (int) c2->type)
1788
0
    {
1789
0
        return ret;
1790
0
    }
1791
0
    if (ret = (int) c1->subtype - (int) c2->subtype)
1792
0
    {
1793
0
        return ret;
1794
0
    }
1795
0
    ret = (int) c1->atnumber - (int) c2->atnumber;
1796
1797
0
    return ret;
1798
0
}
1799
1800
1801
/****************************************************************************/
1802
int RegisterCPoints( C_GROUP *c_group,
1803
                     int *pnum_c,
1804
                     int max_num_c,
1805
                     T_GROUP_INFO *t_group_info,
1806
                     int point1,
1807
                     int point2,
1808
                     int ctype,
1809
                     inp_ATOM *at,
1810
                     int num_atoms )
1811
0
{
1812
0
    int num_c = *pnum_c, i, i1, i2;
1813
0
    AT_NUMB nGroupNumber = 0, nNewGroupNumber;
1814
1815
0
    if (at[point1].c_point == at[point2].c_point)
1816
0
    {
1817
0
        if (at[point1].c_point)
1818
0
        {
1819
0
            return 0;
1820
0
        }
1821
0
        memset( c_group + num_c, 0, sizeof( c_group[0] ) );
1822
0
        if (num_c < max_num_c)
1823
0
        {
1824
0
            c_group[num_c].num[0] = CHARGED_CPOINT( at, point1 ) + CHARGED_CPOINT( at, point2 );
1825
0
            c_group[num_c].num_CPoints += 2;
1826
0
            c_group[num_c].cGroupType = ctype;
1827
            /* get next available c-group number */
1828
0
            for (i = 0; i < num_c; i++)
1829
0
            {
1830
0
                if (nGroupNumber < c_group[i].nGroupNumber)
1831
0
                {
1832
0
                    nGroupNumber = c_group[i].nGroupNumber;
1833
0
                }
1834
0
            }
1835
0
            nGroupNumber++;
1836
0
            c_group[num_c].nGroupNumber =
1837
0
                at[point1].c_point =
1838
0
                at[point2].c_point = nGroupNumber;
1839
0
            *pnum_c = num_c + 1;
1840
            /* count protons */
1841
0
            if (at[point1].num_H)
1842
0
            {
1843
0
                c_group[num_c].num[1] ++;
1844
0
            }
1845
0
            else
1846
0
            {
1847
0
                if (at[point2].num_H)
1848
0
                {
1849
0
                    c_group[num_c].num[1] ++;
1850
0
                }
1851
0
                else
1852
0
                {
1853
0
                    if (( at[point1].endpoint || at[point2].endpoint ) && t_group_info && t_group_info->t_group && t_group_info->num_t_groups)
1854
0
                    {
1855
                        /* !!! add later !!! */
1856
0
                    }
1857
0
                }
1858
0
            }
1859
0
            return 1;
1860
0
        }
1861
1862
0
        return BNS_CPOINT_ERR; /* overflow */
1863
0
    }
1864
1865
0
    if (at[point1].c_point > at[point2].c_point)
1866
0
    {
1867
        /* make sure at[point1].c_point < at[point2].c_point */
1868
0
        i = point1;
1869
0
        point1 = point2;
1870
0
        point2 = i;
1871
0
    }
1872
1873
0
    if (!at[point1].c_point)
1874
0
    {
1875
        /* add a new c-endpoint to an existing c-group */
1876
0
        nGroupNumber = at[point2].c_point;
1877
0
        for (i = 0; i < num_c; i++)
1878
0
        {
1879
0
            if (nGroupNumber == c_group[i].nGroupNumber)
1880
0
            {
1881
0
                at[point1].c_point = at[point2].c_point;
1882
0
                c_group[i].num_CPoints++;
1883
0
                c_group[i].num[0] += CHARGED_CPOINT( at, point1 );
1884
0
                return 1;
1885
0
            }
1886
0
        }
1887
0
        return BNS_CPOINT_ERR; /* program error: c-group not found */
1888
0
    }
1889
0
    else
1890
0
    {
1891
        /* merge two c-groups */
1892
0
        nNewGroupNumber = at[point1].c_point;
1893
0
        nGroupNumber = at[point2].c_point;
1894
0
        for (i = 0, i1 = i2 = -1; i < num_c && ( i1 < 0 || i2 < 0 ); i++)
1895
0
        {
1896
0
            if (nNewGroupNumber == c_group[i].nGroupNumber)
1897
0
            {
1898
0
                i1 = i;
1899
0
                continue;
1900
0
            }
1901
0
            if (nGroupNumber == c_group[i].nGroupNumber)
1902
0
            {
1903
0
                i2 = i;
1904
0
                continue;
1905
0
            }
1906
0
        }
1907
0
        if (i1 < 0 || i2 < 0)
1908
0
        {
1909
0
            return BNS_CPOINT_ERR; /* at least one not found */
1910
0
        }
1911
1912
0
        c_group[i1].num[0] += c_group[i2].num[0];
1913
0
        c_group[i1].num_CPoints += c_group[i2].num_CPoints;
1914
0
        num_c--;
1915
0
        if (num_c > i2)
1916
0
        {
1917
0
            memmove( c_group + i2, c_group + i2 + 1, ( num_c - i2 ) * sizeof( c_group[0] ) );
1918
0
        }
1919
0
        *pnum_c = num_c;
1920
        /* renumber c-groups */
1921
0
        for (i = 0; i < num_c; i++)
1922
0
        {
1923
0
            if (c_group[i].nGroupNumber > nGroupNumber)
1924
0
            {
1925
0
                c_group[i].nGroupNumber--;
1926
0
            }
1927
0
        }
1928
        /* renumber c-points */
1929
0
        for (i = 0; i < num_atoms; i++)
1930
0
        {
1931
0
            if (at[i].c_point > nGroupNumber)
1932
0
            {
1933
0
                at[i].c_point--;
1934
0
            }
1935
0
            else
1936
0
            {
1937
0
                if (at[i].c_point == nGroupNumber)
1938
0
                {
1939
0
                    at[i].c_point = nNewGroupNumber;
1940
0
                }
1941
0
            }
1942
0
        }
1943
0
        return 1;
1944
0
    }
1945
1946
0
}
1947
1948
1949
/****************************************************************************/
1950
int MarkChargeGroups( struct tagCANON_GLOBALS *pCG,
1951
                      inp_ATOM *at,
1952
                      int num_atoms,
1953
                      C_GROUP_INFO *c_group_info,
1954
                      T_GROUP_INFO *t_group_info,
1955
                      struct BalancedNetworkStructure *pBNS,
1956
                      struct BalancedNetworkData *pBD )
1957
110k
{
1958
110k
    int nNumChanges = 0;
1959
1960
110k
    if (c_group_info && c_group_info->c_candidate && c_group_info->max_num_candidates > 0)
1961
806
    {
1962
806
        int i, i1, i2, i3, j, num_tested;
1963
806
        C_CANDIDATE *c_candidate = c_group_info->c_candidate;
1964
806
        int          nMaxNumCandidates = c_group_info->max_num_candidates;
1965
806
        int          nNumCandidates = c_group_info->num_candidates;
1966
806
        S_CHAR       c_type, c_subtype;
1967
806
        int          iat1, iat2, ret, nDelta;
1968
1969
806
        if (nNumCandidates == -1)
1970
0
        {
1971
0
            nNumCandidates = 0; /* 2004-02-26 they could appear after t-group discovery */
1972
                                /*return 0;*/
1973
0
        }
1974
806
        if (nNumCandidates == 0)
1975
806
        {
1976
4.49k
            for (i = 0, nNumCandidates = 0; i < num_atoms; i++)
1977
3.68k
            {
1978
3.68k
                if (0 <= ( c_type = GetChargeType( at, i, &c_subtype ) ))
1979
2
                {
1980
2
                    if (nNumCandidates >= nMaxNumCandidates)
1981
0
                    {
1982
0
                        return BNS_VERT_EDGE_OVFL;
1983
0
                    }
1984
2
                    c_candidate[nNumCandidates].atnumber = i;
1985
2
                    c_candidate[nNumCandidates].type = c_type;
1986
2
                    c_candidate[nNumCandidates].subtype = c_subtype;
1987
2
                    nNumCandidates++;
1988
2
                }
1989
3.68k
            }
1990
806
            if (nNumCandidates <= 1)
1991
806
            {
1992
806
                c_group_info->num_candidates = -1; /* no candidate exists */
1993
806
                return 0;
1994
806
            }
1995
806
        }
1996
        /* sorting keys: (1) atom type (N,P); (2) uncharged=16/charged=0; (3) other;
1997
        atom-charged-N .... i1
1998
        ...
1999
        atom-charged-N
2000
        atom-neutral-N .... i2
2001
        ...
2002
        atom-neutral-N
2003
        atom-charged-P .... i3 ... i1
2004
        ...
2005
        atom-charged-P
2006
        atom-neutral-P ........... i2
2007
        ...
2008
        atom-neutral-P
2009
        end.           ........... i3
2010
        */
2011
2012
0
        qsort( c_candidate, nNumCandidates, sizeof( c_candidate[0] ), CmpCCandidates );
2013
2014
0
        i1 = 0;
2015
0
        num_tested = 0;
2016
0
        nDelta = 0;
2017
2018
0
        while (i1 < nNumCandidates)
2019
0
        {
2020
            /* the the first charged candidate of a new atom type */
2021
0
            for (; i1 < nNumCandidates && ( c_candidate[i1].subtype & C_SUBTYPE_NEUTRAL ); i1++)
2022
0
            {
2023
0
                ;
2024
0
            }
2025
0
            if (i1 == nNumCandidates)
2026
0
            {
2027
0
                break; /* not found */
2028
0
            }
2029
2030
            /* bypass other charged candidates of the same atom type */
2031
0
            for (i2 = i1 + 1; i2 < nNumCandidates &&
2032
0
                  c_candidate[i2].type == c_candidate[i1].type &&
2033
0
                  !( c_candidate[i2].subtype & C_SUBTYPE_NEUTRAL );
2034
0
                  i2++)
2035
0
            {
2036
0
                ;
2037
0
            }
2038
0
            if (i2 == nNumCandidates)
2039
0
            {
2040
0
                break; /* no neutral candidates */
2041
0
            }
2042
2043
            /* find next to the last neutral candidate of the same atom type */
2044
0
            for (i3 = i2;  i3 < nNumCandidates &&
2045
0
                  c_candidate[i3].type == c_candidate[i1].type;
2046
0
                  i3++)
2047
0
            {
2048
0
                ;
2049
0
            }
2050
2051
0
            if (i3 == i2)
2052
0
            {
2053
                /* no neutral candidates found */
2054
0
                if (i2 < nNumCandidates)
2055
0
                {
2056
0
                    i1 = i3;
2057
0
                    continue;  /* move to the next atom type */
2058
0
                }
2059
0
                break; /* nothing more to do */
2060
0
            }
2061
2062
            /* found charged candidates: i1...i2-1;  neutral candidates: i2...i3-1 */
2063
0
            for (i = i1; i < i2; i++)
2064
0
            {
2065
0
                iat1 = c_candidate[i].atnumber;
2066
0
                for (j = i2; j < i3; j++)
2067
0
                {
2068
                    /* check alt path at[iat1]=-=-...-at[iat2]; at[iat1] is charged, at[iat2] is neutral */
2069
0
                    num_tested++;
2070
0
                    iat2 = c_candidate[j].atnumber;
2071
0
                    if (at[iat1].c_point && at[iat1].c_point == at[iat2].c_point)
2072
0
                    {
2073
0
                        continue;
2074
0
                    }
2075
2076
0
                    ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, iat1, iat2, ALT_PATH_MODE_CHARGE );
2077
2078
0
                    if (IS_BNS_ERROR( ret ))
2079
0
                    {
2080
0
                        return ret;
2081
0
                    }
2082
0
                    if (ret & 1)
2083
0
                    {
2084
0
                        nDelta = ( ret & ~3 ) >> 2;
2085
0
                        nNumChanges += ( ret & 2 );
2086
2087
0
                        ret = RegisterCPoints( c_group_info->c_group, &c_group_info->num_c_groups,
2088
0
                                               c_group_info->max_num_c_groups, t_group_info,
2089
0
                                               iat1, iat2, c_candidate[i1].type, at, num_atoms );
2090
2091
0
                        if (IS_BNS_ERROR( ret ))
2092
0
                        {
2093
0
                            return ret;
2094
0
                        }
2095
0
                        if (nDelta)
2096
0
                        {
2097
0
                            goto quick_exit;
2098
0
                        }
2099
0
                    }
2100
0
                }
2101
0
            }
2102
0
            i1 = i3;
2103
0
        }
2104
2105
0
    quick_exit:
2106
0
        if (c_group_info->num_candidates == 0)
2107
0
        {
2108
            /* first time: initialize */
2109
0
            c_group_info->num_candidates = num_tested ? nNumCandidates : -1; /* no candidate exists */
2110
0
        }
2111
0
    }
2112
2113
109k
    return nNumChanges;
2114
110k
}
2115
2116
2117
/****************************************************************************/
2118
int GetSaltChargeType( inp_ATOM *at,
2119
                       int at_no,
2120
                       T_GROUP_INFO *t_group_info,
2121
                       int *s_subtype )
2122
338k
{
2123
    /*
2124
    type (returned value):
2125
    -1 => ignore
2126
    0 => oxygen
2127
    subtype:
2128
    1 = SALT_DONOR_H   => has H
2129
    2 = SALT_DONOR_Neg => has (-) charge
2130
    4 = SALT_ACCEPTOR  => may be an acceptor of H or (-), but not necessarily
2131
2132
    O-atom should be:
2133
    - a terminal atom
2134
    - connected to unsaturated, uncharged, non-radical atom C that has chemical valence 4:
2135
    H-donors:             =CH-OH, =C(-X)-OH
2136
    possible H-acceptors: -CH=O, >C=O
2137
    H-acceptors are true if O is tautomeric
2138
    */
2139
2140
338k
    int iC, tg, i, type;
2141
2142
338k
    *s_subtype = 0; /* initialize the output */
2143
                    /* check whether it is a candidate */
2144
338k
    if (at[at_no].valence != 1 ||
2145
338k
         at[at_no].radical && at[at_no].radical != RADICAL_SINGLET ||
2146
338k
         at[at_no].charge < -1 ||
2147
338k
         at[at_no].charge > 0 && !at[at_no].c_point)
2148
338k
    {
2149
338k
        return -1;
2150
338k
    }
2151
2152
30
    if (at[at_no].el_number == EL_NUMBER_O ||
2153
30
         at[at_no].el_number == EL_NUMBER_S ||
2154
30
         at[at_no].el_number == EL_NUMBER_SE ||
2155
30
         at[at_no].el_number == EL_NUMBER_TE)
2156
30
    {
2157
30
        type = 0;  /* terminal oxygen atom, needs more to be checked... */
2158
30
    }
2159
0
    else
2160
0
    {
2161
0
        type = -1; /* ignore this atom */
2162
0
    }
2163
2164
30
    if (type < 0 ||
2165
30
         at[at_no].chem_bonds_valence + at[at_no].num_H !=
2166
30
         get_el_valence( at[at_no].el_number, at[at_no].charge, 0 ))
2167
0
    {
2168
0
        return -1; /* non-standard valence or not an oxygen */
2169
0
    }
2170
2171
30
    iC = at[at_no].neighbor[0];
2172
2173
30
#if ( SALT_WITH_PROTONS == 1 )
2174
30
    if (at[iC].el_number != EL_NUMBER_C ||
2175
30
         at[iC].chem_bonds_valence + at[iC].num_H != 4 || /* allow =C(H)-OH or -C(H)=O */
2176
30
         at[iC].charge ||
2177
30
         at[iC].radical && at[iC].radical != RADICAL_SINGLET ||
2178
30
         at[iC].valence == at[iC].chem_bonds_valence)
2179
30
    {
2180
30
        return -1; /* oxigen is connected to a wrong atom */
2181
30
    }
2182
#else
2183
    if (at[iC].el_number != EL_NUMBER_C ||
2184
         at[iC].num_H ||
2185
         at[iC].chem_bonds_valence != 4 ||  /* allow only no H on C */
2186
         at[iC].charge ||
2187
         at[iC].radical && at[iC].radical != RADICAL_SINGLET ||
2188
         at[iC].valence == at[iC].chem_bonds_valence)
2189
    {
2190
        return -1; /* oxigen is connected to a wrong atom */
2191
    }
2192
#endif
2193
0
    if (( tg = at[at_no].endpoint ) && t_group_info && t_group_info->t_group)
2194
0
    {
2195
        /* O-atom is in a tautomeric group */
2196
0
        for (i = 0; i < t_group_info->num_t_groups; i++)
2197
0
        {
2198
0
            if (tg == t_group_info->t_group[i].nGroupNumber)
2199
0
            {
2200
                /*
2201
                t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges
2202
                t_group_info->t_group[i].num[1] = number of attached negative charges
2203
                */
2204
0
                if (t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1])
2205
0
                {
2206
0
                    *s_subtype |= SALT_DONOR_H; /* has H */
2207
0
                }
2208
0
                if (t_group_info->t_group[i].num[1])
2209
0
                {
2210
0
                    *s_subtype |= SALT_DONOR_Neg; /* has (-) */
2211
0
                }
2212
0
                *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */
2213
0
                return type;
2214
0
            }
2215
0
        }
2216
0
        return -1; /* error: t-group not found */
2217
0
    }
2218
2219
    /* O is not not in a tautomeric group */
2220
    /* assume valence(O-) < valence(O) < valence(O+) */
2221
0
    if (at[at_no].charge == -1)
2222
0
    {
2223
0
        *s_subtype |= SALT_DONOR_Neg; /* has (-) */
2224
0
    }
2225
0
    if (at[at_no].charge <= 0 && at[at_no].num_H)
2226
0
    {
2227
0
        *s_subtype |= SALT_DONOR_H; /* has H */
2228
0
    }
2229
0
    if (at[at_no].charge == 0 && at[at_no].chem_bonds_valence == 2)
2230
0
    {
2231
0
        *s_subtype |= SALT_ACCEPTOR;
2232
0
    }
2233
    /* since O cannot be a charge point, the following cannot happen: */
2234
0
    if (at[at_no].charge == 1 && at[at_no].c_point && at[at_no].chem_bonds_valence == 2 && at[at_no].num_H)
2235
0
    {
2236
0
        *s_subtype |= SALT_DONOR_H; /* has H */
2237
0
    }
2238
2239
0
    return type;
2240
0
}
2241
2242
2243
/****************************************************************************/
2244
int bDoNotMergeNonTautAtom( inp_ATOM *at, int at_no )
2245
12
{
2246
12
    if (at[at_no].el_number == EL_NUMBER_N)
2247
2
    {
2248
2
        return 1;
2249
2
    }
2250
10
    return 0;
2251
12
}
2252
2253
2254
/****************************************************************************/
2255
int GetOtherSaltChargeType( inp_ATOM *at,
2256
                            int at_no,
2257
                            T_GROUP_INFO *t_group_info,
2258
                            int *s_subtype,
2259
                            int bAccept_O )
2260
338k
{
2261
    /*
2262
    type (returned value):
2263
    -1 => ignore
2264
    1 => not an oxygen
2265
    subtype:
2266
    1 = SALT_DONOR_H   => has H
2267
    2 = SALT_DONOR_Neg => has (-) charge
2268
    4 = SALT_ACCEPTOR  => may be an acceptor of H or (-), but not necessarily
2269
2270
    the atom should be:
2271
    - a tautomeric endpoint atom
2272
    - connected to possible centerpoint atom
2273
2274
    another description of the atom searched here:
2275
2276
    any possibly tautomeric atom adjacent to a possibly centerpoint
2277
    that has at least one double bond (possibly if positively charged);
2278
    if eif.cAcceptor then the bond between the atom and the centerpoint must be possibly double
2279
    if eif.cAcceptor then the bond must be possibly single
2280
    Donors that belong to a t-group are also acceptors
2281
    */
2282
2283
338k
    int tg, i, j, type, endpoint_valence, num_centerpoints, bond_type, centerpoint;
2284
338k
    ENDPOINT_INFO eif;
2285
2286
338k
    *s_subtype = 0; /* initialize the output */
2287
338k
    if (!bAccept_O /* only N */ &&
2288
338k
        ( at[at_no].el_number == EL_NUMBER_O ||
2289
0
          at[at_no].el_number == EL_NUMBER_S ||
2290
0
          at[at_no].el_number == EL_NUMBER_SE ||
2291
0
          at[at_no].el_number == EL_NUMBER_TE ))
2292
0
    {
2293
0
        return -1; /* we are not looking for oxygen here */
2294
0
    }
2295
2296
338k
    type = 1;
2297
338k
    if (!( endpoint_valence = nGetEndpointInfo( at, at_no, &eif ) ))
2298
338k
    {
2299
338k
        return -1; /* not a possible endpoint */
2300
338k
    }
2301
36
    else
2302
36
    {
2303
        /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */
2304
        /* check whether there is adjacent atom-candidate for a centerpoint */
2305
36
        num_centerpoints = 0;
2306
54
        for (j = 0; j < at[at_no].valence; j++)
2307
42
        {
2308
42
            bond_type = (int) at[at_no].bond_type[j] & BOND_TYPE_MASK;
2309
42
            centerpoint = (int) at[at_no].neighbor[j];  /*  a centerpoint candidate */
2310
42
            if (( eif.cAcceptor && ( bond_type == BOND_DOUBLE ||
2311
0
                                     bond_type == BOND_ALTERN || /* possibly double */
2312
0
                                     bond_type == BOND_ALT12NS ||
2313
0
                                     bond_type == BOND_TAUTOM ) ||
2314
42
                  eif.cDonor && ( bond_type == BOND_SINGLE ||
2315
42
                                  bond_type == BOND_ALTERN || /* possibly single */
2316
42
                                  bond_type == BOND_ALT12NS ||
2317
42
                                  bond_type == BOND_TAUTOM ) ) &&
2318
42
                                  ( at[centerpoint].chem_bonds_valence > at[centerpoint].valence ||
2319
                                    /* check for possible endpoint added 2004-02-24 */
2320
42
                                    at[centerpoint].chem_bonds_valence == at[centerpoint].valence &&
2321
18
                                    ( at[centerpoint].endpoint || at[centerpoint].c_point ) /* tautomerism or charge may increment at[centerpoint].chem_bonds_valence*/ ) &&
2322
42
                 is_centerpoint_elem( at[centerpoint].el_number ))
2323
24
            {
2324
24
                num_centerpoints++;
2325
24
                break; /* at least one possibly centerpoint neighbor has been found */
2326
24
            }
2327
42
        }
2328
36
        if (!num_centerpoints)
2329
12
        {
2330
12
            return -1;
2331
12
        }
2332
        /* moved here from just after "type = 1;" line 2004-02-26 */
2333
24
        if (( tg = at[at_no].endpoint ) && t_group_info && t_group_info->t_group)
2334
0
        {
2335
            /* atom is in a tautomeric group */
2336
0
            for (i = 0; i < t_group_info->num_t_groups; i++)
2337
0
            {
2338
0
                if (tg == t_group_info->t_group[i].nGroupNumber)
2339
0
                {
2340
                    /*
2341
                    t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges
2342
                    t_group_info->t_group[i].num[1] = number of attached negative charges
2343
                    */
2344
0
                    if (t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1])
2345
0
                    {
2346
0
                        *s_subtype |= SALT_DONOR_H; /* has H */
2347
0
                    }
2348
0
                    if (t_group_info->t_group[i].num[1])
2349
0
                    {
2350
0
                        *s_subtype |= SALT_DONOR_Neg; /* has (-) */
2351
0
                    }
2352
0
                    *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */
2353
0
                    return type;
2354
0
                }
2355
0
            }
2356
0
            return -1; /* error: t-group not found */
2357
0
        }
2358
2359
24
        if (eif.cAcceptor)
2360
0
        {
2361
0
            *s_subtype |= SALT_ACCEPTOR;
2362
0
        }
2363
24
        if (eif.cDonor)
2364
24
        {
2365
24
            if (at[at_no].charge == -1)
2366
24
            {
2367
24
                *s_subtype |= SALT_DONOR_Neg; /* has (-) */
2368
24
            }
2369
24
            if (at[at_no].num_H)
2370
0
            {
2371
0
                *s_subtype |= SALT_DONOR_H; /* has H */
2372
0
            }
2373
24
        }
2374
24
    }
2375
2376
24
    return type;
2377
338k
}
2378
2379
2380
/****************************************************************************/
2381
int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype )
2382
338k
{
2383
    /*
2384
    type (returned value):
2385
    -1 => ignore
2386
    2 => found:                           SH
2387
    proton donor     -CH2-SH, >CH-SH, >C<    S(-)
2388
    proton acceptor  -CH2-S(-), >CH-S(-), >C<
2389
    subtype:
2390
    1 = SALT_DONOR_H   => has H
2391
    2 = SALT_DONOR_Neg => has (-) charge
2392
    4 = SALT_ACCEPTOR  => may be an acceptor of H or (-), but not necessarily
2393
2394
    non-O-atom should be:
2395
    - a tautomeric endpoint atom
2396
    - connected to possible middle point atom
2397
    */
2398
2399
338k
    int type, endpoint_valence, bond_type, centerpoint;
2400
338k
    ENDPOINT_INFO eif;
2401
2402
338k
    if (at[at_no].valence != 1 || at[at_no].chem_bonds_valence != 1 ||
2403
338k
         1 != ( at[at_no].num_H == 1 ) + ( at[at_no].charge == -1 ))
2404
338k
    {
2405
338k
        return -1;
2406
338k
    }
2407
2408
12
    *s_subtype = 0; /* initialize the output */
2409
12
    if (!( at[at_no].el_number == EL_NUMBER_S ||
2410
12
           at[at_no].el_number == EL_NUMBER_SE ||
2411
12
           at[at_no].el_number == EL_NUMBER_TE ))
2412
12
    {
2413
12
        return -1; /* we are not looking for oxygen here */
2414
12
    }
2415
2416
0
    type = 2; /* non-tautomeric p-donor or acceptor: C-SH, C-S(-) */
2417
2418
0
    if (!( endpoint_valence = nGetEndpointInfo( at, at_no, &eif ) ) ||
2419
0
         eif.cMoveableCharge && !at[at_no].c_point || !eif.cDonor || eif.cAcceptor)
2420
0
    {
2421
0
        return -1; /* not a possible -SH or -S(-) */
2422
0
    }
2423
0
    else
2424
0
    {
2425
        /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */
2426
        /* check whether there is adjacent atom-candidate for a centerpoint */
2427
0
        centerpoint = (int) at[at_no].neighbor[0];
2428
0
        bond_type = (int) at[at_no].bond_type[0] & BOND_TYPE_MASK;
2429
0
        if (at[centerpoint].el_number != EL_NUMBER_C ||
2430
0
             at[centerpoint].charge ||
2431
0
             at[centerpoint].radical && at[centerpoint].radical != RADICAL_SINGLET ||
2432
0
             at[centerpoint].valence != at[centerpoint].chem_bonds_valence)
2433
0
        {
2434
0
            return -1; /* not a carbon with all single bonds */
2435
0
        }
2436
0
        if (at[at_no].num_H == 1)
2437
0
        {
2438
0
            *s_subtype |= SALT_p_DONOR;
2439
0
        }
2440
0
        else
2441
0
        {
2442
0
            if (at[at_no].charge == -1)
2443
0
            {
2444
0
                *s_subtype |= SALT_p_ACCEPTOR;
2445
0
            }
2446
0
            else
2447
0
            {
2448
0
                return -1;
2449
0
            }
2450
0
        }
2451
0
    }
2452
2453
0
    return type;
2454
0
}
2455
2456
2457
/************************************************************************************/
2458
/* new version: merge all, check alt paths, then unmerge unreachable O-atoms if any */
2459
/* Check for oxygen negative charge-H tautomerism (Salts)
2460
allowed long-range tautomerism; more than one H or (-) can be moved, for example:
2461
HO-C=C-O(-)         O=C-C=O
2462
/   \              /   \
2463
R     R            R     R
2464
|     |       =>   |     |
2465
R'    R'           R'    R'
2466
\   /              \   /
2467
O=C-C=O           HO-C=C-O(-)
2468
2469
To check:
2470
2471
|             |
2472
-add all possible HO-C=, O=C, (-)O-C= (including all containing O t-groups) into one t-group;
2473
-temporarily disconnect one of previously not belonging to any t-group O-atoms from the one t-group;
2474
-find whether there is an alt path allowing H or (-) to migrate
2475
from the temp. disconnected O to any one left in the group.
2476
If the alt path does not exist then the temp. disconnected atom does not
2477
participate in the H/(-) migrartion and it will be unmarked/unmerged.
2478
*/
2479
2480
2481
2482
/****************************************************************************/
2483
int comp_candidates( const void *a1, const void *a2 )
2484
0
{
2485
0
    const S_CANDIDATE *s1 = (const S_CANDIDATE *) a1;
2486
0
    const S_CANDIDATE *s2 = (const S_CANDIDATE *) a2;
2487
0
    int ret;
2488
0
    if (s1->type >= 0 /* enabled < */ && s2->type < 0 /* disabled */)
2489
0
    {
2490
0
        return -1; /* enabled goes first */
2491
0
    }
2492
0
    if (s1->type < 0 /* disabled > */ && s2->type >= 0 /* enabled */)
2493
0
    {
2494
0
        return 1;
2495
0
    }
2496
0
    if (s1->endpoint && !s2->endpoint)
2497
0
    {
2498
0
        return -1; /* tautomeric goes first; only tautomeric may be disabled */
2499
0
    }
2500
0
    if (!s1->endpoint && s2->endpoint)
2501
0
    {
2502
0
        return 1; /* tautomeric goes first; only tautomeric may be disabled */
2503
0
    }
2504
0
    if (s1->endpoint && s2->endpoint && ( ret = (int) s1->endpoint - (int) s2->endpoint ))
2505
0
    {
2506
0
        return ret;
2507
0
    }
2508
2509
0
    return (int) s1->atnumber - (int) s2->atnumber;
2510
0
}
2511
2512
2513
/****************************************************************************/
2514
int MarkSaltChargeGroups2( CANON_GLOBALS *pCG,
2515
                           inp_ATOM *at,
2516
                           int num_atoms,
2517
                           S_GROUP_INFO *s_group_info,
2518
                           T_GROUP_INFO *t_group_info,
2519
                           C_GROUP_INFO *c_group_info,
2520
                           struct BalancedNetworkStructure *pBNS,
2521
                           struct BalancedNetworkData *pBD )
2522
110k
{
2523
    /* BNS_EDGE_FORBIDDEN_TEMP */
2524
110k
#define ALT_PATH_FOUND    (MAX_ATOMS+1)
2525
110k
#define NO_ENDPOINT       (MAX_ATOMS+2)  /* the two defines must be different */
2526
110k
#define DISABLE_CANDIDATE 10
2527
110k
#define cPAIR(a,b) cPair[a+b*nNumLeftCandidates]
2528
110k
#define ACCEPTOR_PAIR 1
2529
110k
#define DONOR_PAIR    2
2530
2531
110k
    int nNumChanges = 0, nNumOtherChanges = 0, nNumAcidicChanges = 0, nTotNumChanges = 0;
2532
110k
    S_CHAR      *cPair = NULL;
2533
110k
    T_ENDPOINT  *EndPoint = NULL;
2534
110k
    if (s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0)
2535
110k
    {
2536
110k
        int i, j, i1, j1;
2537
110k
        S_CANDIDATE *s_candidate = s_group_info->s_candidate;
2538
110k
        int          nMaxNumCandidates = s_group_info->max_num_candidates;
2539
110k
        int          nNumCandidates = s_group_info->num_candidates;
2540
110k
        int          nNumOtherCandidates = s_group_info->num_other_candidates;
2541
110k
        int          nNumPOnlyCandidates = s_group_info->num_p_only_candidates;
2542
110k
        int          nNumLeftCandidates = 0;
2543
110k
        int          nNumMarkedCandidates = 0;
2544
110k
        int          s_type, s_subtype;
2545
110k
        int          ret, nDelta;
2546
110k
        int          bHardAddedRemovedProtons = t_group_info && ( t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT );
2547
2548
110k
        int          s_subtype_all = 0;
2549
110k
        int          nDonorPairs, nAcceptorPairs, nCurDonorPairs, nCurAcceptorPairs, bAlreadyTested;
2550
        /*
2551
        ENDPOINT_INFO    eif;
2552
        */
2553
2554
110k
#if ( IGNORE_TGROUP_WITHOUT_H == 1 )
2555
110k
        int          bTGroupHasNegativeChargesOnly = 1;
2556
110k
#endif
2557
        /*return 0;*/ /* debug only */
2558
2559
110k
        i1 = -1;
2560
2561
110k
        if (nNumCandidates <= -2 || !t_group_info || !t_group_info->t_group)
2562
0
        {
2563
0
            return 0;
2564
0
        }
2565
2566
        /*************************************************************************/
2567
        /* Find all candidates including those with differen s_type (other type) */
2568
        /*************************************************************************/
2569
222k
        for (i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i++)
2570
112k
        {
2571
112k
            if (0 == ( s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype ) ) ||
2572
                 /* -C=O or =C-OH, O = S, Se, Te */
2573
112k
                 1 == ( s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ ) ) ||
2574
                 /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */
2575
112k
                 2 == ( s_type = GetOtherSaltType( at, i, &s_subtype ) ) ||
2576
112k
                 ( bHardAddedRemovedProtons && 4 == ( s_type = bIsHardRemHCandidate( at, i, &s_subtype ) ) )
2577
                 /* >C-SH, >C-S(-); S=S,Se,Te */
2578
112k
                 )
2579
8
            {
2580
2581
8
                if (nNumCandidates >= nMaxNumCandidates)
2582
0
                {
2583
0
                    return BNS_VERT_EDGE_OVFL;
2584
0
                }
2585
8
                s_candidate[nNumCandidates].atnumber = i;
2586
8
                s_candidate[nNumCandidates].type = s_type;
2587
8
                s_candidate[nNumCandidates].subtype = s_subtype;
2588
8
                s_candidate[nNumCandidates].endpoint = at[i].endpoint;
2589
8
                nNumCandidates++;
2590
8
                nNumOtherCandidates += ( 1 == s_type );
2591
8
                s_subtype_all |= s_subtype;
2592
8
                i1 = i; /* save a representative of a tautomeric group */
2593
8
            }
2594
112k
        }
2595
2596
110k
        if (nNumCandidates <= 1 ||  /* TG_FLAG_ALLOW_NO_NEGTV_O <=> CHARGED_SALTS_ONLY=0 */
2597
110k
             !( s_subtype_all & SALT_ACCEPTOR ) ||
2598
110k
             ( ( ( t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O ) ||
2599
0
             ( t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE ) ||
2600
0
                 ( t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) ) ?
2601
0
               !( s_subtype_all & ( SALT_DONOR ) ) :
2602
0
               ( !( s_subtype_all & SALT_DONOR_Neg ) || nNumOtherCandidates == nNumCandidates ) )
2603
110k
             )
2604
110k
        {
2605
110k
            s_group_info->num_candidates = 0; /* no candidate exists */
2606
110k
            return 0;
2607
110k
        }
2608
2609
0
        if (!( s_subtype_all & ( SALT_DONOR_Neg ) ))
2610
0
        {
2611
0
            t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE;
2612
0
        }
2613
2614
        /************************************************************************************/
2615
        /* Mark redundant candidates so that only one candidate from one t-group is left in */
2616
        /************************************************************************************/
2617
0
        for (i = 0; i < nNumCandidates; i++)
2618
0
        {
2619
0
            if (2 == s_candidate[nNumCandidates].type)
2620
0
            {
2621
0
                s_candidate[i].type -= DISABLE_CANDIDATE; /* disable >C-SH candidates */
2622
0
                nNumLeftCandidates++; /* count rejected */
2623
0
                continue;
2624
0
            }
2625
0
            if (s_candidate[i].endpoint)
2626
0
            {
2627
0
                for (j = i - 1; 0 <= j; j--)
2628
0
                {
2629
0
                    if (s_candidate[i].endpoint == s_candidate[j].endpoint)
2630
0
                    {
2631
0
                        s_candidate[i].type -= DISABLE_CANDIDATE; /* disable subsequent redundant */
2632
0
                        nNumLeftCandidates++; /* count rejected */
2633
0
                        break;
2634
0
                    }
2635
0
                }
2636
0
            }
2637
0
        }
2638
2639
0
        nNumLeftCandidates = nNumCandidates - nNumLeftCandidates; /* subtract num. rejected from the total */
2640
0
        s_group_info->num_candidates = 0; /* reinit next time */
2641
                                          /*********************************************************************/
2642
                                          /* reorder so that all disabled are at the end, tautomeric are first */
2643
                                          /*********************************************************************/
2644
2645
0
        qsort( s_candidate, nNumCandidates, sizeof( s_candidate[0] ), comp_candidates );
2646
2647
0
        cPair = (S_CHAR *) inchi_calloc( nNumLeftCandidates*nNumLeftCandidates, sizeof( cPair[0] ) );
2648
0
        if (!cPair)
2649
0
        {
2650
            /*printf("BNS_OUT_OF_RAM-6\n");*/
2651
0
            nTotNumChanges = BNS_OUT_OF_RAM;
2652
0
            goto quick_exit;
2653
0
        }
2654
0
        nDonorPairs = nAcceptorPairs = 0;
2655
2656
        /**********************************************************************/
2657
        /* Find whether we have at least one donor pair and one acceptor pair */
2658
        /**********************************************************************/
2659
0
        for (i = 0; i < nNumLeftCandidates; i++)
2660
0
        {
2661
0
            nCurDonorPairs = nCurAcceptorPairs = 0;
2662
0
            for (j = 0; j <= i; j++)
2663
0
            {
2664
0
                if (i == j && !s_candidate[i].endpoint)
2665
0
                {
2666
0
                    continue;  /* same non-taut atom. However, success for i==j means     *
2667
                               * that the whole tautomeric group may donate or accept 2H */
2668
0
                }
2669
                /* check for acceptor pair */
2670
0
                if (( s_candidate[i].subtype & SALT_ACCEPTOR ) && ( s_candidate[j].subtype & SALT_ACCEPTOR ) &&
2671
0
                    ( ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber,
2672
0
                                            s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_TST ) ))
2673
0
                {
2674
0
                    if (IS_BNS_ERROR( ret ))
2675
0
                    {
2676
0
                        nTotNumChanges = ret;
2677
0
                        goto quick_exit;
2678
0
                    }
2679
0
                    if (ret & 1)
2680
0
                    {
2681
0
                        nDelta = ( ret & ~3 ) >> 2;
2682
                        /*nNumChanges += (ret & 2);*/
2683
0
                        if (nDelta)
2684
0
                        {
2685
                            /* alt path unleashed previously localized radicals and they annihilated */
2686
0
                            nNumChanges = 0;
2687
0
                            nTotNumChanges = BNS_RADICAL_ERR;
2688
0
                            goto quick_exit;
2689
0
                        }
2690
0
                        cPAIR( i, j ) |= ACCEPTOR_PAIR; /* the result: mark the pair */
2691
                                                        /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/
2692
0
                    }
2693
0
                }
2694
                /* Check for donor pair */
2695
0
                if (( s_candidate[i].subtype & SALT_DONOR ) && ( s_candidate[j].subtype & SALT_DONOR ) &&
2696
0
                    ( ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber,
2697
0
                                            s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_TST ) ))
2698
0
                {
2699
0
                    if (IS_BNS_ERROR( ret ))
2700
0
                    {
2701
0
                        nTotNumChanges = ret;
2702
0
                        goto quick_exit;
2703
0
                    }
2704
0
                    if (ret & 1)
2705
0
                    {
2706
0
                        nDelta = ( ret & ~3 ) >> 2;
2707
                        /*nNumChanges += (ret & 2);*/
2708
0
                        if (nDelta)
2709
0
                        {
2710
                            /* Alt path unleashed previously localized radicals and they annihilated */
2711
0
                            nNumChanges = 0;
2712
0
                            nTotNumChanges = BNS_RADICAL_ERR;
2713
0
                            goto quick_exit;
2714
0
                        }
2715
0
                        cPAIR( i, j ) |= DONOR_PAIR; /* the result: mark the pair */
2716
                                                     /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/
2717
0
                    }
2718
0
                }
2719
                /* Since the results will be used later to change bonds, check only now */
2720
                /* when both results for (i,j) have been obtained. */
2721
0
                if (cPAIR( i, j ) & ACCEPTOR_PAIR)
2722
0
                {
2723
0
                    nCurAcceptorPairs++;
2724
0
                    if (nDonorPairs)
2725
0
                    {
2726
                        /* Find donor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */
2727
0
                        for (i1 = 0; i1 < i; i1++)
2728
0
                        {
2729
0
                            for (j1 = 0; j1 <= i1; j1++)
2730
0
                            {
2731
                                /* Here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */
2732
0
                                if (j1 != j && i1 != j && ( cPAIR( i1, j1 ) & DONOR_PAIR ))
2733
0
                                {
2734
                                    /* Both the donor and the acceptor pairs have been found */
2735
0
                                    goto bFound2Pairs;
2736
0
                                }
2737
0
                            }
2738
0
                        }
2739
0
                    }
2740
0
                }
2741
0
                if (cPAIR( i, j ) & DONOR_PAIR)
2742
0
                {
2743
0
                    nCurDonorPairs++;
2744
0
                    if (nAcceptorPairs)
2745
0
                    {
2746
                        /* Find acceptor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */
2747
0
                        for (i1 = 0; i1 < i; i1++)
2748
0
                        {
2749
0
                            for (j1 = 0; j1 <= i1; j1++)
2750
0
                            {
2751
                                /* Here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */
2752
0
                                if (j1 != j && i1 != j && ( cPAIR( i1, j1 ) & ACCEPTOR_PAIR ))
2753
0
                                {
2754
                                    /* Both the donor and the acceptor pairs have been found */
2755
0
                                    goto bFound2Pairs;
2756
0
                                }
2757
0
                            }
2758
0
                        }
2759
0
                    }
2760
0
                }
2761
0
            }
2762
0
            nDonorPairs += nCurDonorPairs;
2763
0
            nAcceptorPairs += nCurAcceptorPairs;
2764
0
        }
2765
2766
        /* Nothing has been found */
2767
0
        nNumChanges = 0;
2768
0
        inchi_free( cPair );
2769
0
        cPair = NULL;
2770
0
        goto quick_exit;
2771
2772
2773
        /* Both the donor and the acceptor pairs have been found */
2774
0
    bFound2Pairs:
2775
        /* first, try already found pairs */
2776
0
        i1 = i;
2777
0
        j1 = j;
2778
2779
        /* Find all possible donor and acceptor pairs */
2780
0
        nNumMarkedCandidates = 0;
2781
0
        for (i = 0; i < nNumLeftCandidates; i++)
2782
0
        {
2783
0
            nCurDonorPairs = nCurAcceptorPairs = 0;
2784
0
            for (j = 0; j <= i; j++)
2785
0
            {
2786
0
                bAlreadyTested = ( i < i1 || i == i1 && j <= j1 );
2787
0
                if (bAlreadyTested && ( cPAIR( i, j ) & ACCEPTOR_PAIR ) || !bAlreadyTested)
2788
0
                {
2789
                    /* Checking for acceptor pair */
2790
0
                    if (( s_candidate[i].subtype & SALT_ACCEPTOR ) && ( s_candidate[j].subtype & SALT_ACCEPTOR ) &&
2791
0
                        ( ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber,
2792
0
                                                s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_CHG ) ))
2793
0
                    {
2794
0
                        if (IS_BNS_ERROR( ret ))
2795
0
                        {
2796
0
                            nTotNumChanges = ret;
2797
0
                            goto quick_exit;
2798
0
                        }
2799
0
                        if (ret & 1)
2800
0
                        {
2801
0
                            nDelta = ( ret & ~3 ) >> 2;
2802
0
                            nNumChanges += ( ret & 2 );
2803
0
                            if (nDelta)
2804
0
                            {
2805
                                /* Alt path unleashed previously localized radicals and they annihilated */
2806
0
                                nNumChanges = 0;
2807
0
                                nTotNumChanges = BNS_RADICAL_ERR;
2808
0
                                goto quick_exit;
2809
0
                            }
2810
0
                            cPAIR( i, j ) |= ACCEPTOR_PAIR;
2811
                            /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/
2812
0
                            nCurAcceptorPairs += !bAlreadyTested;
2813
0
                            if (!( s_candidate[i].subtype & SALT_SELECTED ))
2814
0
                            {
2815
0
                                s_candidate[i].subtype |= SALT_SELECTED;
2816
0
                                nNumMarkedCandidates++;
2817
0
                                if (!s_candidate[i].endpoint && s_candidate[i].type)
2818
0
                                {
2819
0
                                    nNumOtherChanges++;
2820
0
                                }
2821
0
                                else
2822
0
                                {
2823
0
                                    nNumAcidicChanges++;
2824
0
                                }
2825
0
                            }
2826
0
                            if (!( s_candidate[j].subtype & SALT_SELECTED ))
2827
0
                            {
2828
0
                                s_candidate[j].subtype |= SALT_SELECTED;
2829
0
                                nNumMarkedCandidates++;
2830
0
                                if (!s_candidate[j].endpoint && s_candidate[j].type)
2831
0
                                {
2832
0
                                    nNumOtherChanges++;
2833
0
                                }
2834
0
                                else
2835
0
                                {
2836
0
                                    nNumAcidicChanges++;
2837
0
                                }
2838
0
                            }
2839
0
                        }
2840
0
                    }
2841
0
                }
2842
2843
0
                if (bAlreadyTested && ( cPAIR( i, j ) & DONOR_PAIR ) || !bAlreadyTested)
2844
0
                {
2845
                    /* Checking for donor pair */
2846
0
                    if (( s_candidate[i].subtype & SALT_DONOR ) && ( s_candidate[j].subtype & SALT_DONOR ) &&
2847
0
                        ( ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber,
2848
0
                                                s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_CHG ) ))
2849
0
                    {
2850
0
                        if (IS_BNS_ERROR( ret ))
2851
0
                        {
2852
0
                            nTotNumChanges = ret;
2853
0
                            goto quick_exit;
2854
0
                        }
2855
0
                        if (ret & 1)
2856
0
                        {
2857
0
                            nDelta = ( ret & ~3 ) >> 2;
2858
0
                            nNumChanges += ( ret & 2 );
2859
0
                            if (nDelta)
2860
0
                            {
2861
                                /* Alt path unleashed previously localized radicals and they annihilated */
2862
0
                                nNumChanges = 0;
2863
0
                                nTotNumChanges = BNS_RADICAL_ERR;
2864
0
                                goto quick_exit;
2865
0
                            }
2866
0
                            cPAIR( i, j ) |= DONOR_PAIR;
2867
                            /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/
2868
0
                            nCurDonorPairs += !bAlreadyTested;
2869
0
                            if (!( s_candidate[i].subtype & SALT_SELECTED ))
2870
0
                            {
2871
0
                                s_candidate[i].subtype |= SALT_SELECTED;
2872
0
                                nNumMarkedCandidates++;
2873
0
                                if (!s_candidate[i].endpoint && s_candidate[i].type)
2874
0
                                {
2875
0
                                    nNumOtherChanges++;
2876
0
                                }
2877
0
                                else
2878
0
                                {
2879
0
                                    nNumAcidicChanges++;
2880
0
                                }
2881
0
                            }
2882
0
                            if (!( s_candidate[j].subtype & SALT_SELECTED ))
2883
0
                            {
2884
0
                                s_candidate[j].subtype |= SALT_SELECTED;
2885
0
                                nNumMarkedCandidates++;
2886
0
                                if (!s_candidate[j].endpoint && s_candidate[j].type)
2887
0
                                {
2888
0
                                    nNumOtherChanges++;
2889
0
                                }
2890
0
                                else
2891
0
                                {
2892
0
                                    nNumAcidicChanges++;
2893
0
                                }
2894
0
                            }
2895
0
                        }
2896
0
                    }
2897
0
                }
2898
0
            }
2899
0
            nDonorPairs += nCurDonorPairs;
2900
0
            nAcceptorPairs += nCurAcceptorPairs;
2901
0
        }
2902
2903
0
        inchi_free( cPair );
2904
0
        cPair = NULL;
2905
2906
0
        if (nNumMarkedCandidates)
2907
0
        {
2908
0
            EndPoint = (T_ENDPOINT *) inchi_calloc( nNumMarkedCandidates, sizeof( EndPoint[0] ) );
2909
0
            if (!EndPoint)
2910
0
            {
2911
                /*printf("BNS_OUT_OF_RAM-7\n");*/
2912
0
                nTotNumChanges = BNS_OUT_OF_RAM;
2913
0
                goto quick_exit;
2914
0
            }
2915
0
            for (i = 0, j = 0; i < nNumLeftCandidates; i++)
2916
0
            {
2917
0
                if (s_candidate[i].subtype & SALT_SELECTED)
2918
0
                {
2919
0
                    s_candidate[i].subtype ^= SALT_SELECTED; /* remove the flag */
2920
0
                    if (j < nNumMarkedCandidates)
2921
0
                    {
2922
0
                        i1 = s_candidate[i].atnumber; /* save a representative of the t-group to be created */
2923
0
                        AddEndPoint( EndPoint + j, at, i1 );
2924
0
                    }
2925
0
                    j++;
2926
0
                }
2927
0
            }
2928
0
            if (j != nNumMarkedCandidates)
2929
0
            {
2930
0
                nTotNumChanges = BNS_PROGRAM_ERR;
2931
0
                goto quick_exit;
2932
0
            }
2933
2934
            /* Merge all marked atoms and their t-groups into one t-group */
2935
0
            ret = RegisterEndPoints( pCG, t_group_info, EndPoint,
2936
0
                                     nNumMarkedCandidates, at,
2937
0
                                     num_atoms, c_group_info, pBNS );
2938
2939
0
            if (ret == -1)
2940
0
            {
2941
0
                ret = BNS_PROGRAM_ERR;
2942
0
            }
2943
0
            if (ret < 0)
2944
0
            {
2945
0
                nTotNumChanges = ret;
2946
0
                goto quick_exit;
2947
0
            }
2948
0
            nTotNumChanges += ( ret > 0 );
2949
0
            inchi_free( EndPoint );
2950
0
            EndPoint = NULL;
2951
2952
0
            if (nNumMarkedCandidates)
2953
0
            {
2954
0
                for (i = nNumLeftCandidates; i < nNumCandidates; i++)
2955
0
                {
2956
0
                    s_candidate[i].type += DISABLE_CANDIDATE;
2957
0
                    j1 = s_candidate[i].atnumber;
2958
0
                    if (at[j1].endpoint == at[i1].endpoint)
2959
0
                    {
2960
0
                        if (!s_candidate[i].endpoint && s_candidate[i].type)
2961
0
                        {
2962
0
                            nNumOtherChanges++;
2963
0
                        }
2964
0
                        else
2965
0
                        {
2966
0
                            nNumAcidicChanges++;
2967
0
                        }
2968
0
                    }
2969
0
                }
2970
0
            }
2971
0
            else
2972
0
            {
2973
0
                for (i = nNumLeftCandidates; i < nNumCandidates; i++)
2974
0
                {
2975
0
                    s_candidate[i].type += DISABLE_CANDIDATE;
2976
0
                }
2977
0
            }
2978
2979
            /* Find whether the new t-group have any movable H */
2980
0
            for (i = 0, bTGroupHasNegativeChargesOnly = 0; i < t_group_info->num_t_groups; i++)
2981
0
            {
2982
0
                if (t_group_info->t_group[i].nGroupNumber == at[i1].endpoint &&
2983
0
                     t_group_info->t_group[i].num[0] == t_group_info->t_group[i].num[1])
2984
0
                {
2985
0
                    bTGroupHasNegativeChargesOnly = 1;
2986
0
                    break;
2987
0
                }
2988
0
            }
2989
0
        }
2990
0
        nTotNumChanges = ( nTotNumChanges > 0 );
2991
2992
0
#if ( IGNORE_TGROUP_WITHOUT_H == 1 )
2993
0
        if (nTotNumChanges && bTGroupHasNegativeChargesOnly)
2994
0
        {
2995
0
            nTotNumChanges = 2;  /* means no moveable H has been affected */
2996
0
        }
2997
0
#endif
2998
0
    }
2999
3000
0
quick_exit:
3001
0
    if (nNumOtherChanges && nTotNumChanges == 1)
3002
0
    {
3003
0
        nTotNumChanges = 5; /* not only acidic atoms merged */
3004
0
    }
3005
0
    if (cPair)
3006
0
    {
3007
0
        inchi_free( cPair );
3008
        /*cPair = NULL;*/
3009
0
    }
3010
0
    if (EndPoint)
3011
0
    {
3012
0
        inchi_free( EndPoint );
3013
        /*EndPoint = NULL;*/
3014
0
    }
3015
3016
0
    return nTotNumChanges; /* 0=>no changes, 1=>new salt tautomerism found, 2=>only new charge tautomerism found */
3017
110k
#undef ALT_PATH_FOUND
3018
110k
#undef NO_ENDPOINT
3019
110k
}
3020
3021
3022
/****************************************************************************/
3023
/* regular one-path version: find alt paths then merge */
3024
/* Check for oxygen negative charge-H tautomerism (Salts)
3025
allowed long-range tautomerism; only one H or (-) can be moved, for example:
3026
HO-C=X-Y=Z-...-C=O  => O=C-X=Y-Z=...=C-OH
3027
*/
3028
3029
#if ( SALT_WITH_PROTONS == 1 )
3030
3031
#define MAX_LOCAL_TGNUM 0 /* was 32; disable since it has not been used */
3032
3033
#if ( MAX_LOCAL_TGNUM > 0 )
3034
typedef struct tagTGroupData {
3035
    S_SHORT nGroupNumber; /* t-group number from t_group_info->t_group->nGroupNumber */
3036
    S_SHORT nGroupIndex;  /* TGroupData[nGroupNumber]nGroupIndex = index of t_group in t_group_info */
3037
    S_SHORT nDonorM;      /* number of endpoint-donors that have negative charge (Minus) */
3038
    S_SHORT nDonorH;      /* number of endpoint-donors that have only H */
3039
    S_SHORT nAccepM;      /* number of endpoint-acceptors that have negative charge (Minus) */
3040
    S_SHORT nAccepH;      /* number of endpoint-acceptors that have H and no negative charge */
3041
    S_SHORT nAccep0;      /* number of endpoint-acceptors that have no H and no negative charge */
3042
    S_SHORT nDonorA;      /* number of acidic endpoint-donors */
3043
    S_SHORT nAccepS;      /* number of acidic endpoint-acceptors */
3044
} TGroupData;
3045
#endif
3046
3047
3048
/****************************************************************************/
3049
int MarkSaltChargeGroups( CANON_GLOBALS *pCG,
3050
                          inp_ATOM *at,
3051
                          int num_atoms,
3052
                          S_GROUP_INFO *s_group_info,
3053
                          T_GROUP_INFO *t_group_info,
3054
                          C_GROUP_INFO *c_group_info,
3055
                          struct BalancedNetworkStructure *pBNS,
3056
                          struct BalancedNetworkData *pBD )
3057
110k
{
3058
3059
110k
    int nNumChanges = 0, nTotNumChanges = 0;
3060
110k
    if (s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0)
3061
110k
    {
3062
110k
        int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, /*k,*/ num_tested;
3063
110k
        S_CANDIDATE *s_candidate = s_group_info->s_candidate;
3064
110k
        int          nMaxNumCandidates = s_group_info->max_num_candidates;
3065
110k
        int          nNumCandidates = s_group_info->num_candidates;
3066
110k
        int          nNumOtherCandidates = s_group_info->num_other_candidates;
3067
110k
        int          nNumPOnlyCandidates = s_group_info->num_p_only_candidates;
3068
110k
        int          s_type, s_subtype;
3069
110k
        int          ret, nDelta, /*nMobile,*/ err = 0;
3070
110k
        int          s_subtype_all = 0;
3071
110k
        int          nGroupNumber;
3072
110k
        T_ENDPOINT   EndPoint[2];
3073
#if ( MAX_LOCAL_TGNUM > 0 )
3074
        TGroupData   tgData[MAX_LOCAL_TGNUM];
3075
        TGroupData   *ptgData = tgData;
3076
#endif
3077
110k
        int cond1 = 0, cond2a = 0, cond2b = 0, cond2c = 0, cond2 = 0;
3078
3079
110k
        if (nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group)
3080
0
        {
3081
0
            return 0;
3082
0
        }
3083
3084
        /* count t-groups */
3085
110k
        for (i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i++)
3086
0
        {
3087
0
            if (nGroupNumber < t_group_info->t_group[i].nGroupNumber)
3088
0
            {
3089
0
                nGroupNumber = t_group_info->t_group[i].nGroupNumber; /* max. t-group number */
3090
0
            }
3091
0
        }
3092
#if ( MAX_LOCAL_TGNUM > 0 )
3093
        /* prepare memory */
3094
        if (nGroupNumber >= MAX_LOCAL_TGNUM)
3095
        {
3096
            if (!( ptgData = (TGroupData*) inchi_calloc( nGroupNumber + 1, sizeof( TGroupData ) ) ))
3097
            {
3098
                err = BNS_OUT_OF_RAM;
3099
                goto quick_exit;
3100
            }
3101
        }
3102
        else
3103
        {
3104
            memset( ptgData, 0, sizeof( tgData ) );
3105
        }
3106
        ptgData[0].nGroupIndex = -1; /* data for non-tautomeric atoms */
3107
        for (i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i++)
3108
        {
3109
            if (nGroupNumber = t_group_info->t_group[i].nGroupNumber)
3110
            {
3111
                ptgData[nGroupNumber].nGroupIndex = i;
3112
                ptgData[i].nGroupNumber = nGroupNumber;
3113
            }
3114
        }
3115
#endif
3116
110k
        nNumCandidates = 0; /* always recalculate 2004-03-22 */
3117
110k
        num_tested = 0;
3118
3119
110k
        if (nNumCandidates == 0)
3120
110k
        {
3121
222k
            for (i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i++)
3122
112k
            {
3123
112k
                if (0 == ( s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype ) ) ||
3124
                     /* -C=O or =C-OH, O = S, Se, Te */
3125
112k
#if ( INCL_NON_SALT_CANDIDATATES == 1 )
3126
112k
                     1 == ( s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 ) ) ||
3127
                     /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */
3128
112k
#endif
3129
112k
                     2 == ( s_type = GetOtherSaltType( at, i, &s_subtype ) )
3130
                     /* >C-SH, >C-S(-); S=S,Se,Te */
3131
112k
                     )
3132
8
                {
3133
3134
8
                    if (nNumCandidates >= nMaxNumCandidates)
3135
0
                    {
3136
0
                        err = BNS_VERT_EDGE_OVFL;
3137
0
                        goto quick_exit;
3138
0
                    }
3139
8
                    s_candidate[nNumCandidates].atnumber = i;
3140
8
                    s_candidate[nNumCandidates].type = s_type;
3141
8
                    s_candidate[nNumCandidates].subtype = s_subtype;
3142
8
                    s_candidate[nNumCandidates].endpoint = at[i].endpoint;
3143
8
                    nNumCandidates++;
3144
8
                    nNumOtherCandidates += ( 1 == s_type );
3145
8
                    nNumPOnlyCandidates += ( 2 == s_type );
3146
8
                    s_subtype_all |= s_subtype;
3147
                    /*i1 = i;*/ /* save a representative of a tautomeric group */
3148
8
                }
3149
112k
            } /* for */
3150
3151
              /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */
3152
110k
            cond1 = s_subtype_all & SALT_ACCEPTOR;
3153
110k
            cond2a = t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O;
3154
110k
            cond2b = t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE;
3155
110k
            cond2c = t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT;
3156
110k
            if (cond2a || cond2b || cond2c)
3157
0
            {
3158
0
                cond2 = !( s_subtype_all & ( SALT_DONOR_Neg | SALT_DONOR_H ) );
3159
0
            }
3160
110k
            else
3161
110k
            {
3162
110k
                cond2 = !( s_subtype_all & SALT_DONOR_Neg ) || nNumOtherCandidates == nNumCandidates;
3163
110k
            }
3164
110k
            if (nNumCandidates <= 1 || !cond1 || cond2
3165
                 /*(
3166
                 ( cond2a || cond2b    || cond2c )
3167
                 ?        !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H))
3168
                 :        ( !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates) ) */
3169
110k
                 )
3170
110k
            {
3171
110k
                s_group_info->num_candidates = -1; /* no candidate exists */
3172
110k
                goto quick_exit;
3173
110k
            }
3174
0
            if (!( s_subtype_all & ( SALT_DONOR_Neg ) ))
3175
0
            {
3176
0
                t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE;
3177
0
            }
3178
0
        }
3179
0
        else
3180
0
        {
3181
0
            for (i = 0; i < nNumCandidates; i++)
3182
0
            {
3183
0
                i1 = s_candidate[i].atnumber;
3184
0
                if (0 <= ( s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype ) )
3185
0
#if ( INCL_NON_SALT_CANDIDATATES == 1 )
3186
0
                     || 0 < ( s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ ) )
3187
0
#endif
3188
0
                     )
3189
0
                {
3190
0
                    s_candidate[nNumCandidates].type = s_type;
3191
0
                    s_candidate[nNumCandidates].subtype = s_subtype;
3192
0
                    s_candidate[nNumCandidates].endpoint = at[i1].endpoint;
3193
0
                }
3194
0
            }
3195
0
        }
3196
3197
        /* Look for alt paths connecting:
3198
        SALT_DONOR_Neg to SALT_ACCEPTOR  : long distance migration of negative charges
3199
        SALT_DONOR_H   to SALT_ACCEPTOR  : long distance migration of H-atoms
3200
        */
3201
0
        do
3202
0
        {
3203
0
            nNumChanges = 0;
3204
0
            for (i1 = 0; i1 < nNumCandidates; i1++)
3205
0
            {
3206
0
                j1 = s_candidate[i1].atnumber;
3207
0
                for (i2 = i1 + 1; i2 < nNumCandidates; i2++)
3208
0
                {
3209
                    /* prev. approach: do not test if both candidates are not "salt-type". Disabled 2004-03-18
3210
                    if ( s_candidate[i1].type && s_candidate[i2].type )
3211
                    continue;
3212
                    */
3213
0
                    j2 = s_candidate[i2].atnumber;
3214
0
                    if (at[j1].endpoint && at[j1].endpoint == at[j2].endpoint)
3215
0
                    {
3216
0
                        continue;
3217
0
                    }
3218
0
                    for (j = 0; j < 2; j++)
3219
0
                    {
3220
0
                        if (j)
3221
0
                        {
3222
0
                            ii1 = i2; /* candidate 1 (donor)    ordering number */
3223
0
                            ii2 = i1; /* candidate 2 (acceptor) ordering number */
3224
0
                            jj1 = j2; /* candidate 1 (donor)    atom number */
3225
0
                            jj2 = j1; /* candidate 2 (acceptor) atom number */
3226
0
                        }
3227
0
                        else
3228
0
                        {
3229
                            /* transposition */
3230
0
                            ii1 = i1; /* candidate 1 (donor)    ordering number */
3231
0
                            ii2 = i2; /* candidate 2 (acceptor) ordering number */
3232
0
                            jj1 = j1; /* candidate 1 (donor)    atom number     */
3233
0
                            jj2 = j2; /* candidate 2 (acceptor) atom number     */
3234
0
                        }
3235
3236
0
                        if (( s_candidate[ii1].subtype & ( SALT_DONOR_Neg | SALT_DONOR_H ) ) &&
3237
0
                            ( s_candidate[ii2].subtype & SALT_ACCEPTOR ))
3238
0
                        {
3239
                            /****printf("\nChkpt @ %-s:%-d ", __FILE__,__LINE__); fflush(stdout);*/
3240
3241
0
                            ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT );
3242
3243
0
                            num_tested++;
3244
0
                            if (IS_BNS_ERROR( ret ))
3245
0
                            {
3246
0
                                err = ret;
3247
0
                                goto quick_exit;
3248
0
                            }
3249
3250
0
                            if (ret & 1)
3251
0
                            {
3252
0
                                nDelta = ( ret & ~3 ) >> 2;
3253
0
                                nNumChanges += ( ret & 2 );
3254
0
                                for (i = 0; i < 2; i++)
3255
0
                                {
3256
0
                                    jj = i ? jj2 : jj1;
3257
0
                                    AddEndPoint( EndPoint + i, at, jj );
3258
0
                                }
3259
3260
                                /* add/merge taut groups and reinit pBNS in the fly */
3261
0
                                ret = RegisterEndPoints( pCG, t_group_info,
3262
0
                                                         EndPoint, 2, at,
3263
0
                                                         num_atoms, c_group_info,
3264
0
                                                         pBNS );
3265
0
                                if (ret == -1)
3266
0
                                {
3267
0
                                    ret = BNS_PROGRAM_ERR;
3268
0
                                }
3269
0
                                if (ret < 0)
3270
0
                                {
3271
0
                                    err = ret;
3272
0
                                    goto quick_exit;
3273
0
                                }
3274
0
                                if (nDelta)
3275
0
                                {
3276
0
                                    err = BNS_RADICAL_ERR;
3277
0
                                    goto quick_exit;
3278
0
                                }
3279
0
                                nNumChanges += ( ret > 0 );
3280
0
                                break; /* avoid redundant repetition */
3281
0
                            }
3282
0
                        }
3283
0
                    }
3284
0
                }
3285
0
            }
3286
0
            nTotNumChanges += nNumChanges;
3287
0
        } while (num_tested && nNumChanges);
3288
3289
110k
    quick_exit:
3290
110k
        if (!err)
3291
110k
        {
3292
110k
            nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */
3293
110k
            if (s_group_info->num_candidates == 0)
3294
0
            {
3295
                /* first time: initialize */
3296
0
                s_group_info->num_candidates = num_tested ? nNumCandidates : -1; /* no candidate exists */
3297
0
            }
3298
110k
        }
3299
0
        else
3300
0
        {
3301
0
            nTotNumChanges = err;
3302
0
        }
3303
#if ( MAX_LOCAL_TGNUM > 0 )
3304
        if (ptgData != tgData)
3305
        {
3306
            inchi_free( ptgData );
3307
        }
3308
#endif
3309
110k
    }
3310
3311
110k
    return nTotNumChanges;
3312
110k
}
3313
3314
3315
#else
3316
3317
3318
/********************************************************************************************************/
3319
int MarkSaltChargeGroups( CANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info,
3320
                          T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info,
3321
                          struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD )
3322
{
3323
3324
    int nNumChanges = 0, nTotNumChanges = 0;
3325
    if (s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0)
3326
    {
3327
        int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, k, num_tested;
3328
        S_CANDIDATE *s_candidate = s_group_info->s_candidate;
3329
        int          nMaxNumCandidates = s_group_info->max_num_candidates;
3330
        int          nNumCandidates = s_group_info->num_candidates;
3331
        int          nNumOtherCandidates = s_group_info->num_other_candidates;
3332
        int          s_type, s_subtype;
3333
        int          ret, nDelta, nMobile;
3334
        int          s_subtype_all = 0;
3335
        T_ENDPOINT   EndPoint[2];
3336
3337
        if (nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group)
3338
        {
3339
            return 0;
3340
        }
3341
        else
3342
            if (nNumCandidates == 0)
3343
            {
3344
                for (i = 0, nNumCandidates = nNumOtherCandidates = 0; i < num_atoms; i++)
3345
                {
3346
                    if (0 <= ( s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype ) ))
3347
                    {
3348
                        if (nNumCandidates >= nMaxNumCandidates)
3349
                        {
3350
                            return BNS_VERT_EDGE_OVFL;
3351
                        }
3352
                        s_candidate[nNumCandidates].atnumber = i;
3353
                        s_candidate[nNumCandidates].type = s_type;
3354
                        s_candidate[nNumCandidates].subtype = s_subtype;
3355
                        s_candidate[nNumCandidates].endpoint = at[i].endpoint;
3356
                        nNumCandidates++;
3357
                        s_subtype_all |= s_subtype;
3358
                        /*i1 = i;*/ /* save a representative of a tautomeric group */
3359
                    }
3360
#if ( INCL_NON_SALT_CANDIDATATES == 1 )
3361
                    else  /* new */
3362
                        if (0 < ( s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 /* bAccept_O*/ ) ))
3363
                        {
3364
                            if (nNumCandidates >= nMaxNumCandidates)
3365
                            {
3366
                                return BNS_VERT_EDGE_OVFL;
3367
                            }
3368
                            s_candidate[nNumCandidates].atnumber = i;
3369
                            s_candidate[nNumCandidates].type = s_type;
3370
                            s_candidate[nNumCandidates].subtype = s_subtype;
3371
                            s_candidate[nNumCandidates].endpoint = at[i].endpoint;
3372
                            nNumCandidates++;
3373
                            nNumOtherCandidates++;
3374
                            s_subtype_all |= s_subtype;
3375
                        }
3376
#endif
3377
                }
3378
3379
                /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */
3380
                if (nNumCandidates <= 1 || nNumOtherCandidates == nNumCandidates ||
3381
                    ( ( t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O ) ?
3382
                      !( s_subtype_all & ( SALT_DONOR_Neg | SALT_DONOR_H ) ) :
3383
                      !( s_subtype_all & SALT_DONOR_Neg ) ) ||
3384
                     !( s_subtype_all & SALT_ACCEPTOR ))
3385
                {
3386
                    s_group_info->num_candidates = -1; /* no candidate exists */
3387
                    return 0;
3388
                }
3389
                if (!( s_subtype_all & ( SALT_DONOR_Neg ) ))
3390
                {
3391
                    t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE;
3392
                }
3393
            }
3394
            else
3395
            {
3396
                for (i = 0; i < nNumCandidates; i++)
3397
                {
3398
                    i1 = s_candidate[i].atnumber;
3399
                    if (0 <= ( s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype ) )
3400
#if ( INCL_NON_SALT_CANDIDATATES == 1 )
3401
                         || 0 < ( s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ ) )
3402
#endif
3403
                         )
3404
                    {
3405
                        s_candidate[nNumCandidates].type = s_type;
3406
                        s_candidate[nNumCandidates].subtype = s_subtype;
3407
                        s_candidate[nNumCandidates].endpoint = at[i1].endpoint;
3408
                    }
3409
                }
3410
            }
3411
        /* Look for alt paths connecting:
3412
        SALT_DONOR_Neg to SALT_ACCEPTOR  : long distance migration of negative charges
3413
        SALT_DONOR_H   to SALT_ACCEPTOR  : long distance migration of H-atoms
3414
        */
3415
        num_tested = 0;
3416
        do
3417
        {
3418
            nNumChanges = 0;
3419
            for (i1 = 0; i1 < nNumCandidates; i1++)
3420
            {
3421
                j1 = s_candidate[i1].atnumber;
3422
                for (i2 = i1 + 1; i2 < nNumCandidates; i2++)
3423
                {
3424
                    if (s_candidate[i1].type && s_candidate[i2].type)
3425
                        continue; /* both candidates are not "salt-type" */
3426
                    j2 = s_candidate[i2].atnumber;
3427
                    if (at[j1].endpoint && at[j1].endpoint == at[j2].endpoint)
3428
                    {
3429
                        continue;
3430
                    }
3431
                    for (j = 0; j < 2; j++)
3432
                    {
3433
                        if (j)
3434
                        {
3435
                            ii1 = i2; /* candidate 1 (donor)    ordering number */
3436
                            ii2 = i1; /* candidate 2 (acceptor) ordering number */
3437
                            jj1 = j2; /* candidate 1 (donor)    atom number */
3438
                            jj2 = j1; /* candidate 2 (acceptor) atom number */
3439
                        }
3440
                        else
3441
                        {      /* transposition */
3442
                            ii1 = i1; /* candidate 1 (donor)    ordering number */
3443
                            ii2 = i2; /* candidate 2 (acceptor) ordering number */
3444
                            jj1 = j1; /* candidate 1 (donor)    atom number     */
3445
                            jj2 = j2; /* candidate 2 (acceptor) atom number     */
3446
                        }
3447
3448
                        if (( s_candidate[ii1].subtype & ( SALT_DONOR_Neg | SALT_DONOR_H ) ) &&
3449
                            ( s_candidate[ii2].subtype & SALT_ACCEPTOR ))
3450
                        {
3451
                            ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT );
3452
                            num_tested++;
3453
                            if (IS_BNS_ERROR( ret ))
3454
                            {
3455
                                return ret;
3456
                            }
3457
                            if (ret & 1)
3458
                            {
3459
                                nDelta = ( ret & ~3 ) >> 2;
3460
                                nNumChanges += ( ret & 2 );
3461
                                for (i = 0; i < 2; i++)
3462
                                {
3463
                                    jj = i ? jj2 : jj1;
3464
                                    EndPoint[i].nAtomNumber = jj;
3465
                                    EndPoint[i].nEquNumber = 0;
3466
                                    EndPoint[i].nGroupNumber = at[jj].endpoint;
3467
                                    if (at[jj].endpoint)
3468
                                    {
3469
                                        memset( EndPoint[i].num, 0, sizeof( EndPoint[i].num ) );
3470
                                    }
3471
                                    else
3472
                                    {
3473
                                        AddAtom2num( EndPoint[i].num, at, jj, 2 ); /* fill out */
3474
                                        AddAtom2DA( EndPoint[i].num_DA, at, jj, 2 );
3475
                                        /*
3476
                                        nMobile  = EndPoint[i].num[1] = (at[jj].charge == -1);
3477
                                        nMobile  = EndPoint[i].num[0] = at[jj].num_H + nMobile;
3478
                                        for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) {
3479
                                        EndPoint[i].num[T_NUM_NO_ISOTOPIC+k] = at[jj].num_iso_H[NUM_H_ISOTOPES-k-1];
3480
                                        }
3481
                                        */
3482
                                    }
3483
                                }
3484
                                /* add/merge taut groups and reinit pBNS */
3485
                                ret = RegisterEndPoints( pCG, t_group_info,
3486
                                                         EndPoint, 2, at, num_atoms, c_group_info, pBNS );
3487
                                if (ret < 0)
3488
                                {
3489
                                    return ret;
3490
                                }
3491
                                nNumChanges += ( ret > 0 );
3492
                                if (nDelta)
3493
                                {
3494
                                    goto quick_exit;
3495
                                }
3496
                                break; /* avoid redundant repetition */
3497
                            }
3498
                        }
3499
                    }
3500
                }
3501
            }
3502
            nTotNumChanges += nNumChanges;
3503
        } while (num_tested && nNumChanges);
3504
3505
    quick_exit:
3506
        nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */
3507
        if (s_group_info->num_candidates == 0)
3508
        {
3509
            /* first time: initialize */
3510
            s_group_info->num_candidates = num_tested ? nNumCandidates : -1; /* no candidate exists */
3511
        }
3512
    }
3513
    return nTotNumChanges;
3514
}
3515
#endif
3516
3517
3518
/****************************************************************************/
3519
int MergeSaltTautGroups( CANON_GLOBALS *pCG,
3520
                         inp_ATOM *at,
3521
                         int num_atoms,
3522
                         S_GROUP_INFO *s_group_info,
3523
                         T_GROUP_INFO *t_group_info,
3524
                         C_GROUP_INFO *c_group_info,
3525
                         struct BalancedNetworkStructure *pBNS )
3526
110k
{
3527
    /* Count candidates to be connected: exclude pure donors that do not belong to any t-group */
3528
110k
    AT_NUMB    nCurTGroupNumber;
3529
110k
    int        i, j, /*k,*/ ret, iat, /*nMobile,*/ nMinNumEndpoints;
3530
110k
    int        s_subtype_all, s_subtype_taut;
3531
110k
    int        nMaxNumCandidates, nNumCandidates, nNumCandidates2;
3532
110k
    T_ENDPOINT EndPointStackArray[MAX_STACK_ARRAY_LEN]; /* will be reallocated if too short */
3533
110k
    T_ENDPOINT  *EndPoint = EndPointStackArray;
3534
3535
3536
110k
    if (!s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/
3537
110k
         !t_group_info || !t_group_info->t_group || !c_group_info)
3538
0
    {
3539
0
        return 0;
3540
0
    }
3541
110k
    nMinNumEndpoints = 0;
3542
110k
    nMaxNumCandidates = s_group_info->max_num_candidates;
3543
110k
    nCurTGroupNumber = MAX_ATOMS;  /* impossible t-group number */
3544
110k
    s_subtype_all = s_subtype_taut = 0;
3545
3546
    /* Collect tautomeric acidic O and previously non-tautomeric C-OH, C-SH, C-O(-), C-S(-)  */
3547
    /* find whether previously found tautomeric atoms have both mobile H and (-) */
3548
110k
    if (1 || ( s_group_info->num_candidates < 0 ))
3549
110k
    {
3550
        /* Can be only -O(-)  and -OH */
3551
110k
        int          s_type, s_subtype;
3552
110k
        S_CANDIDATE *s_candidate = s_group_info->s_candidate;
3553
222k
        for (i = 0, nNumCandidates = nNumCandidates2 = 0; i < num_atoms; i++)
3554
112k
        {
3555
112k
            s_subtype = 0;
3556
112k
            if (0 == ( s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype ) ) ||
3557
                 /* -C=O or =C-OH, O = S, Se, Te */
3558
3559
                 /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/
3560
112k
                 1 == ( s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ ) ) ||
3561
                 /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */
3562
3563
112k
                 2 == ( s_type = GetOtherSaltType( at, i, &s_subtype ) ) ||
3564
                 /* >C-SH, >C-S(-); S=S,Se,Te */
3565
3566
                 /* other proton donor or acceptor */
3567
112k
                 bHasAcidicHydrogen( at, i ) && ( ( s_type = 3 ), ( s_subtype = SALT_p_DONOR ) ) ||
3568
112k
                 bHasAcidicMinus( at, i ) && ( ( s_type = 3 ), ( s_subtype = SALT_p_ACCEPTOR ) )
3569
112k
                 )
3570
12
            {
3571
3572
12
                if (nNumCandidates >= nMaxNumCandidates)
3573
0
                {
3574
0
                    return BNS_VERT_EDGE_OVFL;
3575
0
                }
3576
12
                if (at[i].endpoint)
3577
0
                {
3578
0
                    s_subtype_taut |= s_subtype;
3579
0
                }
3580
12
                else
3581
12
                {
3582
12
                    if (bDoNotMergeNonTautAtom( at, i ))
3583
2
                    {
3584
2
                        continue; /* ignore non-tautomeric N */
3585
2
                    }
3586
12
                }
3587
10
                if (!( s_subtype & SALT_DONOR_ALL ) ||
3588
10
                    ( s_subtype & SALT_ACCEPTOR ) && !at[i].endpoint)
3589
0
                {
3590
0
                    continue;  /* do not include non-taut acceptors like -C=O */
3591
0
                }
3592
10
                s_candidate[nNumCandidates].atnumber = i;
3593
10
                s_candidate[nNumCandidates].type = s_type;
3594
10
                s_candidate[nNumCandidates].subtype = s_subtype;
3595
10
                s_candidate[nNumCandidates].endpoint = at[i].endpoint;
3596
10
                nNumCandidates++;
3597
10
                s_subtype_all |= s_subtype;
3598
10
            }
3599
112k
        }
3600
3601
        /*
3602
        Forced merging occurs upon:
3603
        ===========================
3604
        (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) or
3605
        (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)
3606
3607
3608
        Allow forced merging in cases:
3609
        {t-groups}  (H, (-)}  {H, (-), t-groups}
3610
3611
3612
        Normal salt merging in cases:
3613
        (H, (-)} {H, (-), t-groups},
3614
3615
        Cannot merge H into t-groups if no (-) is present
3616
        */
3617
110k
        if (( t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O ) ||
3618
110k
            ( t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE ) ||
3619
110k
             ( t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT ))
3620
0
        {
3621
            /* Force merge even though no negative charges are present */
3622
0
            if (nNumCandidates <= 1 ||
3623
0
                ( !( s_subtype_all & SALT_DONOR_Neg2 ) || !( s_subtype_all & SALT_DONOR_H2 ) ) &&
3624
0
                 !t_group_info->num_t_groups)
3625
0
            {
3626
0
                s_group_info->num_candidates = -1; /* no candidate exists */
3627
0
                return 0;
3628
0
            }
3629
0
        }
3630
110k
        else
3631
110k
        {
3632
            /* normal salt mode: merge if both -XH and -X(-) are present */
3633
110k
            if (nNumCandidates <= 1 ||
3634
110k
                ( !( s_subtype_all & SALT_DONOR_Neg2 ) || !( s_subtype_all & SALT_DONOR_H2 ) ))
3635
110k
            {
3636
110k
                s_group_info->num_candidates = -1; /* no candidate exists */
3637
110k
                return 0;
3638
110k
            }
3639
110k
        }
3640
        /* -- old code --
3641
        if ( nNumCandidates <= 1 ||
3642
        (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) ||
3643
        (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ?
3644
        !(s_subtype_all & SALT_DONOR_ALL):
3645
        !(s_subtype_all & SALT_DONOR_Neg2)
3646
        )
3647
        ) {
3648
        s_group_info->num_candidates = -1;
3649
        return 0;
3650
        }
3651
        */
3652
4
        if (!( s_subtype_all & ( SALT_DONOR_Neg2 ) ))
3653
0
        {
3654
0
            t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE;
3655
0
        }
3656
4
        s_group_info->num_candidates = nNumCandidates;
3657
4
    }
3658
3659
12
    for (i = 0; i < s_group_info->num_candidates; i++)
3660
8
    {
3661
8
        iat = s_group_info->s_candidate[i].atnumber;
3662
8
        if (( s_group_info->s_candidate[i].subtype & SALT_ACCEPTOR ) && !at[iat].endpoint)
3663
0
        {
3664
0
            continue; /* should not happen */
3665
0
        }
3666
8
        s_subtype_all |= s_group_info->s_candidate[i].subtype;
3667
8
        if (at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint)
3668
8
        {
3669
8
            nMinNumEndpoints++;
3670
8
        }
3671
8
        nCurTGroupNumber = (int) at[iat].endpoint;
3672
8
    }
3673
4
    if (nMinNumEndpoints <= 1)
3674
0
    {
3675
0
        return 0; /* too few endpoints */
3676
0
    }
3677
3678
    /* Make sure we have enough memory */
3679
4
    if (nMinNumEndpoints > MAX_STACK_ARRAY_LEN)
3680
0
    {
3681
0
        if (!( EndPoint = (T_ENDPOINT *) inchi_calloc( nMinNumEndpoints, sizeof( EndPoint[0] ) ) ))
3682
0
        {
3683
            /*printf("BNS_OUT_OF_RAM-8\n");*/
3684
0
            return BNS_OUT_OF_RAM;
3685
0
        }
3686
0
    }
3687
3688
4
    nCurTGroupNumber = MAX_ATOMS;  /* impossible t-group number */
3689
12
    for (i = j = 0; i < s_group_info->num_candidates; i++)
3690
8
    {
3691
8
        iat = s_group_info->s_candidate[i].atnumber;
3692
8
        if (s_group_info->s_candidate[i].subtype == SALT_ACCEPTOR && !at[iat].endpoint)
3693
0
        {
3694
0
            continue;
3695
0
        }
3696
8
        if (at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint)
3697
8
        {
3698
8
            AddEndPoint( EndPoint + j, at, iat );
3699
8
            j++;
3700
8
        }
3701
8
        nCurTGroupNumber = (int) at[iat].endpoint;
3702
8
    }
3703
3704
4
    ret = RegisterEndPoints( pCG, t_group_info, EndPoint, j, at,
3705
4
                             num_atoms, c_group_info, pBNS );
3706
3707
4
    if (ret == -1)
3708
0
    {
3709
0
        ret = BNS_PROGRAM_ERR;
3710
0
    }
3711
3712
4
    if (EndPoint != EndPointStackArray)
3713
0
    {
3714
0
        inchi_free( EndPoint );
3715
0
    }
3716
3717
4
    return ret;
3718
4
}
3719
3720
3721
/****************************************************************************/
3722
int MakeIsotopicHGroup( inp_ATOM *at,
3723
                        int num_atoms,
3724
                        S_GROUP_INFO *s_group_info,
3725
                        T_GROUP_INFO *t_group_info )
3726
45
{
3727
    /* All tautomeric atoms and all possible H+ donors and acceptors that have H */
3728
45
    int        i, j, k, n, bHasH, tg, nError = 0;
3729
45
    int        s_subtype_all, s_subtype_taut;
3730
45
    int        nMaxNumCandidates, nNumCandidates, nNumNonTautCandidates;
3731
3732
3733
45
    if (!s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/
3734
45
         !t_group_info || !t_group_info->t_group)
3735
0
    {
3736
0
        return 0;
3737
0
    }
3738
45
    nMaxNumCandidates = s_group_info->max_num_candidates;
3739
45
    s_subtype_all = s_subtype_taut = 0;
3740
45
    memset( t_group_info->num_iso_H, 0, sizeof( t_group_info->num_iso_H ) );
3741
45
    if (1 || ( s_group_info->num_candidates < 0 ))
3742
45
    {
3743
45
        int s_type, s_subtype;
3744
45
        S_CANDIDATE *s_candidate = s_group_info->s_candidate;
3745
1.35k
        for (i = 0, nNumCandidates = nNumNonTautCandidates = 0; i < num_atoms; i++)
3746
1.31k
        {
3747
1.31k
            s_subtype = 0;
3748
1.31k
            s_type = 0;
3749
1.31k
            if (at[i].endpoint)
3750
4
            {
3751
4
                if (( tg = t_group_info->tGroupNumber[at[i].endpoint] ) &&
3752
4
                     at[i].endpoint == t_group_info->t_group[tg -= 1].nGroupNumber)
3753
4
                {
3754
4
                    bHasH = (int) t_group_info->t_group[tg].num[0] - (int) t_group_info->t_group[tg].num[1];
3755
4
                }
3756
0
                else
3757
0
                {
3758
0
                    nError = BNS_PROGRAM_ERR;
3759
0
                    break;
3760
0
                }
3761
4
            }
3762
1.31k
            else
3763
1.31k
            {
3764
1.31k
                bHasH = (int) at[i].num_H;
3765
1.31k
            }
3766
1.31k
            if (bHasH && at[i].endpoint || /* tautomeric atoms */
3767
                                           /* non-tautomeric heteroatoms that
3768
                                           (a) have H and
3769
                                           (b) may be donors of H
3770
                                           therefore may exchange isotopic-non-isotopic H */
3771
1.31k
                 bHasH &&
3772
1.31k
                 ( 0 == ( s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype ) ) ||
3773
                   /* -C=O or =C-OH, O = S, Se, Te */
3774
3775
                   /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/
3776
0
                   1 == ( s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ ) ) ||
3777
                   /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */
3778
3779
0
                   2 == ( s_type = GetOtherSaltType( at, i, &s_subtype ) ) ||
3780
                   /* >C-SH, >C-S(-); S=S,Se,Te */
3781
3782
                   /* other proton donor or acceptor */
3783
0
                   bHasAcidicHydrogen( at, i ) && ( ( s_type = 3 ), ( s_subtype = SALT_p_DONOR ) ) ||
3784
0
                   bHasAcidicMinus( at, i ) && ( ( s_type = 3 ), ( s_subtype = SALT_p_ACCEPTOR ) ) ||
3785
0
                   bHasOtherExchangableH( at, i ) && ( ( s_type = 3 ), ( s_subtype = SALT_DONOR_H ) ) )
3786
3787
1.31k
                 )
3788
4
            {
3789
4
                if (nNumCandidates >= nMaxNumCandidates)
3790
0
                {
3791
0
                    return BNS_VERT_EDGE_OVFL;
3792
0
                }
3793
4
                s_candidate[nNumCandidates].atnumber = i;
3794
4
                s_candidate[nNumCandidates].type = s_type;
3795
4
                s_candidate[nNumCandidates].subtype = s_subtype;
3796
4
                s_candidate[nNumCandidates].endpoint = at[i].endpoint;
3797
4
                nNumCandidates++;
3798
4
                nNumNonTautCandidates += !at[i].endpoint;
3799
4
                s_subtype_all |= s_subtype;
3800
4
            }
3801
1.31k
        }
3802
3803
45
        if (nError)
3804
0
        {
3805
0
            return nError;
3806
0
        }
3807
3808
45
        if (nNumCandidates > 0)
3809
2
        {
3810
2
            t_group_info->nIsotopicEndpointAtomNumber = (AT_NUMB *) inchi_calloc( nNumNonTautCandidates + 1, sizeof( t_group_info->nIsotopicEndpointAtomNumber[0] ) );
3811
2
            t_group_info->nIsotopicEndpointAtomNumber[0] = nNumNonTautCandidates;
3812
6
            for (i = 0, n = 1; i < nNumCandidates; i++)
3813
4
            {
3814
4
                k = s_candidate[i].atnumber;
3815
4
                if (!at[k].endpoint)
3816
0
                {
3817
0
                    t_group_info->nIsotopicEndpointAtomNumber[n++] = k;
3818
0
                }
3819
16
                for (j = 0; j < NUM_H_ISOTOPES; j++)
3820
12
                {
3821
12
                    t_group_info->num_iso_H[j] += at[k].num_iso_H[j];
3822
12
                }
3823
4
                at[k].cFlags |= AT_FLAG_ISO_H_POINT;
3824
4
            }
3825
2
            t_group_info->nNumIsotopicEndpoints = nNumNonTautCandidates + 1;
3826
2
        }
3827
45
    }
3828
3829
45
    return nNumCandidates;
3830
45
}
3831
3832
3833
/*#else*/ /* } DISCONNECT_SALTS == 0 */
3834
3835
3836
          /**********************************************************************************
3837
          Charges and tautomeric endpoints (N only)
3838
          **********************************************************************************
3839
3840
          H = number of possibly moveable hydrogen atoms
3841
          C = possibly moveable positive charge
3842
3843
          - = single bond
3844
          = = double bond
3845
          # = triple bond
3846
3847
          +-----------------------------------------------------------------------------+
3848
          |ca-| H    | edges to t-   | 1 bond         |  2 bonds       |  3 bonds    *) |
3849
          |se | C    | and c-groups  | (valence)      |  (valence)     |  (valence)     |
3850
          | # |      | (edges flow)  |                |                |                |
3851
          +---|------+---------------+----------------+----------------+----------------|
3852
          | 1 | H=0  | --  (1)       | =NH     (3)    |  =N-     (3)   |   >N-     (3)  |
3853
          |   | C=0  | ==            |                |                |                |
3854
          +---|------+---------------+----------------+----------------+----------------|
3855
          | 2 | H=1  | ==  (2)       | -NH2    (3)    |  -NH-    (3)   |   none         |
3856
          |   | C=0  | ==            |                |                |                |
3857
          +---|------+---------------+----------------+----------------+----------------|
3858
          | 3 | H=0  | --  (0)       | #NH(+)  (4)    |  =N(+)=  (4) +)|   >N(+)=  (4)  |
3859
          |   | C=1  | --            | (prohibited    |                |                |
3860
          |   |      |               | by edge cap)   |                |                |
3861
          +---|------+---------------+----------------+----------------+----------------|
3862
          | 4 | H=1  | ==  (1)       | =NH2(+) (4)  +)|  =NH(+)- (4) +)|   >NH(+)- (4)  |
3863
          |   | C=1  | --            |                |                |                |
3864
          +---+-------------------------------------------------------------------------+
3865
3866
          *) Cannot be a tautomeric endpoint
3867
3868
          +) The three charged types of atoms [=N(+)=, =NH(+)-, =NH2(+)] should be
3869
          checked for possible H-tautomerism. Other types in the marked by *)
3870
          column should not be checked as long as H(+) exchange is not considered
3871
          tautomeric.
3872
3873
          Other possibilities:  -NH3(+)  >NH2(+)  >N(+)<  cannot be H-tautomeric endpoints.
3874
3875
          Case #1 (H=0, C=0) and #4 (H=1,C=0) is indistinguishable from the
3876
          viewpoint of edges flow and capacities except for flow from N to (+) vertex.
3877
3878
          Without taking precautions H(+) can be transferred
3879
3880
          from =NH2(+)  to =NH,
3881
          from =NH(+)-  to =N-,
3882
          from >NH(+)-  to >N-
3883
3884
          or to any other appropriate atom that has a lone electron pair and bonds
3885
          will not change. In this case no bond must be marked as tautomeric.
3886
3887
          For this reason before attempting to transfer H from one endpoint to
3888
          another the charges on the two atoms should be set to zero by
3889
          forcing zero flow from each of atoms to the (+)-vertices if the
3890
          atoms belong to a c-group.
3891
3892
          **********************************************************************************/
3893
3894
3895
          /****************************************************************************
3896
          Mark Tautomer Groups:
3897
          do not identify positively charged N as endpoints for now
3898
          ****************************************************************************/
3899
int MarkTautomerGroups( CANON_GLOBALS *pCG,
3900
                        inp_ATOM *at,
3901
                        int num_atoms,
3902
                        T_GROUP_INFO *t_group_info,
3903
                        C_GROUP_INFO *c_group_info,
3904
                        struct BalancedNetworkStructure *pBNS,
3905
                        struct BalancedNetworkData *pBD )
3906
110k
{
3907
110k
    int i, j, k, m, endpoint_valence, centerpoint, endpoint, bond_type, nMobile, num_changes = 0, tot_changes = 0;
3908
110k
    T_ENDPOINT EndPoint[MAXVAL];
3909
110k
    T_BONDPOS  BondPos[MAXVAL];
3910
110k
    AT_NUMB    nGroupNumber;
3911
110k
    int        bDiffGroups;
3912
110k
    int  nNumEndPoints, nNumBondPos, nNumPossibleMobile;
3913
110k
    int  bTautBond, bNonTautBond, bAltBond;
3914
110k
    int  nNumDonor, nNumAcceptor, bPossiblyEndpoint;
3915
110k
    T_GROUP *t_group;
3916
110k
    int *pnum_t, max_num_t, bIgnoreIsotopic;
3917
110k
    ENDPOINT_INFO eif1, eif2;
3918
110k
    int nErr = 0;
3919
110k
#define ALLOWED_EDGE(PBNS, IAT,IBOND)  ( !(PBNS) || !(PBNS)->edge || !(PBNS)->vert || !(PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].forbidden)
3920
110k
#define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE)  ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\
3921
110k
    ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE))
3922
3923
3924
110k
    if (!t_group_info || !( t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__ATOMS ))
3925
3
    {
3926
3
        return 0;
3927
3
    }
3928
3929
    /*  Initial t_group allocation */
3930
110k
    if (!t_group_info->t_group && !t_group_info->max_num_t_groups)
3931
110k
    {
3932
110k
        INCHI_MODE bTautFlags = t_group_info->bTautFlags;       /*  save initial setting */
3933
110k
        INCHI_MODE bTautFlagsDone = t_group_info->bTautFlagsDone;   /*  save previous findings, if any */
3934
110k
        TNI       tni = t_group_info->tni;
3935
110k
        AT_NUMB   *tGroupNumber = t_group_info->tGroupNumber;
3936
110k
        bIgnoreIsotopic = t_group_info->bIgnoreIsotopic;
3937
110k
        memset( t_group_info, 0, sizeof( *t_group_info ) );
3938
110k
        t_group_info->bIgnoreIsotopic = bIgnoreIsotopic; /*  restore initial setting */
3939
110k
        t_group_info->bTautFlags = bTautFlags;
3940
110k
        t_group_info->bTautFlagsDone = bTautFlagsDone;
3941
110k
        t_group_info->tni = tni;
3942
110k
        t_group_info->tGroupNumber = tGroupNumber;
3943
110k
        t_group_info->max_num_t_groups = num_atoms / 2 + 1; /*  upper limit */
3944
110k
        if (!( t_group_info->t_group = (T_GROUP*) inchi_calloc( t_group_info->max_num_t_groups, sizeof( t_group[0] ) ) ))
3945
0
        {
3946
0
            return ( t_group_info->max_num_t_groups = -1 ); /*  failed, out of RAM */
3947
0
        }
3948
110k
    }
3949
3950
    /*  Check if t_group_info exists */
3951
110k
    if (!t_group_info->t_group || !t_group_info->max_num_t_groups)
3952
0
    {
3953
0
        return 0;
3954
0
    }
3955
3956
110k
    if (0 > t_group_info->max_num_t_groups)
3957
0
    {
3958
0
        return t_group_info->max_num_t_groups;
3959
0
    }
3960
3961
110k
    pnum_t = &t_group_info->num_t_groups; /*  number of found tautomer endpoint groups */
3962
110k
    t_group = t_group_info->t_group;
3963
110k
    max_num_t = t_group_info->max_num_t_groups;
3964
110k
    bIgnoreIsotopic = t_group_info->bIgnoreIsotopic;
3965
3966
    /*  1-3 tautomers */
3967
222k
    for (i = 0; i < num_atoms; i++)
3968
112k
    {
3969
        /*  Find possible endpoint Z = at[i] */
3970
112k
        if (endpoint_valence = nGetEndpointInfo( at, i, &eif1 ))
3971
12
        {
3972
            /*  1st endpoint candidate found. Find centerpoint candidate */
3973
26
            for (j = 0; j < at[i].valence; j++)
3974
14
            {
3975
14
                bond_type = (int) at[i].bond_type[j] & ~BOND_MARK_ALL;
3976
#if ( FIX_BOND23_IN_TAUT == 1 )
3977
                bond_type = ACTUAL_ORDER( pBNS, i, j, bond_type );
3978
#endif
3979
14
                centerpoint = (int) at[i].neighbor[j];  /*  a centerpoint candidate */
3980
14
                if (( bond_type == BOND_DOUBLE ||
3981
14
                      bond_type == BOND_ALTERN ||
3982
14
                      bond_type == BOND_ALT12NS ||
3983
14
                      bond_type == BOND_TAUTOM ) && is_centerpoint_elem( at[centerpoint].el_number )
3984
14
                     && ALLOWED_EDGE( pBNS, i, j )
3985
14
                     )
3986
0
                {
3987
                    /*  Test a centerpoint candidate. */
3988
                    /*  find all endpoints including at[i] and store them into EndPoint[] */
3989
0
                    nNumPossibleMobile = 0;
3990
0
                    nGroupNumber = (AT_NUMB) num_atoms; /*  greater than any tautomeric group number */
3991
0
                    bDiffGroups = -1;         /*  ignore the first difference */
3992
0
                    nNumDonor = nNumAcceptor = 0;
3993
0
                    for (k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k++)
3994
0
                    {
3995
0
                        endpoint = at[centerpoint].neighbor[k]; /*  endpoint candidate */
3996
0
                        bond_type = (int) at[centerpoint].bond_type[k] & ~BOND_MARK_ALL;
3997
#if ( FIX_BOND23_IN_TAUT == 1 )
3998
                        bond_type = ACTUAL_ORDER( pBNS, centerpoint, k, bond_type );
3999
#endif
4000
0
                        bTautBond =
4001
0
                            bNonTautBond =
4002
0
                            bAltBond =
4003
0
                            bPossiblyEndpoint = 0;
4004
0
                        if (!ALLOWED_EDGE( pBNS, centerpoint, k ))
4005
0
                        {
4006
0
                            continue;
4007
0
                        }
4008
0
                        else
4009
0
                        {
4010
0
                            if (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM)
4011
0
                            {
4012
0
                                bTautBond = 1;
4013
0
#if ( REPLACE_ALT_WITH_TAUT == 1 )
4014
0
                                bAltBond = ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS );
4015
0
#endif
4016
0
                            }
4017
0
                            else
4018
0
                            {
4019
0
                                if (bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE)
4020
0
                                {
4021
0
                                    bNonTautBond = 1;
4022
0
                                }
4023
0
                                else
4024
0
                                {
4025
0
                                    continue;
4026
0
                                }
4027
0
                            }
4028
0
                        }
4029
4030
0
                        if (!( endpoint_valence = nGetEndpointInfo( at, endpoint, &eif1 ) ))
4031
0
                            continue; /*  not an endpoint element or can't have mobile groups */
4032
                                      /*  save information about the found possible tautomeric endpoint */
4033
                                      /*  2 = T_NUM_NO_ISOTOPIC non-isotopic values */
4034
0
                        nMobile =
4035
0
                            AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */
4036
0
                        AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 );
4037
                        /* --- why is isitopic info missing ? -- see below
4038
                        nMobile  = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1);
4039
                        nMobile  = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile;
4040
                        */
4041
0
                        if (bNonTautBond)
4042
0
                        {
4043
0
                            m = ( bond_type == BOND_SINGLE && ( nMobile || at[endpoint].endpoint ) );
4044
0
                            nNumDonor += m;
4045
0
                            bPossiblyEndpoint += m;
4046
0
                            m = ( bond_type == BOND_DOUBLE );
4047
0
                            nNumAcceptor += m;
4048
0
                            bPossiblyEndpoint += m;
4049
0
                        }
4050
0
                        else
4051
0
                        {
4052
                            /*  Tautomeric or alternating bond */
4053
0
                            m = ( 0 != at[endpoint].endpoint || eif1.cDonor );
4054
0
                            nNumDonor += m;
4055
0
                            bPossiblyEndpoint += m;
4056
0
                            m = ( at[endpoint].endpoint ||
4057
0
                                  eif1.cNeutralBondsValence > at[endpoint].valence );
4058
0
                            nNumAcceptor += m;
4059
0
                            bPossiblyEndpoint += m;
4060
0
                        }
4061
0
                        if (!bPossiblyEndpoint)
4062
0
                        {
4063
0
                            continue;
4064
0
                        }
4065
0
                        EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */
4066
0
                        EndPoint[nNumEndPoints].nEquNumber = 0;
4067
0
                        EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB) endpoint;
4068
0
                        if (nGroupNumber != at[endpoint].endpoint)
4069
0
                        {
4070
0
                            bDiffGroups++;
4071
0
                            nGroupNumber = at[endpoint].endpoint;
4072
0
                        }
4073
4074
                        /*  save positions of all, not only possibly tautomeric bonds */
4075
#if ( REPLACE_ALT_WITH_TAUT != 1 )
4076
                        if (bNonTautBond || bAltBond)
4077
                        {
4078
#endif
4079
0
                            BondPos[nNumBondPos].nAtomNumber = (AT_NUMB) centerpoint;
4080
0
                            BondPos[nNumBondPos].neighbor_index = (AT_NUMB) k; /* bond ordering number; used to change bonds to tautomeric only  */
4081
0
                            nNumBondPos++;
4082
#if ( REPLACE_ALT_WITH_TAUT != 1 )
4083
                        }
4084
#endif
4085
                        /*  mobile group is possible if (a) the endpoint has a mobile group or */
4086
                        /*                              (b) the centerpoint is adjacent to another endpoint */
4087
0
                        nNumPossibleMobile += ( nMobile > 0 || at[endpoint].endpoint );
4088
0
                        nNumEndPoints++;
4089
                        /*printf("Found %d %d %d %d\n", centerpoint+1, at[centerpoint].el_number, endpoint+1, at[endpoint].el_number);*/
4090
0
                    }
4091
0
                    if (nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor)
4092
0
                    {
4093
                        /*
4094
                        * a tautomeric group has been found
4095
                        *
4096
                        * at this point:
4097
                        * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group
4098
                        * bDiffGroups  > 0 if at least 2 tautomeric groups are to be merged (one of them can be new)
4099
                        * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group
4100
                        * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen
4101
                        */
4102
4103
0
                        nErr = FindAccessibleEndPoints( pCG, EndPoint,
4104
0
                                                        &nNumEndPoints,
4105
0
                                                        BondPos, &nNumBondPos,
4106
0
                                                        pBNS, pBD, at,
4107
0
                                                        num_atoms, c_group_info,
4108
0
                                                        ALT_PATH_MODE_TAUTOM );
4109
4110
0
                        if (IS_BNS_ERROR( nErr ))
4111
0
                        {
4112
0
                            return nErr;
4113
0
                        }
4114
0
                        nErr = 0;
4115
0
                        if (nNumEndPoints > 0) {
4116
0
                            if (!nGroupNumber || bDiffGroups > 0) {
4117
4118
0
                                num_changes = RegisterEndPoints(pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS);
4119
0
                                if (num_changes == -1) {
4120
0
                                    nErr = CT_TAUCOUNT_ERR;
4121
0
                                }
4122
0
                                if (num_changes < 0) {
4123
0
                                    nErr = num_changes;
4124
0
                                }
4125
0
                                if (nErr)
4126
0
                                    goto exit_function;
4127
0
                                tot_changes += (num_changes>0);
4128
0
                            }
4129
0
                            if (nNumBondPos > 0) {
4130
                                /*  some of the bonds have not been marked as tautomeric yet */
4131
0
                                num_changes = SetTautomericBonds(at, nNumBondPos, BondPos);
4132
0
                                tot_changes += (num_changes>0);
4133
0
                            }
4134
0
                        }
4135
0
                    }
4136
0
                }
4137
14
            }
4138
12
        }
4139
112k
    }
4140
4141
4142
110k
#if ( KETO_ENOL_TAUT == 1 )  /***** post v.1 feature *****/
4143
110k
    if (t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT)
4144
0
    {
4145
        /* 1,3 keto-enol tautomerism */
4146
0
        for (i = 0; i < num_atoms; i++)
4147
0
        {
4148
            /*  Find possible endpoint Z = at[i] */
4149
0
            if (endpoint_valence = nGetEndpointInfo_KET( at, i, &eif1 ))
4150
0
            {
4151
                /*  1st endpoint candidate found. Find centerpoint candidate */
4152
0
                for (j = 0; j < at[i].valence; j++)
4153
0
                {
4154
0
                    bond_type = (int) at[i].bond_type[j] & ~BOND_MARK_ALL;
4155
#if ( FIX_BOND23_IN_TAUT == 1 )
4156
                    bond_type = ACTUAL_ORDER( pBNS, i, j, bond_type );
4157
#endif
4158
0
                    centerpoint = (int) at[i].neighbor[j];  /*  a centerpoint candidate */
4159
0
                    if (( bond_type == BOND_DOUBLE ||
4160
0
                          bond_type == BOND_ALTERN ||
4161
0
                          bond_type == BOND_ALT12NS ||
4162
0
                          bond_type == BOND_TAUTOM ) &&
4163
0
                         is_centerpoint_elem_KET( at[centerpoint].el_number ) &&
4164
0
                         !at[centerpoint].charge && !at[centerpoint].radical &&
4165
                         /* only normal carbon is allowed */
4166
0
                         4 == at[centerpoint].chem_bonds_valence + at[centerpoint].num_H
4167
0
                         && ALLOWED_EDGE( pBNS, i, j ))
4168
0
                    {
4169
0
                        int num_O = 0;
4170
0
                        int num_C = 0;
4171
                        /*  Test a centerpoint candidate. */
4172
                        /*  find all endpoints including at[i] and store them into EndPoint[] */
4173
0
                        nNumPossibleMobile = 0;
4174
0
                        nGroupNumber = (AT_NUMB) num_atoms; /*  greater than any tautomeric group number */
4175
0
                        bDiffGroups = -1;         /*  ignore the first difference */
4176
0
                        nNumDonor = nNumAcceptor = 0;
4177
0
                        for (k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k++)
4178
0
                        {
4179
0
                            endpoint = at[centerpoint].neighbor[k]; /*  endpoint candidate */
4180
0
                            bond_type = (int) at[centerpoint].bond_type[k] & ~BOND_MARK_ALL;
4181
#if ( FIX_BOND23_IN_TAUT == 1 )
4182
                            bond_type = ACTUAL_ORDER( pBNS, centerpoint, k, bond_type );
4183
#endif
4184
0
                            bTautBond =
4185
0
                                bNonTautBond =
4186
0
                                bAltBond =
4187
0
                                bPossiblyEndpoint = 0;
4188
0
                            if (!ALLOWED_EDGE( pBNS, centerpoint, k ))
4189
0
                            {
4190
0
                                continue;
4191
0
                            }
4192
0
                            else
4193
0
                            {
4194
0
                                if (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM)
4195
0
                                {
4196
0
                                    bTautBond = 1;
4197
0
#if ( REPLACE_ALT_WITH_TAUT == 1 )
4198
0
                                    bAltBond = ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS );
4199
0
#endif
4200
0
                                }
4201
0
                                else
4202
0
                                {
4203
0
                                    if (bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE)
4204
0
                                    {
4205
0
                                        bNonTautBond = 1;
4206
0
                                    }
4207
0
                                    else
4208
0
                                    {
4209
0
                                        continue;
4210
0
                                    }
4211
0
                                }
4212
0
                            }
4213
4214
0
                            if (!( endpoint_valence = nGetEndpointInfo_KET( at, endpoint, &eif2 ) ))
4215
0
                            {
4216
0
                                continue;
4217
0
                            }
4218
                            /*
4219
                            if ( 3 != eif1.cKetoEnolCode + eif2.cKetoEnolCode && endpoint != i )
4220
                            continue;
4221
                            */
4222
                            /*  save information about the found possible tautomeric endpoint */
4223
                            /*  2 = T_NUM_NO_ISOTOPIC non-isotopic values */
4224
0
                            nMobile =
4225
0
                                AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */
4226
0
                            AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 );
4227
                            /* --- why is isitopic info missing ? -- see below
4228
                            nMobile  = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1);
4229
                            nMobile  = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile;
4230
                            */
4231
0
                            if (bNonTautBond)
4232
0
                            {
4233
0
                                m = ( bond_type == BOND_SINGLE && ( nMobile || at[endpoint].endpoint ) );
4234
0
                                nNumDonor += m;
4235
0
                                bPossiblyEndpoint += m;
4236
0
                                m = ( bond_type == BOND_DOUBLE );
4237
0
                                nNumAcceptor += m;
4238
0
                                bPossiblyEndpoint += m;
4239
0
                            }
4240
0
                            else
4241
0
                            {
4242
                                /*  Tautomeric or alternating bond */
4243
0
                                m = ( 0 != at[endpoint].endpoint || eif1.cDonor );
4244
0
                                nNumDonor += m;
4245
0
                                bPossiblyEndpoint += m;
4246
0
                                m = ( at[endpoint].endpoint ||
4247
0
                                      eif1.cNeutralBondsValence > at[endpoint].valence );
4248
0
                                nNumAcceptor += m;
4249
0
                                bPossiblyEndpoint += m;
4250
0
                            }
4251
0
                            if (!bPossiblyEndpoint)
4252
0
                            {
4253
0
                                continue;
4254
0
                            }
4255
4256
0
                            num_O += ( endpoint_valence == 2 );
4257
0
                            num_C += ( endpoint_valence == 4 );
4258
4259
0
                            EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */
4260
0
                            EndPoint[nNumEndPoints].nEquNumber = 0;
4261
0
                            EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB) endpoint;
4262
0
                            if (nGroupNumber != at[endpoint].endpoint)
4263
0
                            {
4264
0
                                bDiffGroups++;
4265
0
                                nGroupNumber = at[endpoint].endpoint;
4266
0
                            }
4267
4268
                            /*  Save positions of all, not only possibly tautomeric bonds */
4269
#if ( REPLACE_ALT_WITH_TAUT != 1 )
4270
                            if (bNonTautBond || bAltBond)
4271
                            {
4272
#endif
4273
0
                                BondPos[nNumBondPos].nAtomNumber = (AT_NUMB) centerpoint;
4274
0
                                BondPos[nNumBondPos].neighbor_index = (AT_NUMB) k; /* bond ordering number; used to change bonds to tautomeric only  */
4275
0
                                nNumBondPos++;
4276
#if ( REPLACE_ALT_WITH_TAUT != 1 )
4277
                            }
4278
#endif
4279
                            /*  Mobile group is possible if (a) the endpoint has a mobile group or */
4280
                            /*                              (b) the centerpoint is adjacent to another endpoint */
4281
0
                            nNumPossibleMobile += ( nMobile > 0 || at[endpoint].endpoint );
4282
0
                            nNumEndPoints++;
4283
0
                        }
4284
0
                        if (nNumEndPoints > 1 && nNumPossibleMobile &&
4285
0
                             nNumDonor && nNumAcceptor &&
4286
0
                             num_O == 1 && num_C)
4287
0
                        {
4288
                            /*
4289
                            * A tautomeric group has been found
4290
                            *
4291
                            * at this point:
4292
                            * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group
4293
                            * bDiffGroups  > 0 if at least 2 tautomeric groups are to be merged (one of them can be new)
4294
                            * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group
4295
                            * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen
4296
                            */
4297
4298
0
                            nErr = FindAccessibleEndPoints( pCG, EndPoint, &nNumEndPoints, BondPos, &nNumBondPos,
4299
0
                                                            pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM_KET );
4300
4301
0
                            if (IS_BNS_ERROR( nErr ))
4302
0
                            {
4303
0
                                return nErr;
4304
0
                            }
4305
0
                            nErr = 0;
4306
4307
0
                            if (nNumEndPoints > 0)
4308
0
                            {
4309
0
                                if (!nGroupNumber || bDiffGroups > 0)
4310
0
                                {
4311
4312
0
                                    num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS );
4313
0
                                    if (num_changes == -1)
4314
0
                                    {
4315
0
                                        nErr = CT_TAUCOUNT_ERR;
4316
0
                                    }
4317
0
                                    if (num_changes < 0)
4318
0
                                    {
4319
0
                                        nErr = num_changes;
4320
0
                                    }
4321
0
                                    if (nErr)
4322
0
                                    {
4323
0
                                        goto exit_function;
4324
0
                                    }
4325
0
                                    tot_changes += ( num_changes > 0 );
4326
0
                                }
4327
0
                                if (nNumBondPos > 0)
4328
0
                                {
4329
                                    /*  Some of the bonds have not been marked as tautomeric yet */
4330
0
                                    num_changes = SetTautomericBonds( at, nNumBondPos, BondPos );
4331
0
                                    tot_changes += ( num_changes > 0 );
4332
0
                                }
4333
0
                            }
4334
0
                        }
4335
0
                    }
4336
0
                }
4337
0
            }
4338
0
        }
4339
0
    }
4340
110k
#endif  /* KETO_ENOL_TAUT */
4341
4342
110k
#if ( TAUT_OTHER == 1 ) /* { */
4343
110k
    if (!tot_changes)
4344
110k
    {
4345
110k
#define MAX_ALT_PATH_LEN 8
4346
110k
        int nMaxLenDfsPath = MAX_ALT_PATH_LEN;
4347
110k
        int i1, i2;
4348
110k
        AT_RANK *nDfsPathPos = (AT_RANK  *) inchi_calloc( num_atoms, sizeof( nDfsPathPos[0] ) );
4349
110k
        DFS_PATH DfsPath[MAX_ALT_PATH_LEN];
4350
110k
        int      ret;
4351
4352
110k
        if (!nDfsPathPos || !DfsPath)
4353
0
        {
4354
0
            tot_changes = CT_OUT_OF_RAM;  /*   <BRKPT> */
4355
0
            goto free_memory;
4356
0
        }
4357
4358
110k
#if ( TAUT_15_NON_RING      == 1 ) /***** post v.1 feature *****/
4359
110k
        if (t_group_info->bTautFlags & TG_FLAG_1_5_TAUT)
4360
0
        {
4361
            /*  1,5 tautomerism; one of the endpoints should no be on a ring  */
4362
            /*
4363
            O                OH                 O
4364
            ||               |                  ||
4365
            A--pos-          A--pos-            A--pos-
4366
            /   sib-        //   sib-     ?     /   sib-
4367
            C    ly          C    ly            CH   ly
4368
            \\   a     <-->   \   a       <-->   \   a
4369
            B--ring          B--ring            B--ring
4370
            |                ||                 ||
4371
            NH               N                  N
4372
4373
            Note: few recent modifications now allow the terminal N be in a ring, too
4374
            */
4375
0
            for (i1 = 0; i1 < num_atoms; i1++)
4376
0
            {
4377
                /*  Find possible endpoint Z = at[i1] */
4378
0
                if (!( endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) /*||
4379
0
                                                                              at[i1].nNumAtInRingSystem > 1*/)
4380
0
                {
4381
0
                    continue; /*  not a possibly endpoint */
4382
0
                }
4383
4384
0
                if (1)
4385
0
                {
4386
0
                    nNumEndPoints = 0;
4387
0
                    nNumBondPos = 0;
4388
4389
0
                    ret = nGet15TautInAltPath( pCG, at, i1, nDfsPathPos,
4390
0
                                               DfsPath, nMaxLenDfsPath,
4391
0
                                               EndPoint, sizeof( EndPoint ) / sizeof( EndPoint[0] ),
4392
0
                                               BondPos, sizeof( BondPos ) / sizeof( BondPos[0] ),
4393
0
                                               &nNumEndPoints, &nNumBondPos,
4394
0
                                               pBNS, pBD, num_atoms );
4395
4396
0
                    if (ret > 0)
4397
0
                    {
4398
0
                        if (nNumEndPoints)
4399
0
                        {
4400
0
                            num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS );
4401
0
                            if (num_changes == -1)
4402
0
                            {
4403
0
                                nErr = CT_TAUCOUNT_ERR;
4404
0
                            }
4405
0
                            if (num_changes < 0)
4406
0
                            {
4407
0
                                nErr = num_changes;
4408
0
                            }
4409
0
                            if (nErr)
4410
0
                            {
4411
0
                                goto free_memory;
4412
0
                            }
4413
0
                            tot_changes += ( num_changes > 0 );
4414
0
                        }
4415
0
                        if (nNumBondPos)
4416
0
                        {
4417
0
                            tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) );
4418
0
                        }
4419
0
                    }
4420
0
                    else
4421
0
                    {
4422
0
                        if (IS_BNS_ERROR( ret ))
4423
0
                        {
4424
0
                            nErr = ret;
4425
0
                            goto free_memory;
4426
0
                        }
4427
0
                    }
4428
0
                }
4429
0
            }
4430
0
        }
4431
110k
#endif
4432
4433
110k
#if ( TAUT_4PYRIDINOL_RINGS == 1 )
4434
        /*  6-member rings */
4435
        /*
4436
        O              OH             OH
4437
        ||             |              |
4438
        /  \          //  \           /  \\
4439
        ||   ||  <-->  |    ||  <-->  ||   |
4440
        \  /          \\  /           \  //
4441
        NH             N              N
4442
        */
4443
222k
        for (i1 = 0; i1 < num_atoms; i1++)
4444
112k
        {
4445
            /*  Find possible endpoint Z = at[i1] */
4446
112k
            if (3 != ( endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) ||
4447
112k
                 2 != at[i1].valence)
4448
112k
            {
4449
112k
                continue; /*  not a nitrogen atom or a wrong valence */
4450
112k
            }
4451
4452
2
            if (at[i1].nNumAtInRingSystem >= 6)
4453
0
            {
4454
0
                nNumEndPoints = 0;
4455
0
                nNumBondPos = 0;
4456
4457
0
                ret = nGet15TautIn6MembAltRing( pCG, at, i1, nDfsPathPos,
4458
0
                                                DfsPath, nMaxLenDfsPath, EndPoint,
4459
0
                                                sizeof( EndPoint ) / sizeof( EndPoint[0] ),
4460
0
                                                BondPos,
4461
0
                                                sizeof( BondPos ) / sizeof( BondPos[0] ),
4462
0
                                                &nNumEndPoints, &nNumBondPos,
4463
0
                                                pBNS, pBD, num_atoms );
4464
0
                if (ret > 0)
4465
0
                {
4466
0
                    if (nNumEndPoints)
4467
0
                    {
4468
0
                        num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS );
4469
0
                        if (num_changes == -1)
4470
0
                        {
4471
0
                            nErr = CT_TAUCOUNT_ERR;
4472
0
                        }
4473
0
                        if (num_changes < 0)
4474
0
                        {
4475
0
                            nErr = num_changes;
4476
0
                        }
4477
0
                        if (nErr)
4478
0
                        {
4479
0
                            goto free_memory;
4480
0
                        }
4481
0
                        tot_changes += ( num_changes > 0 );
4482
0
                    }
4483
0
                    if (nNumBondPos)
4484
0
                    {
4485
0
                        tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) );
4486
0
                    }
4487
0
                }
4488
0
                else
4489
0
                {
4490
0
                    if (IS_BNS_ERROR( ret ))
4491
0
                    {
4492
0
                        nErr = ret;
4493
0
                        goto free_memory;
4494
0
                    }
4495
0
                }
4496
0
            }
4497
2
        }
4498
110k
#endif /* TAUT_4PYRIDINOL_RINGS */
4499
4500
110k
#if ( TAUT_PYRAZOLE_RINGS == 1 )
4501
        /* 5-member rings:
4502
4503
        Z               Z
4504
        /  \\           //  \
4505
        X     Y  <-->   X     Y
4506
        \\   /          \    //
4507
        N--NH           HN--N
4508
4509
        ^             ^
4510
        search for these NH
4511
        */
4512
        /*  5-member rings (pyrazole derivatives): look for the neighboring N */
4513
222k
        for (i1 = 0; i1 < num_atoms; i1++)
4514
112k
        {
4515
112k
            if (2 == at[i1].valence &&
4516
112k
                 at[i1].nNumAtInRingSystem >= 5 &&
4517
112k
                 3 == ( endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) )
4518
112k
                 )
4519
0
            {
4520
0
                nMobile = at[i1].num_H + ( at[i1].charge == -1 );
4521
0
                for (j = 0; j < at[i1].valence; j++)
4522
0
                {
4523
0
                    int nMobile2, endpoint_valence2;
4524
0
                    i2 = at[i1].neighbor[j];
4525
4526
                    /*  may be important */
4527
0
                    if (i2 >= i1)
4528
0
                    {
4529
0
                        continue; /*  do not try same pair 2 times */
4530
0
                    }
4531
4532
0
                    if (at[i2].nRingSystem != at[i1].nRingSystem)
4533
0
                    {
4534
0
                        continue;
4535
0
                    }
4536
4537
0
                    bond_type = ( at[i1].bond_type[j] & ~BOND_MARK_ALL );
4538
0
                    if (bond_type != BOND_SINGLE &&
4539
0
                         bond_type != BOND_TAUTOM &&
4540
0
                         bond_type != BOND_ALT12NS &&
4541
0
                         bond_type != BOND_ALTERN ||  /* added 1-15-2002 */
4542
0
                         2 != at[i2].valence ||
4543
0
                         3 != ( endpoint_valence2 = nGetEndpointInfo( at, i2, &eif2 ) ))
4544
0
                    {
4545
0
                        continue; /*  not a nitrogen atom or a wrong valence or not a single bond */
4546
0
                    }
4547
0
                    nMobile2 = at[i2].num_H + ( at[i2].charge == -1 );  /*  number of mobile groups */
4548
#if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 )
4549
                    if (at[i1].endpoint && at[i1].endpoint == at[i2].endpoint)
4550
                    {
4551
                        continue; /* atoms already belong to the same t-group */
4552
                    }
4553
#endif
4554
0
                    if (!at[i1].endpoint && !at[i2].endpoint && 1 != nMobile + nMobile2)
4555
0
                    {
4556
0
                        continue;
4557
0
                    }
4558
4559
0
                    ret = nGet12TautIn5MembAltRing( pCG, at, i1, j, nDfsPathPos,
4560
0
                                                    DfsPath, nMaxLenDfsPath,
4561
0
                                                    EndPoint,
4562
0
                                                    sizeof( EndPoint ) / sizeof( EndPoint[0] ),
4563
0
                                                    BondPos,
4564
0
                                                    sizeof( BondPos ) / sizeof( BondPos[0] ),
4565
0
                                                    &nNumEndPoints, &nNumBondPos,
4566
0
                                                    pBNS, pBD, num_atoms );
4567
0
                    if (ret > 0)
4568
0
                    {
4569
0
                        if (nNumEndPoints)
4570
0
                        {
4571
0
                            num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS );
4572
0
                            if (num_changes == -1)
4573
0
                            {
4574
0
                                nErr = CT_TAUCOUNT_ERR;
4575
0
                            }
4576
0
                            if (num_changes < 0)
4577
0
                            {
4578
0
                                nErr = num_changes;
4579
0
                            }
4580
0
                            if (nErr)
4581
0
                                goto free_memory;
4582
0
                            tot_changes += ( num_changes > 0 );
4583
0
                        }
4584
0
                        if (nNumBondPos)
4585
0
                        {
4586
0
                            tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) );
4587
0
                        }
4588
0
                    }
4589
0
                    else
4590
0
                    {
4591
0
                        if (IS_BNS_ERROR( ret ))
4592
0
                        {
4593
0
                            nErr = ret;
4594
0
                            goto free_memory;
4595
0
                        }
4596
0
                    }
4597
0
                }
4598
0
            }
4599
112k
        }
4600
110k
#endif /* TAUT_PYRAZOLE_RINGS */
4601
4602
110k
#if ( TAUT_TROPOLONE_7 == 1 || TAUT_TROPOLONE_5 == 1 ) /* { */
4603
        /********************************************************
4604
        *                                         A  B
4605
        *                                         | ||
4606
        * 7-member rings (tropolones): look for M=Q--R--ZH,
4607
        *                                       ^ ^  ^  ^
4608
        *                               endpoint1 i1 i2 endpoint2
4609
        * where A-Q-R=B belong to a 7-member alt. (except Q-R bond) ring: ..=A-(Q-R)=B-..
4610
        * Bond Q-R should be single or tautomeric or alternating
4611
        * M=Q and R-ZH should be chain (non-ring) bonds
4612
        * Same for 5-member rings
4613
        */
4614
222k
        for (i1 = 0; i1 < num_atoms; i1++)
4615
112k
        {
4616
112k
            if (at[i1].nNumAtInRingSystem >=
4617
112k
#if ( TAUT_TROPOLONE_5 == 1 )
4618
112k
                 5
4619
#else
4620
                 7
4621
#endif
4622
112k
                 &&
4623
112k
                 bIsCenterPointStrict( at, i1 ) &&
4624
112k
#if ( TAUT_RINGS_ATTACH_CHAIN == 1 )
4625
112k
                 at[i1].bCutVertex &&
4626
112k
#endif
4627
112k
                 at[i1].valence == 3 && !at[i1].endpoint)
4628
0
            {
4629
0
                int nMobile1, endpoint1, endpoint1_valence, bond_type1;
4630
0
                int nMobile2, endpoint2, endpoint2_valence, bond_type2;
4631
0
                for (j = 0; j < at[i1].valence; j++)
4632
0
                {
4633
0
                    i2 = at[i1].neighbor[j];
4634
                    /* may be important
4635
                    if ( i2 > i1 )
4636
                    continue;
4637
                    do not try same pair 2 times
4638
                    */
4639
0
                    if (at[i2].nRingSystem != at[i1].nRingSystem ||
4640
0
                         !bIsCenterPointStrict( at, i2 ) ||
4641
0
#if ( TAUT_RINGS_ATTACH_CHAIN == 1 )
4642
0
                         !at[i2].bCutVertex ||
4643
0
#endif
4644
0
                         at[i2].valence != 3 || at[i2].endpoint)
4645
0
                    {
4646
0
                        continue;
4647
0
                    }
4648
0
                    bond_type = ( at[i1].bond_type[j] & ~BOND_MARK_ALL );
4649
0
                    if (bond_type != BOND_SINGLE &&
4650
0
                         bond_type != BOND_TAUTOM &&
4651
0
                         bond_type != BOND_ALT12NS &&
4652
0
                         bond_type != BOND_ALTERN)
4653
0
                    {
4654
0
                        continue; /*  not a single bond between Q-R */
4655
0
                    }
4656
4657
                    /*  Find endpoints */
4658
0
                    for (k = 0; k < at[i1].valence; k++)
4659
0
                    {
4660
0
                        endpoint1 = at[i1].neighbor[k];
4661
0
                        if (endpoint1 == i2)
4662
0
                        {
4663
0
                            continue; /*  j == k */
4664
0
                        }
4665
0
                        if (!( endpoint1_valence = nGetEndpointInfo( at, endpoint1, &eif1 ) ))
4666
0
                        {
4667
0
                            continue; /*  not an endpoint1 element or can't have mobile groups */
4668
0
                        }
4669
0
#if ( TAUT_RINGS_ATTACH_CHAIN == 1 )
4670
0
                        if (at[endpoint1].nRingSystem == at[i1].nRingSystem)
4671
0
                        {
4672
0
                            continue;
4673
0
                        }
4674
0
#endif
4675
0
                        nMobile1 = at[endpoint1].num_H + ( at[endpoint1].charge == -1 );  /*  number of mobile groups */
4676
0
                        if (nMobile1 + at[endpoint1].chem_bonds_valence != endpoint1_valence)
4677
0
                            continue; /*  abnormal endpoint1 valence; ignore. */
4678
0
                        bond_type1 = ( at[i1].bond_type[k] & ~BOND_MARK_ALL );
4679
4680
0
                        if (bond_type1 != BOND_SINGLE &&
4681
0
                             bond_type1 != BOND_DOUBLE &&
4682
0
                             bond_type1 != BOND_TAUTOM &&
4683
0
                             bond_type1 != BOND_ALT12NS &&
4684
0
                             bond_type1 != BOND_ALTERN)
4685
0
                        {
4686
0
                            continue;
4687
0
                        }
4688
0
                        for (m = 0; m < at[i2].valence; m++)
4689
0
                        {
4690
0
                            endpoint2 = at[i2].neighbor[m];
4691
0
                            if (endpoint2 == i1)
4692
0
                            {
4693
0
                                continue;
4694
0
                            }
4695
0
                            if (!( endpoint2_valence = nGetEndpointInfo( at, endpoint2, &eif2 ) ))
4696
0
                            {
4697
0
                                continue; /*  not an endpoint2 element or can't have mobile groups */
4698
0
                            }
4699
0
#if ( TAUT_RINGS_ATTACH_CHAIN == 1 )
4700
0
                            if (at[endpoint2].nRingSystem == at[i2].nRingSystem)
4701
0
                            {
4702
0
                                continue;
4703
0
                            }
4704
0
#endif
4705
0
                            nMobile2 = at[endpoint2].num_H + ( at[endpoint2].charge == -1 );  /*  number of mobile groups */
4706
0
                            bond_type2 = ( at[i2].bond_type[m] & ~BOND_MARK_ALL );
4707
4708
0
                            if (bond_type2 != BOND_SINGLE &&
4709
0
                                 bond_type2 != BOND_DOUBLE &&
4710
0
                                 bond_type2 != BOND_TAUTOM &&
4711
0
                                 bond_type2 != BOND_ALT12NS &&
4712
0
                                 bond_type2 != BOND_ALTERN)
4713
0
                            {
4714
0
                                continue;
4715
0
                            }
4716
4717
                            /*  Final test for possible tautomerism */
4718
0
                            nMobile = 0;
4719
4720
0
                            if (ALLOWED_EDGE( pBNS, i1, k ) && ALLOWED_EDGE( pBNS, i2, m ))
4721
0
                            {
4722
                                /*  Can mobile group move from 1 to 2? */
4723
0
                                nMobile += ( at[endpoint1].endpoint || nMobile1 ) &&  /*  from endpoint1 */
4724
0
                                    ( bond_type1 != BOND_DOUBLE ) &&
4725
4726
0
                                    ( at[endpoint2].endpoint ||          /*  to endpoint2 */
4727
0
                                      eif2.cNeutralBondsValence > at[endpoint2].valence ) &&
4728
0
                                      ( bond_type2 != BOND_SINGLE );
4729
4730
                                /*  Can mobile group move from 2 to 1? */
4731
0
                                nMobile += ( at[endpoint2].endpoint || nMobile2 ) &&  /*  from endpoint2 */
4732
0
                                    ( bond_type2 != BOND_DOUBLE ) && /*changed from BOND_SINGLE 2004-02-26 */
4733
4734
0
                                    ( at[endpoint1].endpoint ||          /*  to endpoint1 */
4735
0
                                      eif1.cNeutralBondsValence > at[endpoint1].valence ) &&
4736
0
                                      ( bond_type1 != BOND_SINGLE );
4737
0
                            }
4738
0
                            if (!nMobile)
4739
0
                            {
4740
0
                                continue;
4741
0
                            }
4742
4743
0
                            if (bond_type1 == bond_type2 &&
4744
0
                                ( bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE ))
4745
0
                            {
4746
0
                                continue;
4747
0
                            }
4748
4749
                            /* -- old --
4750
                            if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint && 1 != nMobile1 + nMobile2 )
4751
                            continue;
4752
                            */
4753
                            /* -- new --
4754
4755
                            if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint ) {
4756
                            if ( !(bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) ||
4757
                            !(bond_type2 == BOND_SINGLE || bond_type2 == BOND_DOUBLE) ) {
4758
                            // at this point bond_type1 != bond_type2
4759
                            continue;
4760
                            }
4761
                            if ( bond_type1 == BOND_SINGLE && !nMobile1 ||
4762
                            bond_type2 == BOND_SINGLE && !nMobile2 ||
4763
                            0 == nMobile1 + nMobile2 ) {
4764
                            continue;
4765
                            }
4766
                            }
4767
                            */
4768
4769
0
#if ( TAUT_TROPOLONE_7 == 1 )
4770
0
                            if (at[i1].nNumAtInRingSystem >= 7)
4771
0
                            {
4772
4773
0
                                ret = nGet14TautIn7MembAltRing( pCG, at, i1,
4774
0
                                                                j, k, m, nDfsPathPos,
4775
0
                                                                DfsPath, nMaxLenDfsPath,
4776
0
                                                                EndPoint,
4777
0
                                                                sizeof( EndPoint ) / sizeof( EndPoint[0] ),
4778
0
                                                                BondPos,
4779
0
                                                                sizeof( BondPos ) / sizeof( BondPos[0] ),
4780
0
                                                                &nNumEndPoints, &nNumBondPos,
4781
0
                                                                pBNS, pBD, num_atoms );
4782
0
                                if (ret > 0)
4783
0
                                {
4784
0
                                    if (nNumEndPoints)
4785
0
                                    {
4786
0
                                        num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS );
4787
0
                                        if (num_changes == -1)
4788
0
                                        {
4789
0
                                            nErr = CT_TAUCOUNT_ERR;
4790
0
                                        }
4791
0
                                        if (num_changes < 0)
4792
0
                                        {
4793
0
                                            nErr = num_changes;
4794
0
                                        }
4795
0
                                        if (nErr)
4796
0
                                        {
4797
0
                                            goto free_memory;
4798
0
                                        }
4799
0
                                        tot_changes += ( num_changes > 0 );
4800
0
                                    }
4801
0
                                    if (nNumBondPos)
4802
0
                                    {
4803
0
                                        tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) );
4804
0
                                    }
4805
0
                                }
4806
0
                                else
4807
0
                                {
4808
0
                                    if (IS_BNS_ERROR( ret ))
4809
0
                                    {
4810
0
                                        nErr = ret;
4811
0
                                        goto free_memory;
4812
0
                                    }
4813
0
                                }
4814
0
                            }
4815
0
#endif
4816
4817
0
#if ( TAUT_TROPOLONE_5 == 1 )
4818
0
                            if (at[i1].nNumAtInRingSystem >= 5)
4819
0
                            {
4820
4821
0
                                ret = nGet14TautIn5MembAltRing( pCG, at, i1, j, k, m, nDfsPathPos,
4822
0
                                                                DfsPath, nMaxLenDfsPath,
4823
0
                                                                EndPoint, sizeof( EndPoint ) / sizeof( EndPoint[0] ),
4824
0
                                                                BondPos, sizeof( BondPos ) / sizeof( BondPos[0] ),
4825
0
                                                                &nNumEndPoints, &nNumBondPos,
4826
0
                                                                pBNS, pBD, num_atoms );
4827
0
                                if (ret > 0)
4828
0
                                {
4829
0
                                    if (nNumEndPoints)
4830
0
                                    {
4831
0
                                        num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS );
4832
0
                                        if (num_changes == -1)
4833
0
                                        {
4834
0
                                            nErr = CT_TAUCOUNT_ERR;
4835
0
                                        }
4836
0
                                        if (num_changes < 0)
4837
0
                                        {
4838
0
                                            nErr = num_changes;
4839
0
                                        }
4840
0
                                        if (nErr)
4841
0
                                        {
4842
0
                                            goto free_memory;
4843
0
                                        }
4844
0
                                        tot_changes += ( num_changes > 0 );
4845
0
                                    }
4846
0
                                    if (nNumBondPos)
4847
0
                                    {
4848
0
                                        tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) );
4849
0
                                    }
4850
0
                                }
4851
0
                                else
4852
0
                                {
4853
0
                                    if (IS_BNS_ERROR( ret ))
4854
0
                                    {
4855
0
                                        nErr = ret;
4856
0
                                        goto free_memory;
4857
0
                                    }
4858
0
                                }
4859
0
                            }
4860
0
#endif
4861
0
                        }
4862
0
                    }
4863
0
                }
4864
0
            }
4865
112k
        }
4866
110k
#endif /* } TAUT_TROPOLONE */
4867
4868
110k
    free_memory:
4869
110k
        if (nDfsPathPos)
4870
110k
        {
4871
110k
            inchi_free( nDfsPathPos );
4872
110k
        }
4873
110k
#undef MAX_ALT_PATH_LEN
4874
110k
    }
4875
110k
#endif  /* } FIND_RING_SYSTEMS */
4876
4877
110k
exit_function:
4878
4879
110k
    return nErr < 0 ? nErr : tot_changes;
4880
110k
}
4881
4882
4883
/****************************************************************************/
4884
int free_t_group_info( T_GROUP_INFO *t_group_info )
4885
384k
{
4886
384k
    if (t_group_info)
4887
384k
    {
4888
384k
        if (t_group_info->t_group)
4889
110k
        {
4890
110k
            inchi_free( t_group_info->t_group );
4891
110k
        }
4892
384k
        if (t_group_info->nEndpointAtomNumber)
4893
47
        {
4894
47
            inchi_free( t_group_info->nEndpointAtomNumber );
4895
47
        }
4896
384k
        if (t_group_info->tGroupNumber)
4897
47
        {
4898
47
            inchi_free( t_group_info->tGroupNumber );
4899
47
        }
4900
384k
        if (t_group_info->nIsotopicEndpointAtomNumber)
4901
4
        {
4902
4
            inchi_free( t_group_info->nIsotopicEndpointAtomNumber );
4903
4
        }
4904
384k
        memset( t_group_info, 0, sizeof( *t_group_info ) );
4905
384k
    }
4906
4907
384k
    return 0;
4908
384k
}
4909
4910
4911
/****************************************************************************/
4912
int make_a_copy_of_t_group_info( T_GROUP_INFO *t_group_info,
4913
                                 T_GROUP_INFO *t_group_info_orig )
4914
5
{
4915
5
    int err = 0, len;
4916
5
    free_t_group_info( t_group_info );
4917
5
    if (t_group_info_orig && t_group_info)
4918
5
    {
4919
5
        if (( len = t_group_info_orig->max_num_t_groups ) > 0)
4920
5
        {
4921
5
            if (t_group_info->t_group =
4922
5
                (T_GROUP*) inchi_malloc( len * sizeof( t_group_info->t_group[0] ) ))
4923
5
            {
4924
5
                memcpy( t_group_info->t_group,
4925
5
                        t_group_info_orig->t_group,
4926
5
                        len * sizeof( t_group_info->t_group[0] ) );
4927
5
            }
4928
0
            else
4929
0
            {
4930
0
                err++;
4931
0
            }
4932
5
        }
4933
5
        if (( len = t_group_info_orig->nNumEndpoints ) > 0)
4934
4
        {
4935
4
            if (t_group_info->nEndpointAtomNumber =
4936
4
                (AT_NUMB*) inchi_malloc( len * sizeof( t_group_info->nEndpointAtomNumber[0] ) ))
4937
4
            {
4938
4
                memcpy( t_group_info->nEndpointAtomNumber,
4939
4
                        t_group_info_orig->nEndpointAtomNumber,
4940
4
                        len * sizeof( t_group_info->nEndpointAtomNumber[0] ) );
4941
4
            }
4942
0
            else
4943
0
            {
4944
0
                err++;
4945
0
            }
4946
4
        }
4947
5
        if (( len = t_group_info_orig->num_t_groups ) > 0)
4948
4
        {
4949
4
            if (t_group_info->tGroupNumber =
4950
4
                (AT_NUMB*) inchi_malloc( len * TGSO_TOTAL_LEN * sizeof( t_group_info->tGroupNumber[0] ) ))
4951
4
            {
4952
4
                memcpy( t_group_info->tGroupNumber,
4953
4
                        t_group_info_orig->tGroupNumber,
4954
4
                        len * TGSO_TOTAL_LEN * sizeof( t_group_info->tGroupNumber[0] ) );
4955
4
            }
4956
0
            else
4957
0
            {
4958
0
                err++;
4959
0
            }
4960
4
        }
4961
5
        if (( len = t_group_info_orig->nNumIsotopicEndpoints ) > 0)
4962
2
        {
4963
2
            if (t_group_info->nIsotopicEndpointAtomNumber =
4964
2
                (AT_NUMB*) inchi_malloc( len * sizeof( t_group_info->nIsotopicEndpointAtomNumber[0] ) ))
4965
2
            {
4966
2
                memcpy( t_group_info->nIsotopicEndpointAtomNumber,
4967
2
                        t_group_info_orig->nIsotopicEndpointAtomNumber,
4968
2
                        len * sizeof( t_group_info->nIsotopicEndpointAtomNumber[0] ) );
4969
2
            }
4970
0
            else
4971
0
            {
4972
0
                err++;
4973
0
            }
4974
2
        }
4975
5
        if (!err)
4976
5
        {
4977
5
            t_group_info->nNumEndpoints = t_group_info_orig->nNumEndpoints;
4978
5
            t_group_info->num_t_groups = t_group_info_orig->num_t_groups;
4979
5
            t_group_info->max_num_t_groups = t_group_info_orig->max_num_t_groups;
4980
5
            t_group_info->bIgnoreIsotopic = t_group_info_orig->bIgnoreIsotopic;
4981
5
            t_group_info->nNumIsotopicEndpoints = t_group_info_orig->nNumIsotopicEndpoints;
4982
5
            t_group_info->tni = t_group_info_orig->tni;
4983
            /*
4984
            t_group_info->nNumRemovedExplicitH       = t_group_info_orig->nNumRemovedExplicitH;
4985
            t_group_info->nNumRemovedProtons         = t_group_info_orig->nNumRemovedProtons;
4986
            t_group_info->bNormalizationFlags        = t_group_info_orig->bNormalizationFlags;
4987
            */
4988
            /*
4989
            t_group_info->bHardAddedRemovedProtons   = t_group_info_orig->bHardAddedRemovedProtons;
4990
            t_group_info->bSimpleAddedRemovedProtons = t_group_info_orig->bSimpleAddedRemovedProtons;
4991
            t_group_info->nNumCanceledCharges        = t_group_info_orig->nNumCanceledCharges;
4992
            */
4993
5
        }
4994
5
        t_group_info->bTautFlags = t_group_info_orig->bTautFlags;
4995
5
        t_group_info->bTautFlagsDone = t_group_info_orig->bTautFlagsDone;
4996
5
    }
4997
4998
5
    return err;
4999
5
}
5000
5001
5002
/****************************************************************************
5003
Set tautomer group isotopic sort keys
5004
****************************************************************************/
5005
int set_tautomer_iso_sort_keys( T_GROUP_INFO *t_group_info )
5006
5
{
5007
5
    T_GROUP       *t_group;
5008
5
    T_GROUP_ISOWT Mult = 1;
5009
5
    int     i, j, num_t_groups, num_iso_t_groups = 0;
5010
5011
5
    if (!t_group_info || !( t_group = t_group_info->t_group ) ||
5012
5
         0 >= ( num_t_groups = t_group_info->num_t_groups ) || t_group_info->nNumIsotopicEndpoints)
5013
3
    {
5014
3
        return 0;
5015
3
    }
5016
5017
4
    for (i = 0; i < num_t_groups; i++)
5018
2
    {
5019
2
        t_group[i].iWeight = 0;
5020
2
        j = T_NUM_ISOTOPIC - 1;
5021
2
        Mult = 1;
5022
2
        do
5023
6
        {
5024
6
            t_group[i].iWeight += Mult * (T_GROUP_ISOWT) t_group[i].num[T_NUM_NO_ISOTOPIC + j];
5025
6
        } while (--j >= 0 && ( Mult *= T_GROUP_ISOWT_MULT ));
5026
5027
2
        num_iso_t_groups += ( t_group[i].iWeight != 0 );
5028
2
    }
5029
5030
2
    return num_iso_t_groups;
5031
5
}
5032
5033
5034
/****************************************************************************
5035
Fill t_group_info with information necessary to fill out tautomer part
5036
of the linear connection table record.
5037
5038
Note: on input, t_group_info should contain information created by MarkTautomerGroups()
5039
No previous t_group_info adjustment due to throwing out disconnected parts of
5040
the chemical structure is needed.
5041
5042
Note2: throws out t_groups containing negative charges only (IGNORE_TGROUP_WITHOUT_H==1)
5043
(leave their tautomeric bonds unchanged)
5044
Note3: removes negative charges from other tautomeric groups
5045
and adjust counts of mobile atoms if permitted         (REMOVE_TGROUP_CHARGE==1)
5046
****************************************************************************/
5047
int CountTautomerGroups( sp_ATOM *at,
5048
                         int num_atoms,
5049
                         T_GROUP_INFO *t_group_info )
5050
110k
{
5051
110k
    int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH;
5052
5053
110k
    AT_NUMB    nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL;
5054
5055
110k
    T_GROUP   *t_group;
5056
110k
    int        num_t;
5057
    /* int bIgnoreIsotopic, max_num_t; */
5058
110k
    AT_NUMB   *nTautomerGroupNumber = NULL;
5059
110k
    AT_NUMB   *nEndpointAtomNumber = NULL;
5060
110k
    AT_NUMB   *tGroupNumber = NULL;
5061
5062
110k
    if (!t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups)
5063
0
    {
5064
0
        return 0; /* empty t-groups */
5065
0
    }
5066
110k
    num_t = t_group_info->num_t_groups;
5067
110k
    t_group = t_group_info->t_group;
5068
    /*
5069
    max_num_t       =  t_group_info->max_num_t_groups;
5070
    bIgnoreIsotopic =  t_group_info->bIgnoreIsotopic;
5071
    */
5072
110k
    num_groups_noH = 0;
5073
5074
    /* the following 2 arrays are to be rebuilt here */
5075
110k
    if (t_group_info->nEndpointAtomNumber)
5076
0
    {
5077
0
        inchi_free( t_group_info->nEndpointAtomNumber );
5078
0
        t_group_info->nEndpointAtomNumber = NULL;
5079
0
    }
5080
110k
    if (t_group_info->tGroupNumber)
5081
0
    {
5082
0
        inchi_free( t_group_info->tGroupNumber );
5083
0
        t_group_info->tGroupNumber = NULL;
5084
0
    }
5085
5086
    /*  Find max_t_group */
5087
110k
    for (i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i++)
5088
4
    {
5089
4
        if (max_t_group < t_group[i].nGroupNumber)
5090
4
            max_t_group = t_group[i].nGroupNumber;
5091
4
    }
5092
    /*  Allocate memory for temp storage of numbers of endpoints  */
5093
110k
    if (max_t_group &&
5094
110k
         !( nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group + 1, sizeof( nTautomerGroupNumber[0] ) ) /*temp*/ ))
5095
0
    {
5096
0
        goto err_exit_function; /*  program error: out of RAM */ /*   <BRKPT> */
5097
0
    }
5098
5099
    /*  Count endpoints for each tautomer group */
5100
222k
    for (i = 0, nNumEndpoints = 0; i < num_atoms; i++)
5101
112k
    {
5102
112k
        if (( j = at[i].endpoint ) == 0)
5103
112k
        {
5104
112k
            continue;
5105
112k
        }
5106
8
        if (j > max_t_group) /*  debug only */
5107
0
        {
5108
0
            goto err_exit_function; /*  program error */ /*   <BRKPT> */
5109
0
        }
5110
8
        nTautomerGroupNumber[j] ++;
5111
8
        nNumEndpoints++;
5112
8
    }
5113
5114
110k
    if (!nNumEndpoints)
5115
110k
    {
5116
110k
        goto exit_function; /*  not a tautomer */
5117
110k
    }
5118
5119
    /*  allocate temporary array */
5120
4
    if (!( nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof( nEndpointAtomNumber[0] ) ) ) ||
5121
4
         !( nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof( nCurrEndpointAtNoPos[0] ) ) /*temp*/ ))
5122
0
    {
5123
0
        goto err_exit_function; /*   program error: out of RAM */ /*   <BRKPT> */
5124
0
    }
5125
5126
    /*
5127
    * Remove missing endpoints from t_group. Since only one
5128
    * disconnected part is processed, some endpoints groups may have disappeared.
5129
    * Mark t_groups containing charges only for subsequent removal
5130
    */
5131
8
    for (i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/)
5132
4
    {
5133
4
        int bNoH = 0, nNumH;
5134
4
        nGroupNumber = t_group[i].nGroupNumber;
5135
8
        for (j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j++)
5136
4
        {
5137
4
            nNumH -= (int) t_group[i].num[j];
5138
4
        }
5139
4
        if (t_group[i].nNumEndpoints != nTautomerGroupNumber[(int) nGroupNumber]
5140
4
#if ( IGNORE_TGROUP_WITHOUT_H == 1 )
5141
4
             || ( bNoH = ( t_group[i].num[0] == t_group[i].num[1] ) )  /* only for (H,-) t-groups; (+) t-groups are not removed */
5142
4
#endif
5143
4
             )
5144
0
        {
5145
0
            if (!nTautomerGroupNumber[(int) nGroupNumber] || bNoH)
5146
0
            {
5147
                /*  The group belongs to another disconnected part of the structure or has only charges */
5148
                /*  Remove the group */
5149
0
                num_t--;
5150
0
                if (i < num_t)
5151
0
                    memmove( t_group + i, t_group + i + 1, ( num_t - i ) * sizeof( t_group[0] ) );
5152
0
                if (bNoH)
5153
0
                {
5154
                    /*  Group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */
5155
0
                    nTautomerGroupNumber[(int) nGroupNumber] = 0;
5156
0
                    num_groups_noH++;
5157
0
                }
5158
                /*i --;*/
5159
0
            }
5160
0
            else
5161
0
            {
5162
                /*  Different number of endpoints */
5163
0
                goto err_exit_function; /*  program error */ /*   <BRKPT> */
5164
0
            }
5165
0
        }
5166
4
        else
5167
4
        {
5168
            /*  Renumber t_group and prepare to renumber at[i].endpoint */
5169
4
            nTautomerGroupNumber[(int) nGroupNumber] =
5170
4
                t_group[i].nGroupNumber = ++nNewGroupNumber; /*  = i+1 */
5171
                                                             /*  get first group atom orig. number position in the nEndpointAtomNumber[] */
5172
                                                             /*  and in the tautomer endpoint canon numbers part of the connection table */
5173
4
            t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] =
5174
4
                i ? ( t_group[i - 1].nFirstEndpointAtNoPos + t_group[i - 1].nNumEndpoints ) : 0;
5175
4
            t_group[i].num[0] = nNumH;
5176
#if ( REMOVE_TGROUP_CHARGE == 1 )
5177
            t_group[i].num[1] = 0;  /* remove only (-) charges */
5178
#endif
5179
                                    /* -- wrong condition. Disabled.
5180
                                    if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only
5181
                                    goto err_exit_function; // program error
5182
                                    }
5183
                                    */
5184
4
            i++;
5185
4
        }
5186
4
    }
5187
4
    if (num_t != nNewGroupNumber)
5188
0
    {
5189
        /*  for debug only */
5190
0
        goto err_exit_function; /*  program error */ /*   <BRKPT> */
5191
0
    }
5192
5193
    /*  Check if any tautomer group was left */
5194
4
    if (!nNewGroupNumber)
5195
0
    {
5196
0
        if (!num_groups_noH)
5197
0
        {
5198
0
            goto err_exit_function; /*  program error: not a tautomer */ /*   <BRKPT> */
5199
0
        }
5200
0
        else
5201
0
        {
5202
0
            goto exit_function;
5203
0
        }
5204
0
    }
5205
5206
    /*
5207
    * An array for tautomer group sorting later, at the time of storing Connection Table
5208
    * Later the sorting consists out of 2 steps:
5209
    * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group
5210
    *    starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1
5211
    * 2) Sort the groups indexes t_group_info->tGroupNumber[]
5212
    */
5213
4
    if (!( tGroupNumber =
5214
4
        (AT_NUMB*) inchi_calloc( nNewGroupNumber*TGSO_TOTAL_LEN, sizeof( tGroupNumber[0] ) ) ))
5215
0
    {
5216
0
        goto err_exit_function; /*  out of RAM */
5217
0
    }
5218
8
    for (i = 0; i < nNewGroupNumber; i++)
5219
4
    {
5220
4
        tGroupNumber[i] = (AT_NUMB) i; /*  initialization: original t_group number = (at[i]->endpoint-1) */
5221
4
    }
5222
    /*
5223
    * Renumber endpoint atoms and save their orig. atom
5224
    * numbers for filling out the tautomer part of the LinearCT.
5225
    * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[]
5226
    */
5227
24
    for (i = 0; i < num_atoms; i++)
5228
20
    {
5229
20
        if (j = (int) at[i].endpoint)
5230
8
        {
5231
8
            j = (int) ( at[i].endpoint = nTautomerGroupNumber[j] ) - 1; /*  new t_group number */
5232
8
            if (j >= 0)
5233
8
            {
5234
                /*  j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */
5235
8
                if (nCurrEndpointAtNoPos[j] >=   /*  debug only */
5236
8
                     t_group[j].nFirstEndpointAtNoPos + t_group[j].nNumEndpoints)
5237
0
                {
5238
0
                    goto err_exit_function; /*  program error */ /*   <BRKPT> */
5239
0
                }
5240
8
                nEndpointAtomNumber[(int) nCurrEndpointAtNoPos[j] ++] = (AT_NUMB) i;
5241
8
            }
5242
0
            else
5243
0
            {
5244
0
                nNumEndpoints--; /*  endpoint has been removed */
5245
0
            }
5246
8
        }
5247
20
    }
5248
4
    t_group_info->num_t_groups = nNewGroupNumber;
5249
4
    t_group_info->nNumEndpoints = nNumEndpoints;
5250
4
    t_group_info->nEndpointAtomNumber = nEndpointAtomNumber;
5251
4
    t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */
5252
4
    inchi_free( nTautomerGroupNumber );
5253
4
    inchi_free( nCurrEndpointAtNoPos );
5254
4
    return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /*  nLenLinearCTTautomer */
5255
5256
0
err_exit_function:
5257
0
    ret = CT_TAUCOUNT_ERR;
5258
5259
110k
exit_function:
5260
5261
    /*  release allocated memory; set "no tautomeric group" */
5262
110k
    if (nEndpointAtomNumber)
5263
0
    {
5264
0
        inchi_free( nEndpointAtomNumber );
5265
0
    }
5266
110k
    if (nTautomerGroupNumber)
5267
0
    {
5268
0
        inchi_free( nTautomerGroupNumber );
5269
0
    }
5270
110k
    if (tGroupNumber)
5271
0
    {
5272
0
        inchi_free( tGroupNumber );
5273
0
    }
5274
110k
    if (nCurrEndpointAtNoPos)
5275
0
    {
5276
0
        inchi_free( nCurrEndpointAtNoPos );
5277
0
    }
5278
110k
    t_group_info->nNumEndpoints = 0;
5279
110k
    t_group_info->num_t_groups = 0;
5280
110k
    if (!ret && ( ( t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT ) ||
5281
110k
                  t_group_info->nNumIsotopicEndpoints > 1 && ( t_group_info->bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ) ) ))
5282
1
    {
5283
1
        ret = 1; /* only protons have been (re)moved or neitralization happened */
5284
1
    }
5285
5286
110k
    return ret;
5287
0
}
5288
5289
5290
#if ( READ_INCHI_STRING == 1 )
5291
#if ( INCLUDE_NORMALIZATION_ENTRY_POINT == 1 )
5292
5293
5294
/****************************************************************************/
5295
int CountTautomerGroupsInpAt( inp_ATOM *at,
5296
                              int num_atoms,
5297
                              T_GROUP_INFO *t_group_info )
5298
{
5299
    int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH;
5300
5301
    AT_NUMB    nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL;
5302
5303
    T_GROUP   *t_group;
5304
    int        num_t;
5305
    /* int bIgnoreIsotopic, max_num_t; */
5306
    AT_NUMB   *nTautomerGroupNumber = NULL;
5307
    AT_NUMB   *nEndpointAtomNumber = NULL;
5308
    AT_NUMB   *tGroupNumber = NULL;
5309
5310
    if (!t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups)
5311
    {
5312
        return 0; /* empty t-groups */
5313
    }
5314
    num_t = t_group_info->num_t_groups;
5315
    t_group = t_group_info->t_group;
5316
    /*
5317
    max_num_t       =  t_group_info->max_num_t_groups;
5318
    bIgnoreIsotopic =  t_group_info->bIgnoreIsotopic;
5319
    */
5320
    num_groups_noH = 0;
5321
5322
    /* the following 2 arrays are to be rebuilt here */
5323
    if (t_group_info->nEndpointAtomNumber)
5324
    {
5325
        inchi_free( t_group_info->nEndpointAtomNumber );
5326
        t_group_info->nEndpointAtomNumber = NULL;
5327
    }
5328
    if (t_group_info->tGroupNumber)
5329
    {
5330
        inchi_free( t_group_info->tGroupNumber );
5331
        t_group_info->tGroupNumber = NULL;
5332
    }
5333
    /*  find max_t_group */
5334
    for (i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i++)
5335
    {
5336
        if (max_t_group < t_group[i].nGroupNumber)
5337
            max_t_group = t_group[i].nGroupNumber;
5338
    }
5339
    /*  allocate memory for temp storage of numbers of endpoints  */
5340
    if (max_t_group &&
5341
         !( nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group + 1, sizeof( nTautomerGroupNumber[0] ) ) /*temp*/ ))
5342
    {
5343
        goto err_exit_function; /*  program error: out of RAM */ /*   <BRKPT> */
5344
    }
5345
5346
    /*  count endpoints for each tautomer group */
5347
    for (i = 0, nNumEndpoints = 0; i < num_atoms; i++)
5348
    {
5349
        if (( j = at[i].endpoint ) == 0)
5350
            continue;
5351
        if (j > max_t_group) /*  debug only */
5352
            goto err_exit_function; /*  program error */ /*   <BRKPT> */
5353
        nTautomerGroupNumber[j] ++;
5354
        nNumEndpoints++;
5355
    }
5356
5357
    if (!nNumEndpoints)
5358
    {
5359
        goto exit_function; /*  not a tautomer */
5360
    }
5361
5362
    /*  allocate temporary array */
5363
    if (!( nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof( nEndpointAtomNumber[0] ) ) ) ||
5364
         !( nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof( nCurrEndpointAtNoPos[0] ) ) /*temp*/ ))
5365
    {
5366
        goto err_exit_function; /*   program error: out of RAM */ /*   <BRKPT> */
5367
    }
5368
    /*
5369
    * Remove missing endpoints from t_group. Since only one
5370
    * disconnected part is processed, some endpoints groups may have disappeared.
5371
    * Mark t_groups containing charges only for subsequent removal
5372
    */
5373
    for (i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/)
5374
    {
5375
        int bNoH = 0, nNumH;
5376
        nGroupNumber = t_group[i].nGroupNumber;
5377
        for (j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j++)
5378
        {
5379
            nNumH -= (int) t_group[i].num[j];
5380
        }
5381
        if (t_group[i].nNumEndpoints != nTautomerGroupNumber[(int) nGroupNumber]
5382
#if ( IGNORE_TGROUP_WITHOUT_H == 1 )
5383
             || ( bNoH = ( t_group[i].num[0] == t_group[i].num[1] ) )  /* only for (H,-) t-groups; (+) t-groups are not removed */
5384
#endif
5385
             )
5386
        {
5387
            if (!nTautomerGroupNumber[(int) nGroupNumber] || bNoH)
5388
            {
5389
                /*  the group belongs to another disconnected part of the structure or has only charges */
5390
                /*  Remove the group */
5391
                num_t--;
5392
                if (i < num_t)
5393
                    memmove( t_group + i, t_group + i + 1, ( num_t - i ) * sizeof( t_group[0] ) );
5394
                if (bNoH)
5395
                {
5396
                    /*  group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */
5397
                    nTautomerGroupNumber[(int) nGroupNumber] = 0;
5398
                    num_groups_noH++;
5399
                }
5400
                /*i --;*/
5401
            }
5402
            else
5403
            {
5404
                /*  different number of endpoints */
5405
                goto err_exit_function; /*  program error */ /*   <BRKPT> */
5406
            }
5407
        }
5408
        else
5409
        {
5410
            /*  renumber t_group and prepare to renumber at[i].endpoint */
5411
            nTautomerGroupNumber[(int) nGroupNumber] =
5412
                t_group[i].nGroupNumber = ++nNewGroupNumber; /*  = i+1 */
5413
                                                             /*  get first group atom orig. number position in the nEndpointAtomNumber[] */
5414
                                                             /*  and in the tautomer endpoint canon numbers part of the connection table */
5415
            t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] =
5416
                i ? ( t_group[i - 1].nFirstEndpointAtNoPos + t_group[i - 1].nNumEndpoints ) : 0;
5417
            t_group[i].num[0] = nNumH;
5418
#if ( REMOVE_TGROUP_CHARGE == 1 )
5419
            t_group[i].num[1] = 0;  /* remove only (-) charges */
5420
#endif
5421
                                    /* -- wrong condition. Disabled.
5422
                                    if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only
5423
                                    goto err_exit_function; // program error
5424
                                    }
5425
                                    */
5426
            i++;
5427
        }
5428
    }
5429
    if (num_t != nNewGroupNumber)
5430
    { /*  for debug only */
5431
        goto err_exit_function; /*  program error */ /*   <BRKPT> */
5432
    }
5433
5434
    /*  check if any tautomer group was left */
5435
    if (!nNewGroupNumber)
5436
    {
5437
        if (!num_groups_noH)
5438
            goto err_exit_function; /*  program error: not a tautomer */ /*   <BRKPT> */
5439
        else
5440
            goto exit_function;
5441
    }
5442
    /*
5443
    * an array for tautomer group sorting later, at the time of storing Connection Table
5444
    * Later the sorting consists out of 2 steps:
5445
    * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group
5446
    *    starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1
5447
    * 2) Sort the groups indexes t_group_info->tGroupNumber[]
5448
    */
5449
    if (!( tGroupNumber =
5450
        (AT_NUMB*) inchi_calloc( nNewGroupNumber*TGSO_TOTAL_LEN, sizeof( tGroupNumber[0] ) ) ))
5451
    {
5452
        goto err_exit_function; /*  out of RAM */
5453
    }
5454
    for (i = 0; i < nNewGroupNumber; i++)
5455
    {
5456
        tGroupNumber[i] = (AT_NUMB) i; /*  initialization: original t_group number = (at[i]->endpoint-1) */
5457
    }
5458
    /*
5459
    * renumber endpoint atoms and save their orig. atom
5460
    * numbers for filling out the tautomer part of the LinearCT.
5461
    * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[]
5462
    */
5463
    for (i = 0; i < num_atoms; i++)
5464
    {
5465
        if (j = (int) at[i].endpoint)
5466
        {
5467
            j = (int) ( at[i].endpoint = nTautomerGroupNumber[j] ) - 1; /*  new t_group number */
5468
            if (j >= 0)
5469
            { /*  j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */
5470
                if (nCurrEndpointAtNoPos[j] >=   /*  debug only */
5471
                     t_group[j].nFirstEndpointAtNoPos + t_group[j].nNumEndpoints)
5472
                {
5473
                    goto err_exit_function; /*  program error */ /*   <BRKPT> */
5474
                }
5475
                nEndpointAtomNumber[(int) nCurrEndpointAtNoPos[j] ++] = (AT_NUMB) i;
5476
            }
5477
            else
5478
            {
5479
                nNumEndpoints--; /*  endpoint has been removed */
5480
            }
5481
        }
5482
    }
5483
    t_group_info->num_t_groups = nNewGroupNumber;
5484
    t_group_info->nNumEndpoints = nNumEndpoints;
5485
    t_group_info->nEndpointAtomNumber = nEndpointAtomNumber;
5486
    t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */
5487
    inchi_free( nTautomerGroupNumber );
5488
    inchi_free( nCurrEndpointAtNoPos );
5489
    return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /*  nLenLinearCTTautomer */
5490
5491
err_exit_function:
5492
    ret = CT_TAUCOUNT_ERR;
5493
exit_function:
5494
    /*  release allocated memory; set "no tautomeric group" */
5495
    if (nEndpointAtomNumber)
5496
    {
5497
        inchi_free( nEndpointAtomNumber );
5498
    }
5499
    if (nTautomerGroupNumber)
5500
    {
5501
        inchi_free( nTautomerGroupNumber );
5502
    }
5503
    if (tGroupNumber)
5504
    {
5505
        inchi_free( tGroupNumber );
5506
    }
5507
    if (nCurrEndpointAtNoPos)
5508
    {
5509
        inchi_free( nCurrEndpointAtNoPos );
5510
    }
5511
    t_group_info->nNumEndpoints = 0;
5512
    t_group_info->num_t_groups = 0;
5513
    if (!ret && ( ( t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT ) ||
5514
                  t_group_info->nNumIsotopicEndpoints > 1 && ( t_group_info->bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ) ) ))
5515
    {
5516
        ret = 1; /* only protons have been (re)moved or neitralization happened */
5517
    }
5518
5519
    return ret;
5520
}
5521
#endif
5522
#endif
5523
5524
5525
/****************************************************************************
5526
Tautomers: Compare for sorting
5527
5528
Compare for sorting Ranks only
5529
Globals: pn_tRankForSort
5530
****************************************************************************/
5531
int CompRankTautomer( const void* a1, const void* a2, void *p )
5532
4
{
5533
4
    AT_RANK *pn_tRankForSort = (AT_RANK *) p;
5534
4
    int ret = (int) pn_tRankForSort[(int) ( *(const AT_RANK*) a1 )] -
5535
4
        (int) pn_tRankForSort[(int) ( *(const AT_RANK*) a2 )];
5536
5537
4
    return ret;
5538
4
}
5539
5540
5541
/****************************************************************************/
5542
int SortTautomerGroupsAndEndpoints( CANON_GLOBALS *pCG,
5543
                                    T_GROUP_INFO *t_group_info,
5544
                                    int num_atoms,
5545
                                    int num_at_tg,
5546
                                    AT_RANK *nRank )
5547
110k
{
5548
110k
    int i, nFirstEndpointAtNoPos, nNumEndpoints;
5549
110k
    AT_RANK *pn_tRankForSort;
5550
110k
    AT_NUMB  *nEndpointAtomNumber;
5551
110k
    int       num_t_groups = num_at_tg - num_atoms;
5552
110k
    T_GROUP   *t_group = NULL;
5553
5554
    /*  Check if sorting is required */
5555
110k
    if (num_t_groups <= 0 || t_group_info->nNumEndpoints < 2)
5556
110k
    {
5557
110k
        return 0; /*  no tautomer data */
5558
110k
    }
5559
5560
4
    t_group = t_group_info->t_group;
5561
5562
    /*  Sort endpoints within the groups */
5563
8
    for (i = 0; i < num_t_groups; i++)
5564
4
    {
5565
4
        if (t_group[i].nNumEndpoints < 2)
5566
0
        {
5567
0
            continue;  /*  program error; should not happen */ /*   <BRKPT> */
5568
0
        }
5569
5570
        /*  Set globals for sorting */
5571
4
        nFirstEndpointAtNoPos = t_group[i].nFirstEndpointAtNoPos;
5572
4
        nNumEndpoints = t_group[i].nNumEndpoints;
5573
4
        if (nNumEndpoints + nFirstEndpointAtNoPos > t_group_info->nNumEndpoints)
5574
0
        {
5575
            /*  for debug only */
5576
0
            return CT_TAUCOUNT_ERR; /*  program error */ /*   <BRKPT> */
5577
0
        }
5578
4
        nEndpointAtomNumber = t_group_info->nEndpointAtomNumber + (int) nFirstEndpointAtNoPos;
5579
4
        pn_tRankForSort = nRank;
5580
5581
4
        insertions_sort( pn_tRankForSort, nEndpointAtomNumber, nNumEndpoints,
5582
4
                         sizeof( nEndpointAtomNumber[0] ), CompRankTautomer );
5583
4
    }
5584
5585
    /*  Sort the tautomeric groups according to their ranks only
5586
    (that is, ignoring the isotopic composition of the mobile groups and ranks of the endpoints) */
5587
4
    if (t_group_info->num_t_groups > 1)
5588
0
    {
5589
        /*  Set globals for sorting */
5590
        /*  a hack: the ranks of all tautomeric groups are */
5591
        /*  located at nRank[num_atoms..num_at_tg-1] */
5592
0
        pn_tRankForSort = nRank + num_atoms;
5593
        /*  Sort */
5594
        /*  ordering numbers to sort : t_group_info->tGroupNumber; */
5595
5596
0
        insertions_sort( pn_tRankForSort, t_group_info->tGroupNumber,
5597
0
                         num_t_groups, sizeof( t_group_info->tGroupNumber[0] ),
5598
0
                         CompRankTautomer );
5599
0
    }
5600
5601
4
    return t_group_info->num_t_groups;
5602
4
}