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