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