Branch data Line data Source code
1 : : /*
2 : : * SPDX-License-Identifier: MIT
3 : : * Copyright (c) 2024 IUPAC and InChI Trust
4 : : */
5 : :
6 : :
7 : : /*
8 : : Pre-processing related functions
9 : :
10 : : */
11 : :
12 : : #include <stdlib.h>
13 : : #include <string.h>
14 : : #include <stdarg.h>
15 : : #include <errno.h>
16 : : #include <limits.h>
17 : : #include <math.h>
18 : : #include <ctype.h>
19 : :
20 : :
21 : : #include "mode.h"
22 : : #include "ichitime.h"
23 : : #ifndef COMPILE_ANSI_ONLY
24 : : #include <conio.h>
25 : : #endif
26 : : #include "ichimain.h"
27 : : #include "ichi_io.h"
28 : : #include "mol_fmt.h"
29 : : #include "inchi_api.h"
30 : : #include "readinch.h"
31 : : #ifdef TARGET_LIB_FOR_WINCHI
32 : : #include "../../../IChI_lib/src/ichi_lib.h"
33 : : #include "inchi_api.h"
34 : : #else
35 : : #include "inchi_gui.h"
36 : : #endif
37 : : #include "readinch.h"
38 : : #include "inpdef.h"
39 : : #include "ichi_io.h"
40 : :
41 : : #include "bcf_s.h"
42 : :
43 : : /* Local prototypes */
44 : : static int OrigAtData_bCheckUnusualValences(ORIG_ATOM_DATA* orig_at_data,
45 : : int bAddIsoH,
46 : : char* pStrErrStruct,
47 : : int bNoWarnings);
48 : :
49 : : static void OAD_PolymerUnit_RemoveLinkFromCRUChain(int at1, int at2, int* nbonds, int** bonds);
50 : :
51 : : /****************************************************************************
52 : : Check inp_ATOM's for unusual valence
53 : : ****************************************************************************/
54 : 122 : int OrigAtData_bCheckUnusualValences(ORIG_ATOM_DATA* orig_at_data,
55 : : int bAddIsoH,
56 : : char* pStrErrStruct,
57 : : int bNoWarnings)
58 : : {
59 : 122 : int i, val, num_found = 0;
60 : : char msg[32];
61 : : int len, num_H;
62 : :
63 [ + - + - ]: 122 : int already_here = (orig_at_data && orig_at_data->num_inp_atoms > 0);
64 : :
65 [ + - ]: 122 : inp_ATOM* at = already_here ? orig_at_data->at : NULL;
66 : :
67 [ + - ]: 122 : if ( at )
68 : : {
69 [ + + ]: 929 : for ( i = 0, num_found = 0; i < orig_at_data->num_inp_atoms; i++ )
70 : : {
71 [ + - ]: 807 : num_H = bAddIsoH ? NUMH(at, i) : at[i].num_H;
72 : :
73 : 807 : val = detect_unusual_el_valence(at[i].el_number,
74 : 807 : at[i].charge,
75 : 807 : at[i].radical,
76 : 807 : at[i].chem_bonds_valence,
77 : : num_H,
78 : 807 : at[i].valence);
79 [ + + ]: 807 : if ( val )
80 : : {
81 : 19 : num_found++;
82 [ + - ]: 19 : if ( !bNoWarnings )
83 : : {
84 : 19 : WarningMessage(pStrErrStruct, "Accepted unusual valence(s):");
85 : : }
86 : 19 : len = sprintf(msg, "%s", at[i].elname);
87 [ + + ]: 19 : if ( at[i].charge )
88 : : {
89 : 2 : len += sprintf(msg + len, "%+d", at[i].charge);
90 : : }
91 [ - + ]: 19 : if ( at[i].radical )
92 : : {
93 [ # # ]: 0 : len += sprintf(msg + len, ",%s", at[i].radical == RADICAL_SINGLET ? "s" :
94 [ # # ]: 0 : at[i].radical == RADICAL_DOUBLET ? "d" :
95 [ # # ]: 0 : at[i].radical == RADICAL_TRIPLET ? "t" : "?");
96 : : }
97 : 19 : len += sprintf(msg + len, "(%d)", val);
98 [ + - ]: 19 : if ( !bNoWarnings )
99 : : {
100 : 19 : WarningMessage(pStrErrStruct, msg);
101 : : }
102 : : }
103 : : }
104 : : }
105 : :
106 : 122 : return num_found;
107 : : }
108 : :
109 : :
110 : : /****************************************************************************
111 : : Make a copy of ORIG_ATOM_DATA
112 : : ****************************************************************************/
113 : 124 : int OrigAtData_Duplicate(ORIG_ATOM_DATA* new_orig_atom,
114 : : ORIG_ATOM_DATA* orig_atom)
115 : : {
116 : 124 : inp_ATOM* at = NULL;
117 : 124 : AT_NUMB* nCurAtLen = NULL;
118 : 124 : AT_NUMB* nOldCompNumber = NULL;
119 : : int k, m, nn;
120 : 124 : int orig_nat = orig_atom->num_inp_atoms;
121 : :
122 : 124 : int ret = -1; /* fail; 0 - OK */
123 : :
124 [ + + ]: 124 : if ( new_orig_atom->at &&
125 [ - + ]: 1 : new_orig_atom->num_inp_atoms >= orig_nat )
126 : : {
127 : 0 : at = new_orig_atom->at;
128 : : }
129 : : else
130 : : {
131 : 124 : at = (inp_ATOM*)inchi_calloc((long long)orig_nat + 1, sizeof(at[0])); /* djb-rwth: cast operator added */
132 [ - + ]: 124 : if ( !at )
133 : : {
134 : 0 : goto exit_function;
135 : : }
136 : : }
137 : :
138 [ + + ]: 124 : if ( new_orig_atom->nOldCompNumber &&
139 [ + - ]: 1 : new_orig_atom->num_components >= orig_atom->num_components )
140 : : {
141 : 1 : nCurAtLen = new_orig_atom->nCurAtLen;
142 : : }
143 : : else
144 : : {
145 : 123 : nCurAtLen = (AT_NUMB*)inchi_calloc((long long)orig_atom->num_components + 1, sizeof(nCurAtLen[0])); /* djb-rwth: cast operator added */
146 [ - + ]: 123 : if ( !nCurAtLen )
147 : : {
148 : 0 : goto exit_function;
149 : : }
150 : : }
151 : :
152 [ + + ]: 124 : if ( new_orig_atom->nCurAtLen &&
153 [ + - ]: 1 : new_orig_atom->num_components >= orig_atom->num_components )
154 : : {
155 : 1 : nOldCompNumber = new_orig_atom->nOldCompNumber;
156 : : }
157 : : else
158 : : {
159 : 123 : nOldCompNumber = (AT_NUMB*)inchi_calloc((long long)orig_atom->num_components + 1,
160 : : sizeof(nOldCompNumber[0])); /* djb-rwth: cast operator added */
161 [ - + ]: 123 : if ( !nOldCompNumber )
162 : : {
163 : 0 : goto exit_function;
164 : : }
165 : : }
166 : :
167 [ - + - + : 124 : if ( at && nCurAtLen && nOldCompNumber )
- + ]
168 : : {
169 : : /* Copy */
170 [ + + ]: 124 : if ( orig_atom->at )
171 : : {
172 : 123 : memcpy(at, orig_atom->at,
173 : : orig_nat * sizeof(new_orig_atom->at[0]));
174 : : }
175 [ + + ]: 124 : if ( orig_atom->nCurAtLen )
176 : : {
177 : 1 : memcpy(nCurAtLen, orig_atom->nCurAtLen,
178 : 1 : orig_atom->num_components * sizeof(nCurAtLen[0]));
179 : : }
180 [ + + ]: 124 : if ( orig_atom->nOldCompNumber )
181 : : {
182 : 1 : memcpy(nOldCompNumber, orig_atom->nOldCompNumber,
183 : 1 : orig_atom->num_components * sizeof(nOldCompNumber[0]));
184 : : }
185 : :
186 : : /* Deallocate */
187 [ + + + - ]: 124 : if ( new_orig_atom->at && new_orig_atom->at != at )
188 : : {
189 [ + - ]: 1 : inchi_free(new_orig_atom->at);
190 : : }
191 [ + + - + ]: 124 : if ( new_orig_atom->nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen )
192 : : {
193 [ # # ]: 0 : inchi_free(new_orig_atom->nCurAtLen);
194 : : }
195 [ + + ]: 124 : if ( new_orig_atom->nOldCompNumber &&
196 [ - + ]: 1 : new_orig_atom->nOldCompNumber != nOldCompNumber )
197 : : {
198 [ # # ]: 0 : inchi_free(new_orig_atom->nOldCompNumber);
199 : : }
200 : :
201 : 124 : *new_orig_atom = *orig_atom;
202 : 124 : new_orig_atom->at = at;
203 : 124 : new_orig_atom->nCurAtLen = nCurAtLen;
204 : 124 : new_orig_atom->nOldCompNumber = nOldCompNumber;
205 : :
206 : : /* Data that are not to be copied */
207 : 124 : new_orig_atom->nNumEquSets = 0;
208 : 124 : memset(new_orig_atom->bSavedInINCHI_LIB, 0, sizeof(new_orig_atom->bSavedInINCHI_LIB)); /* djb-rwth: memset_s C11/Annex K variant? */
209 : 124 : memset(new_orig_atom->bPreprocessed, 0, sizeof(new_orig_atom->bPreprocessed)); /* djb-rwth: memset_s C11/Annex K variant? */
210 : :
211 : :
212 : :
213 : 124 : new_orig_atom->szCoord = NULL;
214 [ + + ]: 124 : if ( orig_atom->szCoord )
215 : : {
216 : 1 : new_orig_atom->szCoord = (MOL_COORD*)inchi_calloc(orig_nat, sizeof(new_orig_atom->szCoord[0]));
217 [ - + ]: 1 : if ( !new_orig_atom->szCoord )
218 : : {
219 : 0 : goto exit_function;
220 : : }
221 : 1 : memcpy(new_orig_atom->szCoord, orig_atom->szCoord, orig_nat * sizeof(new_orig_atom->szCoord[0]));
222 : : }
223 : :
224 : :
225 : : /* Arrays that are not to be copied */
226 : :
227 : 124 : new_orig_atom->nEquLabels = NULL;
228 : 124 : new_orig_atom->nSortedOrder = NULL;
229 : :
230 : 124 : new_orig_atom->polymer = NULL;
231 [ + + ]: 124 : if ( orig_atom->polymer )
232 : : {
233 : : /* Polymer stuff -- deep copy */
234 : 3 : OAD_Polymer* oldp = orig_atom->polymer;
235 : 3 : OAD_Polymer* newp = NULL;
236 : :
237 : 3 : newp = (OAD_Polymer*)inchi_calloc(1, sizeof(OAD_Polymer));
238 [ - + ]: 3 : if ( !newp )
239 : : {
240 [ # # ]: 0 : inchi_free(newp); /* djb-rwth: avoiding memory leak */
241 : 0 : goto exit_function;
242 : : }
243 : 3 : memcpy(newp, orig_atom->polymer, sizeof(OAD_Polymer));
244 : 3 : newp->units = (OAD_PolymerUnit**)inchi_calloc(newp->n, sizeof(OAD_PolymerUnit*)); /* djb-rwth: inchi_calloc must return OAD_PolymerUnit** */
245 [ - + ]: 3 : if ( !newp->units )
246 : : {
247 [ # # ]: 0 : inchi_free(newp); /* djb-rwth: avoiding memory leak */
248 : 0 : goto exit_function;
249 : : }
250 [ + + ]: 6 : for ( k = 0; k < orig_atom->polymer->n; k++ )
251 : : {
252 : 3 : newp->units[k] = OAD_PolymerUnit_CreateCopy(orig_atom->polymer->units[k]);
253 : : }
254 [ - + ]: 3 : if ( oldp->n_pzz > 0 )
255 : : {
256 : 0 : newp->n_pzz = oldp->n_pzz;
257 : 0 : newp->pzz = (int*)inchi_calloc(newp->n_pzz, sizeof(int));
258 [ # # ]: 0 : if ( !newp->pzz )
259 : : {
260 [ # # ]: 0 : inchi_free(newp->units); /* djb-rwth: fixing coverity ID #499546 */
261 [ # # ]: 0 : inchi_free(newp); /* djb-rwth: avoiding memory leak */
262 : 0 : goto exit_function;
263 : : }
264 : 0 : memcpy(newp->pzz, oldp->pzz, newp->n_pzz * sizeof(oldp->pzz[0]));
265 : : }
266 : 3 : new_orig_atom->polymer = newp;
267 : : }
268 : :
269 : 124 : new_orig_atom->v3000 = NULL;
270 [ + + ]: 124 : if ( orig_atom->v3000 )
271 : : {
272 : : /* V3000 features -- deep copy */
273 : 57 : OAD_V3000* new_v3000 = NULL;
274 : 57 : new_v3000 = (OAD_V3000*)inchi_calloc(1, sizeof(OAD_V3000));
275 [ - + ]: 57 : if ( !new_v3000 )
276 : : {
277 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
278 : 0 : goto exit_function;
279 : : }
280 : 57 : memcpy(new_v3000, orig_atom->v3000, sizeof(OAD_V3000));
281 [ + - ]: 57 : if ( orig_atom->v3000->atom_index_orig )
282 : : {
283 : 57 : new_v3000->atom_index_orig = (int*)inchi_calloc(orig_nat, sizeof(int));
284 : : /* if ( NULL==new_v3000->atom_index_orig ) {TREAT_ERR( err, 9001, "Out of RAM"); goto exit_function; } */
285 [ - + ]: 57 : if ( !new_v3000->atom_index_orig )
286 : : {
287 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
288 : 0 : goto exit_function;
289 : : }
290 : 57 : memcpy(new_v3000->atom_index_orig, orig_atom->v3000->atom_index_orig, orig_nat * sizeof(int));
291 : : }
292 [ + - ]: 57 : if ( orig_atom->v3000->atom_index_fin )
293 : : {
294 : 57 : new_v3000->atom_index_fin = (int*)inchi_calloc(orig_nat, sizeof(int));
295 : : /* if ( NULL==new_v3000->atom_index_fin ) {TREAT_ERR( err, 9001, "Out of RAM"); goto exit_function; } */
296 [ - + ]: 57 : if ( !new_v3000->atom_index_fin )
297 : : {
298 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
299 : 0 : goto exit_function;
300 : : }
301 : 57 : memcpy(new_v3000->atom_index_fin, orig_atom->v3000->atom_index_fin, orig_nat * sizeof(int));
302 : : }
303 [ + + + - ]: 57 : if ( orig_atom->v3000->n_haptic_bonds && orig_atom->v3000->lists_haptic_bonds )
304 : : {
305 : 2 : new_v3000->lists_haptic_bonds = (int**)inchi_calloc(orig_atom->v3000->n_haptic_bonds, sizeof(int*));
306 : : /* if ( NULL==new_v3000->lists_haptic_bonds ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exit_function; }*/
307 [ + + ]: 5 : for ( m = 0; m < orig_atom->v3000->n_haptic_bonds; m++ )
308 : : {
309 : 3 : int* lst = NULL;
310 : 3 : int* old_lst = orig_atom->v3000->lists_haptic_bonds[m];
311 : 3 : nn = old_lst[2] + 3;
312 : 3 : lst = new_v3000->lists_haptic_bonds[m] = (int*)inchi_calloc(nn, sizeof(int));
313 [ - + ]: 3 : if ( !lst )
314 : : {
315 [ # # ]: 0 : inchi_free(new_v3000->lists_haptic_bonds); /* djb-rwth: fixing coverity ID #499504 */
316 [ # # ]: 0 : inchi_free(new_v3000->atom_index_orig); /* djb-rwth: fixing coverity ID #499540 */
317 [ # # ]: 0 : inchi_free(new_v3000->atom_index_fin); /* djb-rwth: fixing coverity ID #499613 */
318 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
319 : 0 : goto exit_function;
320 : : }
321 : 3 : memcpy(lst, old_lst, nn * sizeof(int));
322 : : }
323 : : }
324 [ + + + - ]: 57 : if ( orig_atom->v3000->n_steabs && orig_atom->v3000->lists_steabs )
325 : : {
326 : 23 : new_v3000->lists_steabs = (int**)inchi_calloc(orig_atom->v3000->n_steabs, sizeof(int*));
327 : : /* if ( NULL==new_v3000->lists_steabs ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exit_function; }*/
328 [ + + ]: 46 : for ( m = 0; m < orig_atom->v3000->n_steabs; m++ )
329 : : {
330 : 23 : int* lst = NULL;
331 : 23 : int* old_lst = orig_atom->v3000->lists_steabs[m];
332 : 23 : nn = old_lst[1] + 2;
333 : 23 : lst = new_v3000->lists_steabs[m] = (int*)inchi_calloc(nn, sizeof(int));
334 [ - + ]: 23 : if ( !lst )
335 : : {
336 [ # # ]: 0 : inchi_free(new_v3000->lists_haptic_bonds); /* djb-rwth: fixing coverity ID #499504 */
337 [ # # ]: 0 : inchi_free(new_v3000->lists_steabs); /* djb-rwth: fixing coverity ID #499543 */
338 [ # # ]: 0 : inchi_free(new_v3000->atom_index_orig); /* djb-rwth: fixing coverity ID #499540 */
339 [ # # ]: 0 : inchi_free(new_v3000->atom_index_fin); /* djb-rwth: fixing coverity ID #499613 */
340 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
341 : 0 : goto exit_function;
342 : : }
343 : 23 : memcpy(lst, old_lst, nn * sizeof(int));
344 : : }
345 : : }
346 [ + + + - ]: 57 : if ( orig_atom->v3000->n_sterel && orig_atom->v3000->lists_sterel )
347 : : {
348 : 17 : new_v3000->lists_sterel = (int**)inchi_calloc(orig_atom->v3000->n_sterel, sizeof(int*));
349 [ - + ]: 17 : if ( !new_v3000 )
350 : : {
351 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
352 : 0 : goto exit_function;
353 : : }
354 : : /* if ( NULL==new_v3000->lists_sterel ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exit_function; }*/
355 [ + + ]: 48 : for ( m = 0; m < orig_atom->v3000->n_sterel; m++ )
356 : : {
357 : 31 : int* lst = NULL;
358 : 31 : int* old_lst = orig_atom->v3000->lists_sterel[m];
359 : 31 : nn = old_lst[1] + 2;
360 [ + - ]: 31 : if ( new_v3000->lists_sterel ) /* djb-rwth: fixing a NULL pointer dereference */
361 : 31 : lst = new_v3000->lists_sterel[m] = (int*)inchi_calloc(nn, sizeof(int));
362 [ - + ]: 31 : if ( !lst )
363 : : {
364 [ # # ]: 0 : inchi_free(new_v3000->lists_haptic_bonds); /* djb-rwth: fixing coverity ID #499504 */
365 [ # # ]: 0 : inchi_free(new_v3000->lists_steabs); /* djb-rwth: fixing coverity ID #499543 */
366 [ # # ]: 0 : inchi_free(new_v3000->lists_sterel); /* djb-rwth: fixing coverity ID #499504 */
367 [ # # ]: 0 : inchi_free(new_v3000->atom_index_orig); /* djb-rwth: fixing coverity ID #499540 */
368 [ # # ]: 0 : inchi_free(new_v3000->atom_index_fin); /* djb-rwth: fixing coverity ID #499613 */
369 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
370 : 0 : goto exit_function;
371 : : }
372 : 31 : memcpy(lst, old_lst, nn * sizeof(int));
373 : : }
374 : : }
375 [ + + + - ]: 57 : if ( orig_atom->v3000->n_sterac && orig_atom->v3000->lists_sterac )
376 : : {
377 : 26 : new_v3000->lists_sterac = (int**)inchi_calloc(orig_atom->v3000->n_sterac, sizeof(int*));
378 : : /* if ( NULL==new_v3000->lists_sterac ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exit_function; }*/
379 [ + - ]: 26 : if ( new_v3000->lists_sterac ) /* djb-rwth: fixing a NULL pointer dereference */
380 : : {
381 [ + + ]: 73 : for ( m = 0; m < orig_atom->v3000->n_sterac; m++ )
382 : : {
383 : 47 : int* lst = NULL;
384 : 47 : int* old_lst = orig_atom->v3000->lists_sterac[m];
385 : 47 : nn = old_lst[1] + 2;
386 : 47 : lst = new_v3000->lists_sterac[m] = (int*)inchi_calloc(nn, sizeof(int));
387 [ - + ]: 47 : if ( !lst )
388 : : {
389 [ # # ]: 0 : inchi_free(new_v3000->lists_haptic_bonds); /* djb-rwth: fixing coverity ID #499504 */
390 [ # # ]: 0 : inchi_free(new_v3000->lists_steabs); /* djb-rwth: fixing coverity ID #499543 */
391 [ # # ]: 0 : inchi_free(new_v3000->lists_sterel); /* djb-rwth: fixing coverity ID #499504 */
392 [ # # ]: 0 : inchi_free(new_v3000->lists_sterac); /* djb-rwth: fixing coverity ID #499575 */
393 [ # # ]: 0 : inchi_free(new_v3000->atom_index_orig); /* djb-rwth: fixing coverity ID #499540 */
394 [ # # ]: 0 : inchi_free(new_v3000->atom_index_fin); /* djb-rwth: fixing coverity ID #499613 */
395 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
396 : 0 : goto exit_function;
397 : : }
398 : 47 : memcpy(lst, old_lst, nn * sizeof(int));
399 : : }
400 : : }
401 : : }
402 : :
403 : 57 : new_orig_atom->v3000 = new_v3000;
404 : : }
405 : :
406 : : /* Success */
407 : 124 : ret = 0;
408 : : }
409 : :
410 : 0 : exit_function:
411 : :
412 [ - + ]: 124 : if ( ret != 0 )
413 : : {
414 [ # # # # ]: 0 : if ( at && new_orig_atom->at != at )
415 [ # # ]: 0 : inchi_free(at);
416 [ # # # # ]: 0 : if ( nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen )
417 [ # # ]: 0 : inchi_free(nCurAtLen);
418 [ # # # # ]: 0 : if ( nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber )
419 [ # # ]: 0 : inchi_free(nOldCompNumber);
420 : : }
421 : :
422 : 124 : return ret;
423 : : }
424 : :
425 : :
426 : : /****************************************************************************
427 : : Preprocess the whole structure
428 : :
429 : : The plan is as follows.
430 : :
431 : : 1. Copy orig_inp_data --> prep_inp_data (then work with the latter)
432 : :
433 : : 2. Fix odd things in prep_inp_data
434 : :
435 : : Find whether the structure can be disconnected or is a salt
436 : : - check if needs salt disconnection
437 : : - check if needs metal disconnection
438 : :
439 : : 3. If ( orig_inp_data->bDisconnectSalts ) then
440 : : disconnect salts in prep_inp_data
441 : :
442 : : Mark the (disconnected) components in prep_inp_data
443 : :
444 : : Detect isotopic H on heteroatoms (necessary condition
445 : : for global isotopic tautomerism)
446 : :
447 : : 4. Detect unusual valences (should be called before metal disconnection)
448 : :
449 : : 5. Create metal-disconnected structure if applicable.
450 : : - save reconnected structure in prep_inp_data+1 if requested
451 : : - make Disconnected structure in prep_inp_data
452 : : ****************************************************************************/
453 : 122 : int PreprocessOneStructure(struct tagINCHI_CLOCK* ic,
454 : : STRUCT_DATA* sd,
455 : : INPUT_PARMS* ip,
456 : : ORIG_ATOM_DATA* orig_inp_data,
457 : : ORIG_ATOM_DATA* prep_inp_data)
458 : : {
459 : : int i;
460 : 122 : INCHI_MODE bTautFlags = 0;
461 : 122 : INCHI_MODE bTautFlagsDone = 0;
462 : :
463 : : /* 1. Copy orig_inp_data --> prep_inp_data */
464 : :
465 [ - + ]: 122 : if ( 0 > OrigAtData_Duplicate(prep_inp_data, orig_inp_data) )
466 : : {
467 : 0 : AddErrorMessage(sd->pStrErrStruct, "Out of RAM");
468 : 0 : sd->nStructReadError = 99;
469 : 0 : sd->nErrorType = _IS_FATAL;
470 : 0 : goto exit_function;
471 : : }
472 : :
473 : : #if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) )
474 : : if ( bHasMetalAtom(orig_inp_data) )
475 : : {
476 : : sd->bExtract |= EXTR_HAS_METAL_ATOM;
477 : : }
478 : : #endif
479 : :
480 : : /* 2. Fix odd things in prep_inp_data */
481 : :
482 [ - + ]: 122 : if ( 0 < fix_odd_things(prep_inp_data->num_inp_atoms, prep_inp_data->at, /*0*/ip->bTautFlags & TG_FLAG_FIX_SP3_BUG, ip->bFixNonUniformDraw) )
483 : : {
484 : : /* changed 2010-03-17 DT */
485 [ # # ]: 0 : if ( !ip->bNoWarnings )
486 : : {
487 : 0 : WarningMessage(sd->pStrErrStruct, "Charges were rearranged");
488 : : }
489 [ # # ]: 0 : if ( sd->nErrorType < _IS_WARNING )
490 : : {
491 : 0 : sd->nErrorType = _IS_WARNING;
492 : : }
493 : 0 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE;
494 : : }
495 : :
496 : : #if ( FIX_ADJ_RAD == 1 )
497 : : if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS )
498 : : {
499 : : if ( 0 < FixAdjacentRadicals(prep_inp_data->num_inp_atoms, prep_inp_data->at) )
500 : : {
501 : : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ADJ_RADICALS_DONE;
502 : : }
503 : : }
504 : : #endif
505 : :
506 : : #if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS & EXTR_HAS_FEATURE) )
507 : : if ( bFoundFeature(prep_inp_data->at, prep_inp_data->num_inp_atoms) )
508 : : {
509 : : sd->bExtract |= EXTR_HAS_FEATURE;
510 : : }
511 : : #endif
512 : :
513 : :
514 : : /* Find whether the structure can be disconnected or is a salt */
515 : :
516 : :
517 : : /* Needs salt disconnection? */
518 : :
519 : : /* (@nnuk -> Nauman Ullah Khan) :: In case of Metal Salts with MolecularInorganics parameter we need to skip this pre-processing of Salts */
520 [ + + ]: 122 : if ( ip->bMolecularInorganics )
521 : : {
522 : : ;
523 : : }
524 [ + - ]: 98 : else if ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS )
525 : : {
526 : 98 : prep_inp_data->bDisconnectSalts = (0 < DisconnectSalts(prep_inp_data, 0));
527 : : }
528 : : else
529 : : {
530 : 0 : prep_inp_data->bDisconnectSalts = 0;
531 : : }
532 : :
533 : : /* Needs metal disconnection? */
534 : :
535 [ + - ]: 122 : if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD )
536 : : {
537 : 122 : i = (0 != (ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD));
538 : 122 : bMayDisconnectMetals(prep_inp_data, i, &bTautFlagsDone); /* changes prep_inp_data->bDisconnectCoord */
539 : 122 : sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* whether any disconnection has been rejected because of the metal proper valence */
540 : :
541 : : #if ( bRELEASE_VERSION == 0 )
542 : : if ( i && (bTautFlagsDone & TG_FLAG_CHECK_VALENCE_COORD_DONE) )
543 : : {
544 : : sd->bExtract |= EXTR_METAL_WAS_NOT_DISCONNECTED;
545 : : }
546 : : #endif
547 : : }
548 : : else
549 : : {
550 : 0 : prep_inp_data->bDisconnectCoord = 0;
551 : : }
552 : 122 : orig_inp_data->bDisconnectSalts = prep_inp_data->bDisconnectSalts;
553 : 122 : orig_inp_data->bDisconnectCoord = prep_inp_data->bDisconnectCoord;
554 : :
555 : : /* 3. if( orig_inp_data->bDisconnectSalts ) then
556 : : disconnect salts in prep_inp_data */
557 : :
558 [ + - - + : 122 : if ( (ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS) && prep_inp_data->bDisconnectSalts &&
- - ]
559 : 0 : 0 < (i = DisconnectSalts(prep_inp_data, 1)) ) /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
560 : : {
561 [ # # ]: 0 : if ( !ip->bNoWarnings )
562 : : {
563 : 0 : WarningMessage(sd->pStrErrStruct, "Salt was disconnected");
564 : : }
565 : 0 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_SALTS_DONE;
566 [ # # ]: 0 : if ( sd->nErrorType < _IS_WARNING )
567 : : {
568 : 0 : sd->nErrorType = _IS_WARNING;
569 : : }
570 [ # # ]: 0 : if ( (i = ReconcileAllCmlBondParities(prep_inp_data->at, prep_inp_data->num_inp_atoms, 0)) ) /* djb-rwth: addressing LLVM warning */
571 : : {
572 : : char szErrCode[16];
573 : 0 : sprintf(szErrCode, "%d", i);
574 : 0 : AddErrorMessage(sd->pStrErrStruct, "0D Parities Reconciliation failed:");
575 : 0 : AddErrorMessage(sd->pStrErrStruct, szErrCode);
576 : : }
577 : :
578 : : #if ( bRELEASE_VERSION == 0 )
579 : : sd->bExtract |= EXTR_SALT_WAS_DISCONNECTED;
580 : : #endif
581 : : }
582 : : else
583 : : {
584 : 122 : prep_inp_data->bDisconnectSalts = 0;
585 : : }
586 : :
587 : : /* Mark the (disconnected) components in prep_inp_data */
588 : :
589 : 122 : prep_inp_data->num_components = MarkDisconnectedComponents(prep_inp_data, 0);
590 : :
591 [ - + ]: 122 : if ( prep_inp_data->num_components < 0 )
592 : : {
593 : 0 : AddErrorMessage(sd->pStrErrStruct, "Out of RAM");
594 : 0 : sd->nStructReadError = 99;
595 : 0 : sd->nErrorType = _IS_FATAL;
596 : 0 : goto exit_function;
597 : : }
598 : :
599 : : /* Detect isotopic H on heteroatoms -- necessary condition
600 : : for global isotopic tautomerism */
601 : :
602 [ - + ]: 122 : if ( (i = bNumHeterAtomHasIsotopicH(prep_inp_data->at, prep_inp_data->num_inp_atoms)) ) /* djb-rwth: addressing LLVM warning */
603 : : {
604 [ # # ]: 0 : if ( i & 1 )
605 : : {
606 : 0 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_H_DONE;
607 : : }
608 [ # # ]: 0 : if ( i & 2 )
609 : : {
610 : 0 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE;
611 : : }
612 : : }
613 : :
614 : : /* 4a. Detect unusual valences */
615 : :
616 : 122 : if ( OrigAtData_bCheckUnusualValences(prep_inp_data, 1, sd->pStrErrStruct, ip->bNoWarnings) )
617 : : {
618 : : #if ( bRELEASE_VERSION == 0 )
619 : : sd->bExtract |= EXTR_UNUSUAL_VALENCES;
620 : : #else
621 : : ;
622 : : #endif
623 : : }
624 : :
625 : :
626 : : /* 5. if( orig_inp_data->bDisconnectCoord ) then
627 : : -- copy prep_inp_data --> prep_inp_data+1
628 : : -- disconnect metals in prep_inp_data */
629 : :
630 : : /* (@nnuk -> Nauman Ullah Khan) :: In case of Metals with MolecularInorganics parameter we need to skip this pre-processing of Metals */
631 [ + + ]: 122 : if ( ip->bMolecularInorganics )
632 : : {
633 : 24 : return 0; /* Skipping over current functionality */
634 : : }
635 [ + + ]: 98 : else if ( prep_inp_data->bDisconnectCoord )
636 : : {
637 : :
638 : 4 : prep_inp_data->num_components = MarkDisconnectedComponents(prep_inp_data, 0);
639 [ - + ]: 4 : if ( prep_inp_data->num_components < 0 )
640 : : {
641 : 0 : AddErrorMessage(sd->pStrErrStruct, "Out of RAM");
642 : 0 : sd->nStructReadError = 99;
643 : 0 : sd->nErrorType = _IS_FATAL;
644 : 0 : goto exit_function;
645 : : }
646 : :
647 : : /* Save reconnected structure in prep_inp_data+1 if requested */
648 [ - + ]: 4 : if ( 0 != (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) )
649 : : {
650 [ # # ]: 0 : if ( 0 > OrigAtData_Duplicate(prep_inp_data + 1, prep_inp_data) )
651 : : {
652 : 0 : AddErrorMessage(sd->pStrErrStruct, "Out of RAM");
653 : 0 : sd->nStructReadError = 99;
654 : 0 : sd->nErrorType = _IS_FATAL;
655 : 0 : goto exit_function;
656 : : }
657 : 0 : sd->bTautFlags[INCHI_REC] = sd->bTautFlags[INCHI_BAS];
658 : 0 : sd->bTautFlagsDone[INCHI_REC] = sd->bTautFlagsDone[INCHI_BAS];
659 : : {
660 : : /* Remove "parity undefined in disconnected structure" flag from reconnected structure */
661 : : int k, m; /* djb-rwth: removing redundant variables */
662 : 0 : inp_ATOM* at = (prep_inp_data + 1)->at;
663 : 0 : int num_at = (prep_inp_data + 1)->num_inp_atoms;
664 [ # # ]: 0 : for ( k = 0; k < num_at; k++ )
665 : : {
666 [ # # # # ]: 0 : for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m++ ) /* djb-rwth: removing redundant code */
667 : : {
668 : 0 : at[k].sb_parity[m] &= SB_PARITY_MASK;
669 : : }
670 : : }
671 : : }
672 : : }
673 : :
674 : : /* Make disconnected structure in prep_inp_data */
675 : 4 : i = (0 != (ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD));
676 : :
677 : : /* prep_inp_data->bDisconnectCoord > 1 means add
678 : : prep_inp_data->bDisconnectCoord-1 explicit H atoms */
679 [ + - ]: 4 : if ( 0 < (i = DisconnectMetals(prep_inp_data, i, &bTautFlagsDone)) )
680 : : {
681 [ + - ]: 4 : if ( !ip->bNoWarnings )
682 : : {
683 : 4 : WarningMessage(sd->pStrErrStruct, "Metal was disconnected");
684 : : }
685 : 4 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_COORD_DONE;
686 [ + + ]: 4 : if ( sd->nErrorType < _IS_WARNING )
687 : : {
688 : 3 : sd->nErrorType = _IS_WARNING;
689 : : }
690 : :
691 : : #if ( bRELEASE_VERSION == 0 )
692 : : sd->bExtract |= EXTR_METAL_WAS_DISCONNECTED;
693 : : #endif
694 : :
695 : : /* last parm=1 means find link between unchanged by Metal Disconnection components */
696 : 4 : prep_inp_data->num_components = MarkDisconnectedComponents(prep_inp_data, 1);
697 : :
698 [ - + ]: 4 : if ( prep_inp_data->num_components < 0 )
699 : : {
700 : 0 : AddErrorMessage(sd->pStrErrStruct, "Out of RAM");
701 : 0 : sd->nStructReadError = 99;
702 : 0 : sd->nErrorType = _IS_FATAL;
703 : 0 : goto exit_function;
704 : : }
705 : :
706 : : {
707 : : /* Set parities for the disconnected structure */
708 : : int k, m, p;
709 : 4 : inp_ATOM* at = (prep_inp_data)->at;
710 : 4 : int num_at = (prep_inp_data)->num_inp_atoms;
711 [ + + ]: 29 : for ( k = 0; k < num_at; k++ )
712 : : {
713 [ + - - + ]: 25 : for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p = at[k].sb_parity[m]); m++ )
714 : : {
715 [ # # ]: 0 : if ( p & SB_PARITY_FLAG )
716 : : {
717 : 0 : at[k].sb_parity[m] = (p >> SB_PARITY_SHFT) & SB_PARITY_MASK;
718 : : }
719 : : }
720 : : }
721 : : }
722 : :
723 [ - + ]: 4 : if ( (i = ReconcileAllCmlBondParities(prep_inp_data->at, prep_inp_data->num_inp_atoms, 1)) ) /* djb-rwth: addressing LLVM warning */
724 : : {
725 : : char szErrCode[16];
726 : 0 : sprintf(szErrCode, "%d", i);
727 : 0 : AddErrorMessage(sd->pStrErrStruct, "0D Parities Reconciliation failed:");
728 : 0 : AddErrorMessage(sd->pStrErrStruct, szErrCode);
729 : : }
730 : :
731 : : #if ( REMOVE_ION_PAIRS_DISC_STRU == 1 )
732 [ - + ]: 4 : if ( 0 < remove_ion_pairs(prep_inp_data->num_inp_atoms, prep_inp_data->at) )
733 : : {
734 [ # # ]: 0 : if ( !ip->bNoWarnings )
735 : : {
736 : 0 : WarningMessage(sd->pStrErrStruct, "Charges were rearranged");
737 : : }
738 [ # # ]: 0 : if ( sd->nErrorType < _IS_WARNING )
739 : : {
740 : 0 : sd->nErrorType = _IS_WARNING;
741 : : }
742 : 0 : sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE;
743 : 0 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE;
744 : : }
745 : : #endif
746 : :
747 : : /*
748 : : if prep_inp_data->nOldCompNumber[i] = iINChI+1 > 0 then
749 : : component #(i+1) in prep_inp_data is identical to component #(iINChI+1) in prep_inp_data+1
750 : : */
751 : : }
752 [ # # ]: 0 : else if ( i < 0 )
753 : : {
754 : 0 : AddErrorMessage(sd->pStrErrStruct, "Cannot disconnect metal error");
755 : 0 : sd->nStructReadError = i;
756 : 0 : sd->nErrorType = _IS_ERROR;
757 : 0 : goto exit_function;
758 : : }
759 : : }
760 : : else
761 : : {
762 : : /* Remove "disconnected structure parities" from the structure */
763 : : int k, m; /* djb-rwth: removing redundant variables */
764 : 94 : inp_ATOM* at = (prep_inp_data)->at;
765 : 94 : int num_at = (prep_inp_data)->num_inp_atoms;
766 [ + + ]: 721 : for ( k = 0; k < num_at; k++ )
767 : : {
768 [ + - - + ]: 627 : for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m++ ) /* djb-rwth: removing redundant code */
769 : : {
770 : 0 : at[k].sb_parity[m] &= SB_PARITY_MASK;
771 : : }
772 : : }
773 : : }
774 : :
775 : 94 : exit_function:
776 : :
777 [ + - + - ]: 98 : if ( sd->nErrorType < _IS_ERROR && prep_inp_data )
778 : : {
779 [ - + ]: 98 : if ( 0 < post_fix_odd_things(prep_inp_data->num_inp_atoms, prep_inp_data->at) )
780 : : {
781 [ # # ]: 0 : if ( !ip->bNoWarnings )
782 : : {
783 : 0 : WarningMessage(sd->pStrErrStruct, "Charges were rearranged");
784 : : }
785 [ # # ]: 0 : if ( sd->nErrorType < _IS_WARNING )
786 : : {
787 : 0 : sd->nErrorType = _IS_WARNING;
788 : : }
789 : 0 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE;
790 : : }
791 [ + + ]: 98 : if ( (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) &&
792 [ - + - - ]: 4 : (prep_inp_data + 1)->at && (prep_inp_data + 1)->num_inp_atoms > 0 )
793 : : {
794 [ # # ]: 0 : if ( 0 < post_fix_odd_things((prep_inp_data + 1)->num_inp_atoms, (prep_inp_data + 1)->at) )
795 : : {
796 [ # # ]: 0 : if ( !ip->bNoWarnings )
797 : : {
798 : 0 : WarningMessage(sd->pStrErrStruct, "Charges were rearranged");
799 : : }
800 [ # # ]: 0 : if ( sd->nErrorType < _IS_WARNING )
801 : : {
802 : 0 : sd->nErrorType = _IS_WARNING;
803 : : }
804 : 0 : sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE;
805 : 0 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE;
806 : : }
807 : : }
808 : : }
809 : :
810 : 98 : sd->bTautFlags[INCHI_BAS] |= bTautFlags; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */
811 : 98 : sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */
812 : :
813 : 98 : return sd->nErrorType;
814 : : }
815 : :
816 : :
817 : : #ifndef TARGET_API_LIB
818 : :
819 : :
820 : : /****************************************************************************/
821 : : int CreateCompositeNormAtom(COMP_ATOM_DATA* composite_norm_data,
822 : : INP_ATOM_DATA2* all_inp_norm_data,
823 : : int num_components)
824 : : {
825 : : int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H; /* djb-rwth: removing redundant variables */
826 : : int num_comp[TAUT_NUM + 1], num_taut[TAUT_NUM + 1], num_del[TAUT_NUM + 1], num_at[TAUT_NUM + 1], num_inp_at[TAUT_NUM + 1];
827 : : int ret = 0, indicator = 1;
828 : : inp_ATOM* at, * at_from;
829 : : memset(num_comp, 0, sizeof(num_comp)); /* djb-rwth: memset_s C11/Annex K variant? */
830 : : memset(num_taut, 0, sizeof(num_taut)); /* djb-rwth: memset_s C11/Annex K variant? */
831 : : memset(num_del, 0, sizeof(num_taut)); /* djb-rwth: memset_s C11/Annex K variant? */
832 : :
833 : : /* count taut and non-taut components */
834 : : for ( j = 0; j < TAUT_NUM; j++ )
835 : : {
836 : : num_comp[j] = num_taut[j] = 0;
837 : : for ( i = 0; i < num_components; i++ )
838 : : {
839 : : if ( all_inp_norm_data[i][j].bExists )
840 : : {
841 : : num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted);
842 : : num_comp[j]++;
843 : : num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric);
844 : : }
845 : : }
846 : : }
847 : :
848 : : /* count intermediate taut structure components */
849 : : if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] )
850 : : {
851 : : /*
852 : : num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES];
853 : : */
854 : :
855 : : for ( i = 0, j = TAUT_YES; i < num_components; i++ )
856 : : {
857 : : if ( all_inp_norm_data[i][j].bExists &&
858 : : (all_inp_norm_data[i][j].bDeleted ||
859 : : (all_inp_norm_data[i][j].bTautomeric &&
860 : : all_inp_norm_data[i][j].at_fixed_bonds &&
861 : : all_inp_norm_data[i][j].bTautPreprocessed)) ) /* djb-rwth: addressing LLVM warning */
862 : : {
863 : : num_comp[TAUT_INI]++;
864 : : }
865 : : }
866 : : }
867 : :
868 : : /* count atoms and allocate composite atom data */
869 : : for ( jj = 0; jj <= TAUT_INI; jj++ )
870 : : {
871 : : num_at[jj] = num_inp_at[jj] = 0;
872 : : j = inchi_min(jj, TAUT_YES);
873 : : if ( num_comp[jj] )
874 : : {
875 : : for ( i = 0; i < num_components; i++ )
876 : : {
877 : : if ( all_inp_norm_data[i][j].bDeleted )
878 : : {
879 : : continue;
880 : : }
881 : : /* find k = the normaized structure index */
882 : : if ( jj == TAUT_INI )
883 : : {
884 : : if ( all_inp_norm_data[i][j].bExists &&
885 : : all_inp_norm_data[i][j].at_fixed_bonds )
886 : : {
887 : : k = j;
888 : : }
889 : : else
890 : : if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted &&
891 : : !all_inp_norm_data[i][j].bDeleted )
892 : : {
893 : : k = ALT_TAUT(j);
894 : : }
895 : : else
896 : : {
897 : : if ( all_inp_norm_data[i][j].bExists )
898 : : {
899 : : k = j;
900 : : }
901 : : else
902 : : {
903 : : continue;
904 : : }
905 : : }
906 : : }
907 : : else
908 : : {
909 : : if ( all_inp_norm_data[i][j].bExists )
910 : : {
911 : : k = j;
912 : : }
913 : : else
914 : : {
915 : : if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted )
916 : : {
917 : : k = ALT_TAUT(j);
918 : : }
919 : : else
920 : : {
921 : : continue;
922 : : }
923 : : }
924 : : }
925 : : num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */
926 : : num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H;
927 : : }
928 : : if ( num_inp_at[jj] )
929 : : {
930 : : if ( !CreateCompAtomData(composite_norm_data + jj, num_inp_at[jj], num_components, jj == TAUT_INI) )
931 : : {
932 : : goto exit_error;
933 : : }
934 : : composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj];
935 : : }
936 : : }
937 : : }
938 : :
939 : : /* fill out composite atom */
940 : : for ( jj = 0; jj <= TAUT_INI; jj++, indicator <<= 1 )
941 : : {
942 : : j = inchi_min(jj, TAUT_YES);
943 : : if ( num_comp[jj] )
944 : : {
945 : : tot_num_at = 0;
946 : : tot_num_H = 0;
947 : : for ( i = 0; i < num_components; i++ )
948 : : {
949 : : if ( all_inp_norm_data[i][j].bDeleted )
950 : : {
951 : : composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons;
952 : : for ( n = 0; n < NUM_H_ISOTOPES; n++ )
953 : : {
954 : : composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n];
955 : : }
956 : : continue;
957 : : }
958 : : /* djb-rwth: removing redundant code */
959 : : /* find k = the normaized structure index */
960 : : if ( jj == TAUT_INI )
961 : : {
962 : : if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds )
963 : : {
964 : : k = j;
965 : : }
966 : : else
967 : : {
968 : : if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists )
969 : : {
970 : : k = ALT_TAUT(j);
971 : : }
972 : : else
973 : : {
974 : : if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted )
975 : : {
976 : : k = j;
977 : : }
978 : : else
979 : : {
980 : : continue;
981 : : }
982 : : }
983 : : }
984 : : }
985 : : else
986 : : {
987 : : if ( all_inp_norm_data[i][j].bExists )
988 : : {
989 : : k = j;
990 : : }
991 : : else
992 : : {
993 : : if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted )
994 : : {
995 : : k = ALT_TAUT(j);
996 : : }
997 : : else
998 : : {
999 : : continue;
1000 : : }
1001 : : }
1002 : : }
1003 : : /* copy main atoms */
1004 : : cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */
1005 : : cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */
1006 : :
1007 : : if ( (tot_num_at + cur_num_at) > num_at[jj] ||
1008 : : (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] )
1009 : : {
1010 : : goto exit_error; /* miscount */
1011 : : }
1012 : : at = composite_norm_data[jj].at + tot_num_at; /* points to the 1st destination atom */
1013 : : at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds) ?
1014 : : all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at;
1015 : : memcpy(at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at); /* copy atoms except terminal H */
1016 : : /* shift neighbors of main atoms */
1017 : : for ( n = 0; n < cur_num_at; n++, at++ )
1018 : : {
1019 : : for ( m = 0; m < at->valence; m++ )
1020 : : {
1021 : : at->neighbor[m] += tot_num_at;
1022 : : }
1023 : : }
1024 : : /* copy explicit H */
1025 : : if ( cur_num_H )
1026 : : {
1027 : : at = composite_norm_data[jj].at + num_at[jj] + tot_num_H; /* points to the 1st destination atom */
1028 : : memcpy(at, at_from + cur_num_at,
1029 : : sizeof(composite_norm_data[0].at[0]) * cur_num_H);
1030 : : /* shift neighbors of explicit H atoms */
1031 : : for ( n = 0; n < cur_num_H; n++, at++ )
1032 : : {
1033 : : for ( m = 0; m < at->valence; m++ )
1034 : : {
1035 : : at->neighbor[m] += tot_num_at;
1036 : : }
1037 : : }
1038 : : }
1039 : : /* composite counts */
1040 : : composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer;
1041 : : composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic;
1042 : : composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds;
1043 : : composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric;
1044 : : composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons;
1045 : : for ( n = 0; n < NUM_H_ISOTOPES; n++ )
1046 : : {
1047 : : composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n];
1048 : : composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n];
1049 : : }
1050 : : /*
1051 : : composite_norm_data[j].num_at += cur_num_at + cur_num_H;
1052 : : composite_norm_data[j].num_removed_H += cur_num_H;
1053 : : */
1054 : : /* total count */
1055 : : tot_num_at += cur_num_at;
1056 : : tot_num_H += cur_num_H;
1057 : : /* offset for the next component */
1058 : : if ( composite_norm_data[jj].nOffsetAtAndH )
1059 : : {
1060 : : composite_norm_data[jj].nOffsetAtAndH[2 * i] = tot_num_at;
1061 : : composite_norm_data[jj].nOffsetAtAndH[2 * i + 1] = num_at[jj] + tot_num_H;
1062 : : }
1063 : : }
1064 : : if ( tot_num_at != num_at[jj] ||
1065 : : num_at[jj] + tot_num_H != num_inp_at[jj] )
1066 : : {
1067 : : goto exit_error; /* miscount */
1068 : : }
1069 : : composite_norm_data[jj].bExists = (tot_num_at > 0);
1070 : : ret |= indicator;
1071 : : }
1072 : : }
1073 : : return ret;
1074 : :
1075 : : exit_error:
1076 : :
1077 : : return ret;
1078 : : }
1079 : : #endif
1080 : :
1081 : :
1082 : : /****************************************************************************/
1083 : 0 : void OrigAtData_DebugTrace(ORIG_ATOM_DATA* d)
1084 : : {
1085 : : int i, k;
1086 : :
1087 : : ITRACE_("\n\n*********************************************************************\n* ORIG_ATOM_DATA @ 0x%p", d);
1088 : : ITRACE_("\n* num_inp_atoms = %-d\n* num_inp_bonds = %-d\n* num_dimensions = %-d\n* num_components = %-d",
1089 : : d->num_inp_atoms, d->num_inp_bonds, d->num_dimensions, d->num_components);
1090 : : ITRACE_("\n* ATOMS");
1091 [ # # ]: 0 : for ( i = 0; i < d->num_inp_atoms; i++ )
1092 : : {
1093 : : ITRACE_("\n* #%-5d %s%-d ( charge %-d, rad %-d nH %-d val %-d) [%-f %-f %-f]",
1094 : : i, d->at[i].elname, d->at[i].orig_at_number, d->at[i].charge, d->at[i].radical, d->at[i].num_H, d->at[i].valence,
1095 : : d->at[i].x, d->at[i].y, d->at[i].z);
1096 [ # # ]: 0 : if ( d->at[i].valence > 0 )
1097 : : {
1098 : : ITRACE_("\n bonds to ");
1099 [ # # ]: 0 : for ( k = 0; k < d->at[i].valence; k++ )
1100 : : {
1101 : 0 : int nbr = d->at[i].neighbor[k]; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
1102 : : ITRACE_("%s%-3d ", d->at[nbr].elname, nbr + 1);
1103 : : }
1104 : : }
1105 [ # # ]: 0 : if ( d->at[i].valence > 0 )
1106 : : {
1107 : : ITRACE_("\n bond types ");
1108 [ # # ]: 0 : for ( k = 0; k < d->at[i].valence; k++ )
1109 : : ITRACE_("%-3d ", d->at[i].bond_type[k]);
1110 : : }
1111 : : }
1112 : : /*OAD_Polymer_DebugTrace( d->polymer );*/
1113 : : ITRACE_("\n* V3000 INFO @ 0x%-p", d->v3000);
1114 : : ITRACE_("\n*\n");
1115 : 0 : if ( d->v3000 )
1116 : : {
1117 : : ITRACE_("\n* n_star_atoms = %-d\n* n_haptic_bonds = %-d\n* n_collections = %-d",
1118 : : d->v3000->n_star_atoms, d->v3000->n_haptic_bonds, d->v3000->n_collections);
1119 : : }
1120 : : ITRACE_("\n*\n* End ORIG_ATOM_DATA\n*********************************************************************\n");
1121 : :
1122 : 0 : return;
1123 : : }
1124 : :
1125 : :
1126 : :
1127 : : /*
1128 : : Polymer related procedures
1129 : : */
1130 : :
1131 : :
1132 : :
1133 : :
1134 : : /****************************************************************************
1135 : : Create a new OAD_PolymerUnit
1136 : : ****************************************************************************/
1137 : 0 : OAD_PolymerUnit* OAD_PolymerUnit_New(int maxatoms,
1138 : : int maxbonds,
1139 : : int id,
1140 : : int label,
1141 : : int type,
1142 : : int subtype,
1143 : : int conn,
1144 : : char* smt,
1145 : : int na,
1146 : : INT_ARRAY* alist,
1147 : : int nb,
1148 : : INT_ARRAY* blist,
1149 : : int nbkbonds,
1150 : : int** bkbonds)
1151 : : {
1152 : 0 : int k, err = 0;
1153 : 0 : OAD_PolymerUnit* u2 = NULL;
1154 : :
1155 : 0 : u2 = (OAD_PolymerUnit*)inchi_calloc(1, sizeof(OAD_PolymerUnit));
1156 [ # # ]: 0 : if ( NULL == u2 )
1157 : : {
1158 : 0 : err = 1;
1159 : 0 : goto exit_function;
1160 : : }
1161 : :
1162 : 0 : u2->id = id;
1163 : 0 : u2->label = label;
1164 : 0 : u2->type = type;
1165 : 0 : u2->subtype = subtype;
1166 : 0 : u2->conn = conn;
1167 : 0 : u2->na = na;
1168 : 0 : u2->nb = nb;
1169 : 0 : u2->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1170 : 0 : u2->cyclized = 0;
1171 [ # # ]: 0 : for ( k = 0; k < 4; k++ )
1172 : : {
1173 : 0 : u2->xbr1[k] = 0.0;
1174 : 0 : u2->xbr2[k] = 0.0;
1175 : : }
1176 : 0 : strcpy(u2->smt, smt);
1177 : 0 : u2->cap1 = -1;
1178 : 0 : u2->end_atom1 = -1;
1179 : 0 : u2->cap2 = -1;
1180 : 0 : u2->end_atom2 = -1;
1181 : 0 : u2->maxbkbonds = maxbonds;
1182 : 0 : u2->nbkbonds = nbkbonds;
1183 : 0 : u2->cap1_is_undef = 0;
1184 : 0 : u2->cap2_is_undef = 0;
1185 : :
1186 : 0 : u2->alist = NULL;
1187 [ # # # # ]: 0 : if ( na > 0 || maxatoms > 0 )
1188 : : {
1189 [ # # ]: 0 : u2->alist = (int*)inchi_calloc(na > 0 ? na : maxatoms, sizeof(int));
1190 [ # # ]: 0 : if ( !u2->alist )
1191 : : {
1192 : 0 : err = 2;
1193 : 0 : goto exit_function;
1194 : : }
1195 [ # # ]: 0 : for ( k = 0; k < na; k++ )
1196 : : {
1197 : 0 : u2->alist[k] = alist->item[k];
1198 : : }
1199 : : }
1200 : 0 : u2->blist = NULL;
1201 [ # # # # ]: 0 : if ( nb > 0 || maxbonds > 0 )
1202 : : {
1203 [ # # ]: 0 : u2->blist = (int*)inchi_calloc(nb > 0 ? 2 * nb : 2 * maxbonds, sizeof(int));
1204 [ # # ]: 0 : if ( !u2->blist )
1205 : : {
1206 : 0 : err = 3;
1207 : 0 : goto exit_function;
1208 : : }
1209 [ # # ]: 0 : if ( blist )
1210 : : {
1211 [ # # ]: 0 : for ( k = 0; k < 2 * nb; k++ )
1212 : : {
1213 : 0 : u2->blist[k] = blist->item[k];
1214 : : }
1215 : : }
1216 : :
1217 : : }
1218 : 0 : u2->bkbonds = NULL;
1219 : :
1220 : 0 : exit_function:
1221 : :
1222 [ # # ]: 0 : if ( err )
1223 : : {
1224 : 0 : OAD_PolymerUnit_Free(u2);
1225 : 0 : return NULL;
1226 : : }
1227 : :
1228 : 0 : return u2;
1229 : : }
1230 : :
1231 : :
1232 : : /****************************************************************************
1233 : : Create a copy of OAD_PolymerUnit
1234 : : ****************************************************************************/
1235 : 5 : OAD_PolymerUnit* OAD_PolymerUnit_CreateCopy(OAD_PolymerUnit* u)
1236 : : {
1237 : 5 : int k, err = 0;
1238 : 5 : OAD_PolymerUnit* u2 = NULL;
1239 : :
1240 : 5 : u2 = (OAD_PolymerUnit*)inchi_calloc(1, sizeof(OAD_PolymerUnit));
1241 [ - + ]: 5 : if ( NULL == u2 )
1242 : : {
1243 : 0 : err = 1;
1244 : 0 : goto exit_function;
1245 : : }
1246 : 5 : u2->id = u->id;
1247 : 5 : u2->type = u->type;
1248 : 5 : u2->subtype = u->subtype;
1249 : 5 : u2->conn = u->conn;
1250 : 5 : u2->label = u->label;
1251 : 5 : u2->na = u->na;
1252 : 5 : u2->nb = u->nb;
1253 : 5 : u2->cyclizable = u->cyclizable;
1254 : 5 : u2->cyclized = u->cyclized;
1255 : 5 : u2->cap1_is_undef = u->cap1_is_undef;
1256 : 5 : u2->cap2_is_undef = u->cap2_is_undef;
1257 : :
1258 [ + + ]: 25 : for ( k = 0; k < 4; k++ )
1259 : : {
1260 : 20 : u2->xbr1[k] = u->xbr1[k];
1261 : 20 : u2->xbr2[k] = u->xbr2[k];
1262 : : }
1263 : :
1264 : 5 : strcpy(u2->smt, u->smt);
1265 : :
1266 : 5 : u2->cap1 = u->cap1;
1267 : 5 : u2->end_atom1 = u->end_atom1;
1268 : 5 : u2->cap2 = u->cap2;
1269 : 5 : u2->end_atom2 = u->end_atom2;
1270 : 5 : u2->nbkbonds = u->nbkbonds;
1271 : 5 : u2->maxbkbonds = inchi_max(u->maxbkbonds, u->nbkbonds);
1272 : :
1273 : 5 : u2->alist = (int*)inchi_calloc(u2->na, sizeof(int));
1274 [ - + ]: 5 : if ( !u2->alist )
1275 : : {
1276 : 0 : err = 2;
1277 : 0 : goto exit_function;
1278 : : }
1279 [ + + ]: 20 : for ( k = 0; k < u2->na; k++ )
1280 : : {
1281 : 15 : u2->alist[k] = u->alist[k];
1282 : : }
1283 : :
1284 : 5 : u2->blist = (int*)inchi_calloc(2 * (long long)u2->nb, sizeof(int)); /* djb-rwth: cast operator added */
1285 [ - + ]: 5 : if ( !u2->blist )
1286 : : {
1287 : 0 : err = 2;
1288 : 0 : goto exit_function;
1289 : : }
1290 [ + + ]: 25 : for ( k = 0; k < 2 * u2->nb; k++ )
1291 : : {
1292 : 20 : u2->blist[k] = u->blist[k];
1293 : : }
1294 : :
1295 : 5 : err = imat_new(u2->maxbkbonds, 2, &(u2->bkbonds));
1296 [ - + ]: 5 : if ( !err )
1297 : : {
1298 [ - + ]: 5 : for ( k = 0; k < u2->nbkbonds; k++ )
1299 : : {
1300 : 0 : u2->bkbonds[k][0] = u->bkbonds[k][0];
1301 : 0 : u2->bkbonds[k][1] = u->bkbonds[k][1];
1302 : : }
1303 : : }
1304 : :
1305 : 5 : exit_function:
1306 [ - + ]: 5 : if ( err )
1307 : : {
1308 : 0 : OAD_PolymerUnit_Free(u2);
1309 : 0 : return NULL;
1310 : : }
1311 : :
1312 : 5 : return u2;
1313 : : }
1314 : :
1315 : :
1316 : : /****************************************************************************/
1317 : 6 : void OAD_PolymerUnit_Free(OAD_PolymerUnit* unit)
1318 : : {
1319 : :
1320 : : ITRACE_("\n************** About to free OAD_PolymerUnit @ %-p\n", unit);
1321 : 6 : OAD_PolymerUnit_DebugTrace(unit);
1322 : :
1323 [ + - ]: 6 : if ( unit )
1324 : : {
1325 [ + - ]: 6 : if ( unit->alist )
1326 : : {
1327 [ + - ]: 6 : inchi_free(unit->alist);
1328 : 6 : unit->alist = NULL;
1329 : : }
1330 [ + - ]: 6 : if ( unit->blist )
1331 : : {
1332 [ + - ]: 6 : inchi_free(unit->blist);
1333 : 6 : unit->blist = NULL;
1334 : : }
1335 [ + - ]: 6 : if ( unit->bkbonds )
1336 : : {
1337 : 6 : imat_free(unit->maxbkbonds, unit->bkbonds);
1338 : 6 : unit->bkbonds = NULL;
1339 : : }
1340 : : }
1341 : :
1342 [ + - ]: 6 : inchi_free(unit);
1343 : :
1344 : 6 : return;
1345 : : }
1346 : :
1347 : :
1348 : : /****************************************************************************
1349 : : Compare two polymer units, modified lexicographic order
1350 : : Modification: unit with smaller alist always go first
1351 : : ****************************************************************************/
1352 : 0 : int OAD_PolymerUnit_CompareAtomListsMod(OAD_PolymerUnit* u1,
1353 : : OAD_PolymerUnit* u2)
1354 : : {
1355 : : int i;
1356 : 0 : int n1 = u1->na;
1357 : 0 : int n2 = u2->na;
1358 : 0 : int n = n1;
1359 [ # # ]: 0 : if ( n1 < n2 ) return -1;
1360 [ # # ]: 0 : if ( n1 > n2 ) return 1;
1361 : : /* n1 == n2 == n */
1362 [ # # ]: 0 : for ( i = 0; i < n; i++ )
1363 : : {
1364 [ # # ]: 0 : if ( u1->alist[i] < u2->alist[i] ) return -1;
1365 [ # # ]: 0 : if ( u1->alist[i] > u2->alist[i] ) return 1;
1366 : : }
1367 : :
1368 : 0 : return 0;
1369 : : }
1370 : :
1371 : :
1372 : : /****************************************************************************
1373 : : Compare two polymer units, lexicographic order
1374 : : ****************************************************************************/
1375 : 0 : int OAD_PolymerUnit_CompareAtomLists(OAD_PolymerUnit* u1,
1376 : : OAD_PolymerUnit* u2)
1377 : : {
1378 : : int i;
1379 : 0 : int n1 = u1->na;
1380 : 0 : int n2 = u2->na;
1381 : 0 : int n = inchi_min(n1, n2);
1382 : :
1383 [ # # ]: 0 : for ( i = 0; i < n; i++ )
1384 : : {
1385 [ # # ]: 0 : if ( u1->alist[i] < u2->alist[i] )
1386 : : {
1387 : 0 : return -1;
1388 : : }
1389 [ # # ]: 0 : if ( u1->alist[i] > u2->alist[i] )
1390 : : {
1391 : 0 : return 1;
1392 : : }
1393 : : }
1394 : :
1395 [ # # ]: 0 : if ( n1 < n2 )
1396 : : {
1397 : 0 : return -1;
1398 : : }
1399 : :
1400 [ # # ]: 0 : if ( n1 > n2 )
1401 : : {
1402 : 0 : return 1;
1403 : : }
1404 : :
1405 : 0 : return 0;
1406 : : }
1407 : :
1408 : :
1409 : : /****************************************************************************
1410 : : Sort SRU bond lists atoms and bonds themselves
1411 : : ****************************************************************************/
1412 : 2 : int OAD_PolymerUnit_OrderBondAtomsAndBondsThemselves(OAD_PolymerUnit* u,
1413 : : int n_star_atoms,
1414 : : int* star_atoms)
1415 : : {
1416 : : int k;
1417 : :
1418 : : /* Sort bond atoms */
1419 [ + + ]: 6 : for ( k = 0; k < u->nb; k++ )
1420 : : {
1421 : : /* Place not-in-unit bond end to first place */
1422 : 4 : int a1 = u->blist[2 * k];
1423 : 4 : int a2 = u->blist[2 * k + 1];
1424 : 4 : int a1_is_not_in_alist = 0;
1425 : 4 : int a1_is_star_atom = 0;
1426 : 4 : int a2_is_not_in_alist = 0;
1427 : 4 : int a2_is_star_atom = 0;
1428 : :
1429 [ + + ]: 4 : if ( !is_in_the_ilist(u->alist, a1, u->na) )
1430 : : {
1431 : 2 : a1_is_not_in_alist = 1;
1432 : : }
1433 [ - + ]: 4 : if ( is_in_the_ilist(star_atoms, a1, n_star_atoms) )
1434 : : {
1435 : 0 : a1_is_star_atom = 1;
1436 : : }
1437 : :
1438 [ + + ]: 4 : if ( !is_in_the_ilist(u->alist, a2, u->na) )
1439 : : {
1440 : 2 : a2_is_not_in_alist = 1;
1441 : : }
1442 [ - + ]: 4 : if ( is_in_the_ilist(star_atoms, a2, n_star_atoms) )
1443 : : {
1444 : 0 : a2_is_star_atom = 1;
1445 : : }
1446 : :
1447 [ + + - + : 4 : if ( (a1_is_not_in_alist || a1_is_star_atom) &&
+ - ]
1448 [ - + ]: 2 : (a2_is_not_in_alist || a2_is_star_atom) )
1449 : : {
1450 : : /* Both the ends are out of unit: the crossing bond is invalid */
1451 : 0 : return 1;
1452 : : }
1453 : : /* If a2 is star atom or non-star external to the current unit, swap(a2,a1) */
1454 [ + - + + ]: 4 : if ( a2_is_star_atom || a2_is_not_in_alist )
1455 : : {
1456 : 2 : u->blist[2 * k] = a2;
1457 : 2 : u->blist[2 * k + 1] = a1;
1458 : : }
1459 : : }
1460 : :
1461 : : /* Sort bond themselves
1462 : : for now, consider only the simplest cases of 2 bonds
1463 : : */
1464 [ + - ]: 2 : if ( u->nb == 2 ) /* two bonds in SBL */
1465 : : {
1466 : 2 : int b1a1 = u->blist[0];
1467 : 2 : int b1a2 = u->blist[1];
1468 : 2 : int b2a1 = u->blist[2];
1469 : 2 : int b2a2 = u->blist[3];
1470 [ - + ]: 2 : if ( b1a1 > b2a1 )
1471 : : {
1472 : : /* swap */
1473 : 0 : u->blist[0] = b2a1; u->blist[1] = b2a2;
1474 : 0 : u->blist[2] = b1a1; u->blist[3] = b1a2;
1475 : : }
1476 : : }
1477 : :
1478 : : /* for single or no bonds, do nothing
1479 : : else
1480 : : ;
1481 : : */
1482 : :
1483 : 2 : return 0;
1484 : : }
1485 : :
1486 : :
1487 : : /****************************************************************************
1488 : : Parse pseudoelement and polymer data
1489 : : (unit, types, subtypes, connections, etc.)
1490 : : ****************************************************************************/
1491 : 122 : int OAD_ValidatePolymerAndPseudoElementData(ORIG_ATOM_DATA* orig_at_data,
1492 : : int treat_polymers,
1493 : : int bNPZz,
1494 : : char* pStrErr,
1495 : : int bNoWarnings)
1496 : : {
1497 : 122 : int i, k, kk, type, subtype, representation, err = 0;
1498 : 122 : int nat = orig_at_data->num_inp_atoms;
1499 : 122 : int nsgroups = 0;
1500 : 122 : OAD_PolymerUnit* u = NULL;
1501 : 122 : OAD_Polymer* pd = orig_at_data->polymer;
1502 : :
1503 : :
1504 : : /* Assign polymer type and subunits type and check polymer data for consistency */
1505 : : /* djb-rwth: addressing coverity ID #499497 -- TREAT_ERR properly used in all cases */
1506 : :
1507 : 122 : orig_at_data->valid_polymer = 0;
1508 [ + + + - ]: 122 : if ( treat_polymers && pd )
1509 : : {
1510 : 2 : orig_at_data->valid_polymer = 1;
1511 : : }
1512 [ + + ]: 122 : if ( orig_at_data->valid_polymer )
1513 : : {
1514 : 2 : nsgroups = pd->n;
1515 : : }
1516 [ + + ]: 122 : if ( nsgroups == 1 )
1517 : : {
1518 : : /* Check if copolymer */
1519 : 2 : type = pd->units[0]->type;
1520 [ - + ]: 2 : if ( type == POLYMER_STY_COP )
1521 : : {
1522 [ # # ]: 0 : TREAT_ERR(err, 9001, "Copolymer must contain more than one unit");
1523 : 0 : goto exit_function;
1524 : : }
1525 : : /* Check if copolymer subtype */
1526 : 2 : subtype = pd->units[0]->subtype;
1527 [ + - + - : 2 : if ( subtype == POLYMER_SST_RAN || subtype == POLYMER_SST_ALT || subtype == POLYMER_SST_BLK )
- + ]
1528 : : {
1529 : : /** @nnuk:
1530 : : * 9002 remains assigned to this validation; its former use for
1531 : : *unsupported polymer H end groups was removed with GHI #252.
1532 : : */
1533 [ # # ]: 0 : TREAT_ERR(err, 9002, "Single polymer unit may not be RAN/ALT/BLO");
1534 : 0 : goto exit_function;
1535 : : }
1536 : : }
1537 : :
1538 : : /* For each CRU */
1539 [ + + ]: 124 : for ( i = 0; i < nsgroups; i++ )
1540 : : {
1541 : : /* Check if unit data makes sense */
1542 : 2 : u = pd->units[i];
1543 [ + - - + ]: 2 : if ( u->nb != 0 && u->nb != 2)
1544 : : {
1545 [ # # ]: 0 : TREAT_ERR(err, 9003, "Number of crossing bonds in polymer unit is not 0 or 2");
1546 : 0 : goto exit_function;
1547 : : }
1548 [ - + ]: 2 : if ( u->na < 1 )
1549 : : {
1550 [ # # ]: 0 : TREAT_ERR(err, 9004, "Empty polymer unit");
1551 : 0 : goto exit_function;
1552 : : }
1553 [ - + ]: 2 : if ( u->na > nat )
1554 : : {
1555 [ # # ]: 0 : TREAT_ERR(err, 9005, "Too large polymer unit");
1556 : 0 : goto exit_function;
1557 : : }
1558 [ + + ]: 8 : for ( k = 0; k < u->na; k++ )
1559 : : {
1560 : 6 : int atom = u->alist[k];
1561 [ + - - + ]: 6 : if ( atom < 1 || atom > nat )
1562 : : {
1563 [ # # ]: 0 : TREAT_ERR(err, 9006, "Invalid atom number in polymer unit");
1564 : 0 : goto exit_function;
1565 : : }
1566 : : /* was not accounting for COP ...
1567 : : if (is_in_the_ilist( pd->pzz, atom, pd->n_pzz ))
1568 : : {
1569 : : TREAT_ERR( err, 9007, "Star atom inside polymer unit" );
1570 : : goto exit_function;
1571 : : }
1572 : : */
1573 : : }
1574 : :
1575 : 2 : OAD_PolymerUnit_SetEndsAndCaps(u, orig_at_data, &err, pStrErr);
1576 : : /* Reveal and store CRU caps and ends('stars and partners')
1577 : : Also set `unit->cap1_is_undef`, `unit->cap2_is_undef`, `unit->cyclizable`
1578 : : */
1579 [ - + ]: 2 : if ( err )
1580 : : {
1581 : 0 : goto exit_function;
1582 : : }
1583 : :
1584 : :
1585 : : /* Set possibly missing unit parameters */
1586 : 2 : u->nbkbonds = 0;
1587 : 2 : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1588 : 2 : u->cyclized = 0;
1589 : : }
1590 : :
1591 : :
1592 : 122 : OAD_ValidateAndSortOutPseudoElementAtoms(orig_at_data, treat_polymers, bNPZz, &err, pStrErr);
1593 : : /* Here we:
1594 : : Make more polymer and pseudoatom data checks
1595 : : Convert both "*" and "Zz" temporarily to "Zy" (polymer-unrelated interal pseudoatoms)
1596 : : If applicable, check each CRU and back-convert "Zy" to "Zz" (polymer-related
1597 : : pseudoelement atoms) if they are for valid bi-undef-end CRU
1598 : : */
1599 : :
1600 [ - + ]: 122 : if ( err )
1601 : : {
1602 : : /* already treated TREAT_ERR( err, 9040, "Improper pseudoelement atoms" ); */
1603 : 0 : goto exit_function;
1604 : : }
1605 : :
1606 : :
1607 : : /* Make more polymer and pseudoatom data checks */
1608 : :
1609 : : /* Check if non-polymer-related Zz/star atoms enabled */
1610 [ - + - - ]: 122 : if ( orig_at_data->n_zy > 0 && bNPZz == 0 )
1611 : : {
1612 [ # # ]: 0 : TREAT_ERR(err, 9, "Non-polymer-related Zz/star atoms are not allowed");
1613 : 0 : goto exit_function;
1614 : : }
1615 : :
1616 [ + + - + ]: 122 : if ( !pd || !orig_at_data->valid_polymer )
1617 : : {
1618 : 120 : goto exit_function;
1619 : : }
1620 : :
1621 [ - + ]: 2 : if ( pd->n_pzz > 0 )
1622 : : {
1623 : : /* Allocate memory for polymer-related pseudoatoms */
1624 [ # # ]: 0 : if ( pd->treat == POLYMERS_NO )
1625 : : {
1626 [ # # ]: 0 : TREAT_ERR(err, 9, "Pseudoelement endgroups are not allowed");
1627 : 0 : goto exit_function;
1628 : : }
1629 [ # # ]: 0 : if ( pd->pzz )
1630 : : {
1631 [ # # ]: 0 : inchi_free(pd->pzz);
1632 : 0 : pd->pzz = NULL;
1633 : : }
1634 : 0 : pd->pzz = (int*)inchi_calloc(pd->n_pzz, sizeof(int));
1635 [ # # ]: 0 : if ( !pd->pzz )
1636 : : {
1637 [ # # ]: 0 : TREAT_ERR(err, 9010, "Not enough memory");
1638 : 0 : goto exit_function;
1639 : : }
1640 : 0 : kk = 0;
1641 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
1642 : : {
1643 [ # # ]: 0 : if ( !strcmp(orig_at_data->at[k].elname, "Zz") )
1644 : : {
1645 : 0 : pd->pzz[kk++] = k + 1; /* djb-rwth: buffer overrun avoided implicitly */
1646 : : }
1647 : : }
1648 : : }
1649 : :
1650 : : /* Check copolymers and ensure that COP includes > 1 SRU */
1651 [ + + ]: 4 : for ( i = 0; i < pd->n; i++ )
1652 : : {
1653 : 2 : u = pd->units[i];
1654 : :
1655 [ + - ]: 2 : if ( u->type == POLYMER_STY_COP ||
1656 [ + - ]: 2 : u->type == POLYMER_STY_SRU /* what drawn as 'SRU' [xyz]n may be actually copolymer [xyz]co */
1657 : : )
1658 : : {
1659 : 2 : int j, in_units = 0;
1660 : :
1661 [ + - ]: 2 : if ( u->nb > 0 )
1662 : : {
1663 : : /* crossing bonds present, either valid SRU or invalid copolymer */
1664 [ - + ]: 2 : if ( u->type == POLYMER_STY_COP )
1665 : : {
1666 [ # # ]: 0 : TREAT_ERR(err, 9026, "Polymer COP unit contains bracket-crossing bonds, not supported");
1667 : 0 : goto exit_function;
1668 : : }
1669 : : else
1670 : : {
1671 : 2 : continue;
1672 : : }
1673 : : }
1674 : : /* now we have no crossing bonds units */
1675 [ # # ]: 0 : for ( j = 0; j < pd->n; j++ )
1676 : : {
1677 [ # # ]: 0 : if ( pd->units[j]->type == POLYMER_STY_COP )
1678 : : {
1679 : 0 : continue;
1680 : : }
1681 [ # # ]: 0 : if ( is_ilist_inside(pd->units[j]->alist, pd->units[j]->na, pd->units[i]->alist, pd->units[i]->na) )
1682 : : {
1683 : 0 : in_units++;
1684 [ # # ]: 0 : if ( in_units == 2 )
1685 : : {
1686 : 0 : break;
1687 : : }
1688 : : }
1689 : : }
1690 [ # # ]: 0 : if ( in_units > 1 )
1691 : : {
1692 [ # # ]: 0 : if ( u->type != POLYMER_STY_COP )
1693 : : {
1694 : 0 : u->type = POLYMER_STY_COP;
1695 [ # # ]: 0 : if ( !bNoWarnings )
1696 : : {
1697 : 0 : WarningMessage(pStrErr, "Convert multiple-subunits unit to copolymer");
1698 : : }
1699 : : }
1700 : : }
1701 : : else /* in_units <= 1)*/
1702 : : {
1703 [ # # ]: 0 : if ( u->type == POLYMER_STY_COP )
1704 : : {
1705 [ # # ]: 0 : TREAT_ERR(err, 9027, "Polymer COP unit contains a single SRU instead of multiple");
1706 : 0 : goto exit_function;
1707 : : }
1708 : : }
1709 : : }
1710 : : }
1711 : :
1712 : 2 : representation = OAD_Polymer_GetRepresentation(pd);
1713 : :
1714 : : /* Make more polymer data checks and perform some corrections*/
1715 [ - + ]: 2 : if ( representation == POLYMER_REPRESENTATION_SOURCE_BASED )
1716 : : {
1717 [ # # ]: 0 : for ( i = 0; i < nsgroups; i++ )
1718 : : {
1719 : : /* Replace source-based 'SRU' with 'MON' */
1720 [ # # ]: 0 : if ( pd->units[i]->type == POLYMER_STY_SRU )
1721 : : {
1722 : 0 : pd->units[i]->type = POLYMER_STY_MON;
1723 [ # # ]: 0 : if ( !bNoWarnings )
1724 : : {
1725 : 0 : WarningMessage(pStrErr, "Converted src-based polymer unit type to MON");
1726 : : }
1727 : : }
1728 [ # # ]: 0 : if ( pd->units[i]->type == POLYMER_STY_COP )
1729 : : {
1730 : : /* Set missing copolymer subtype to RAN */
1731 [ # # ]: 0 : if ( pd->units[i]->subtype == POLYMER_SST_NON )
1732 : : {
1733 : 0 : pd->units[i]->subtype = POLYMER_SST_RAN;
1734 [ # # ]: 0 : if ( !bNoWarnings )
1735 : : {
1736 : 0 : WarningMessage(pStrErr, "Set missing copolymer subtype to RAN");
1737 : : }
1738 : : }
1739 : : }
1740 : : /* Suppress connectivity ("HH", "HT", "EU") */
1741 [ # # ]: 0 : if ( pd->units[i]->conn != POLYMER_CONN_NON )
1742 : : {
1743 : 0 : pd->units[i]->conn = POLYMER_CONN_NON;
1744 [ # # ]: 0 : if ( !bNoWarnings )
1745 : : {
1746 : 0 : WarningMessage(pStrErr, "Ignore connection pattern for src-based polymer unit");
1747 : : }
1748 : : }
1749 : : }
1750 : : }
1751 : :
1752 : : #ifdef ALLOW_MIXED_SRU_AND_MON
1753 [ - + - - ]: 2 : else if ( representation == POLYMER_REPRESENTATION_STRUCTURE_BASED ||
1754 : : representation == POLYMER_REPRESENTATION_MIXED )
1755 : : #else
1756 : : else if ( representation == POLYMER_REPRESENTATION_STRUCTURE_BASED )
1757 : : #endif
1758 : : {
1759 [ + + ]: 4 : for ( i = 0; i < nsgroups; i++ )
1760 : : {
1761 : : int a1, a2, a1_is_not_in_alist, a1_is_star_atom, a2_is_not_in_alist, a2_is_star_atom;
1762 : :
1763 : 2 : u = pd->units[i];
1764 : :
1765 : : /* SRU that is copolymer unit embedding other SRU's */
1766 [ - + ]: 2 : if ( u->nb == 0 )
1767 : : {
1768 [ # # ]: 0 : if ( u->type == POLYMER_STY_COP )
1769 : : {
1770 : : ;
1771 : : }
1772 [ # # ]: 0 : else if ( u->type == POLYMER_STY_SRU )
1773 : : {
1774 : 0 : u->type = POLYMER_STY_COP;
1775 [ # # ]: 0 : if ( !bNoWarnings )
1776 : : {
1777 : 0 : WarningMessage(pStrErr, "Set copolymer embedding unit mark to COP");
1778 : : }
1779 : : }
1780 : : }
1781 [ - + ]: 2 : if ( u->type == POLYMER_STY_COP )
1782 : : {
1783 : 0 : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1784 : : /* Set possibly missing copolymer subtype to RAN */
1785 [ # # ]: 0 : if ( u->subtype == POLYMER_SST_NON )
1786 : : {
1787 : 0 : u->subtype = POLYMER_SST_RAN;
1788 [ # # ]: 0 : if ( !bNoWarnings )
1789 : : {
1790 : 0 : WarningMessage(pStrErr, "Set missing copolymer subtype to RAN");
1791 : : }
1792 : : }
1793 : 0 : continue;
1794 : : }
1795 : :
1796 : : #ifdef ALLOW_MIXED_SRU_AND_MON
1797 [ - + ]: 2 : if ( u->type == POLYMER_STY_MON )
1798 : : {
1799 : 0 : continue;
1800 : : }
1801 : : #endif
1802 : : /* SRU with endgroups or stars. Check it. */
1803 [ + + ]: 6 : for ( k = 0; k < u->nb; k++ )
1804 : : {
1805 : : /* Check that there are no H end groups */
1806 : 4 : a1 = u->blist[2 * k]; a2 = u->blist[2 * k + 1];
1807 : :
1808 : : /**
1809 : : *@nnuk: GHI#252 addressed and redundant logic removed
1810 : : */
1811 : :
1812 : : /* Ensure that caps of polymer unit lie outside it */
1813 : 4 : a1_is_not_in_alist = a1_is_star_atom = 0;
1814 : 4 : a2_is_not_in_alist = a2_is_star_atom = 0;
1815 [ + + ]: 4 : if ( !is_in_the_ilist(u->alist, a1, u->na) )
1816 : : {
1817 : 2 : a1_is_not_in_alist = 1;
1818 : : }
1819 [ - + ]: 4 : if ( is_in_the_ilist(pd->pzz, a1, pd->n_pzz) )
1820 : : {
1821 : 0 : a1_is_star_atom = 1;
1822 : : }
1823 [ + + ]: 4 : if ( !is_in_the_ilist(u->alist, a2, u->na) )
1824 : : {
1825 : 2 : a2_is_not_in_alist = 1;
1826 : : }
1827 [ - + ]: 4 : if ( is_in_the_ilist(pd->pzz, a2, pd->n_pzz) )
1828 : : {
1829 : 0 : a2_is_star_atom = 1;
1830 : : }
1831 [ + + - + : 4 : if ( (a1_is_not_in_alist || a1_is_star_atom) &&
+ - ]
1832 [ - + ]: 2 : (a2_is_not_in_alist || a2_is_star_atom) )
1833 : : {
1834 [ # # ]: 0 : TREAT_ERR(err, 9032, "Caps of polymer unit lie inside it");
1835 : 0 : goto exit_function;
1836 : : }
1837 : : }
1838 : :
1839 [ - + - - ]: 2 : if ( u->type == POLYMER_STY_SRU || u->type == POLYMER_STY_MOD ||
1840 [ # # # # ]: 0 : u->type == POLYMER_STY_CRO || u->type == POLYMER_STY_MER )
1841 : : {
1842 : : /* If SRU connection is missing, set to default ('either') */
1843 [ - + ]: 2 : if ( u->conn == POLYMER_CONN_NON )
1844 : : {
1845 [ # # ]: 0 : if ( !bNoWarnings )
1846 : : {
1847 : 0 : WarningMessage(pStrErr, "Set missing copolymer unit connection to EU");
1848 : : }
1849 : 0 : u->conn = POLYMER_CONN_EU;
1850 : : }
1851 : :
1852 [ + - + - ]: 2 : if ( u->cap1 && u->cap2 )
1853 : : {
1854 : : /* Set SRU closure type */
1855 [ - + ]: 2 : if ( u->na == 1 )
1856 : : {
1857 : : #ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL
1858 : 0 : u->cyclizable = CLOSING_SRU_DIRADICAL;
1859 : : #else
1860 : : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1861 : : #ifdef CLOSING_STARRED_SRU_IS_A_MUST
1862 : : TREAT_ERR(err, 9029, "Could not perform SRU closure");
1863 : : goto exit_function;
1864 : : #endif
1865 : : #endif
1866 : : }
1867 [ - + ]: 2 : else if ( u->na == 2 )
1868 : : {
1869 : :
1870 : : #ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND
1871 : 0 : u->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;
1872 : : #else
1873 : : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1874 : : #ifdef CLOSING_STARRED_SRU_IS_A_MUST
1875 : : TREAT_ERR(err, 9029, "Could not perform SRU closure");
1876 : : goto exit_function;
1877 : : #endif
1878 : : #endif
1879 : : }
1880 : : else
1881 : : {
1882 : 2 : u->cyclizable = CLOSING_SRU_RING;
1883 : : }
1884 : : }
1885 : :
1886 [ - + ]: 2 : if ( u->conn != POLYMER_CONN_HT )
1887 : : {
1888 : : /* frame shift/SRU cyclization is for head-to-tail connections only */
1889 : 0 : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1890 : : }
1891 : :
1892 [ + - ]: 2 : if ( u->cyclizable != CLOSING_SRU_NOT_APPLICABLE )
1893 : : {
1894 : : /* Allocate PS (frame-shiftable) bonds */
1895 [ - + ]: 2 : if ( u->bkbonds )
1896 : : {
1897 : 0 : imat_free(u->maxbkbonds, u->bkbonds);
1898 : 0 : u->bkbonds = NULL;
1899 : : }
1900 : 2 : u->maxbkbonds = orig_at_data->num_inp_bonds + 2;
1901 : 2 : err = imat_new(u->maxbkbonds, 2, &(u->bkbonds));
1902 [ - + ]: 2 : if ( err )
1903 : : {
1904 [ # # ]: 0 : TREAT_ERR(err, 9034, "Not enough memory (polymers)");
1905 : 0 : goto exit_function;
1906 : : }
1907 : : }
1908 : : }
1909 : :
1910 : : }
1911 : : }
1912 : : else
1913 : : {
1914 [ # # ]: 0 : TREAT_ERR(err, 9035, "Invalid kind of polymer representation");
1915 : 0 : goto exit_function;
1916 : : }
1917 : :
1918 : 2 : orig_at_data->valid_polymer = 1;
1919 : :
1920 : 122 : exit_function:
1921 [ - + ]: 122 : if ( err )
1922 : : {
1923 : 0 : orig_at_data->valid_polymer = 0;
1924 : : }
1925 : :
1926 : 122 : return err;
1927 : : }
1928 : :
1929 : :
1930 : : /****************************************************************************/
1931 : 2 : int UnMarkRingSystemsInp(inp_ATOM* at, int num_atoms)
1932 : : {
1933 : : int i;
1934 [ + + ]: 12 : for ( i = 0; i < num_atoms; i++ )
1935 : : {
1936 : 10 : at[i].bCutVertex = 0;
1937 : 10 : at[i].nRingSystem = 0;
1938 : 10 : at[i].nNumAtInRingSystem = 0;
1939 : 10 : at[i].nBlockSystem = 0;
1940 : : }
1941 : :
1942 : 2 : return 0;
1943 : : }
1944 : :
1945 : :
1946 : : /****************************************************************************
1947 : : Preprocess OAD_Polymer (NB: frame shift is invoked from here)
1948 : : ****************************************************************************/
1949 : 0 : int OAD_Polymer_CyclizeCloseableUnits(ORIG_ATOM_DATA* orig_at_data,
1950 : : int treat_polymers,
1951 : : char* pStrErr,
1952 : : int bNoWarnings)
1953 : : {
1954 : 0 : int i, err = 0; /* djb-rwth: removing redundant variables */
1955 : :
1956 [ # # ]: 0 : for ( i = 0; i < orig_at_data->polymer->n; i++ )
1957 : : {
1958 : 0 : OAD_PolymerUnit* unit = orig_at_data->polymer->units[i];
1959 : :
1960 [ # # ]: 0 : if ( !unit->cyclizable )
1961 : : {
1962 : 0 : continue;
1963 : : }
1964 : :
1965 : : /* Find stars and their partners */
1966 : 0 : OAD_PolymerUnit_SetEndsAndCaps(unit, orig_at_data, &err, pStrErr);
1967 : : /* Reveal and store CRU caps and ends('stars and partners')
1968 : : Also set `unit->cap1_is_undef`, `unit->cap2_is_undef`, `unit->cyclizable`
1969 : : */
1970 [ # # ]: 0 : if ( err )
1971 : : {
1972 : 0 : break;
1973 : : }
1974 [ # # ]: 0 : if ( !unit->cyclizable )
1975 : : {
1976 : 0 : continue;
1977 : : }
1978 : :
1979 : :
1980 [ # # ]: 0 : if ( OAD_PolymerUnit_HasMetal(unit, orig_at_data->at) )
1981 : : {
1982 : : /*unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;*/
1983 [ # # ]: 0 : if ( unit->cyclizable == CLOSING_SRU_RING )
1984 : : {
1985 : : /*unit->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;*/
1986 [ # # ]: 0 : if ( !bNoWarnings )
1987 : : {
1988 : 0 : WarningMessage(pStrErr, "Frame shift in metallated polymer unit may be missed");
1989 : : }
1990 : : }
1991 : : }
1992 : :
1993 : : /* Now remove bonds to cap ("star atoms and cyclize a SRU */
1994 : 0 : OAD_PolymerUnit_UnlinkCapsAndConnectEndAtoms(unit, orig_at_data, &err, pStrErr);
1995 : :
1996 [ # # ]: 0 : if ( err )
1997 : : {
1998 : 0 : break;
1999 : : }
2000 [ # # ]: 0 : if ( !unit->cyclizable )
2001 : : {
2002 : 0 : continue;
2003 : : }
2004 : :
2005 : : /* djb-rwth: removing redundant code */
2006 : : }
2007 : :
2008 : : /*
2009 : : if ( ncyclized )
2010 : : {
2011 : : if (!bNoWarnings)
2012 : : {
2013 : : WarningMessage( pStrErr, "Made provision for frame shift in polymer unit(s)" );
2014 : : }
2015 : : }
2016 : : */
2017 : :
2018 : 0 : return err;
2019 : : }
2020 : :
2021 : :
2022 : : /****************************************************************************
2023 : : Check if SRU contains metal
2024 : : ****************************************************************************/
2025 : 0 : int OAD_PolymerUnit_HasMetal(OAD_PolymerUnit* u, inp_ATOM* at)
2026 : : {
2027 : : int i;
2028 [ # # ]: 0 : for ( i = 0; i < u->na; i++ )
2029 : : {
2030 [ # # ]: 0 : if ( is_el_a_metal(at[u->alist[i] - 1].el_number) )
2031 : : {
2032 : 0 : return 1;
2033 : : }
2034 : : }
2035 : :
2036 : 0 : return 0;
2037 : : }
2038 : :
2039 : :
2040 : : /****************************************************************************/
2041 : 491 : void OAD_Polymer_Free(OAD_Polymer* pd)
2042 : : {
2043 [ + + ]: 491 : if ( pd )
2044 : : {
2045 [ - + ]: 4 : if ( pd->pzz )
2046 : : {
2047 [ # # ]: 0 : inchi_free(pd->pzz);
2048 : 0 : pd->pzz = NULL;
2049 : 0 : pd->n_pzz = 0;
2050 : : }
2051 [ + - + - ]: 4 : if ( pd->n && pd->units )
2052 : : {
2053 : : int k;
2054 [ + + ]: 8 : for ( k = 0; k < pd->n; k++ )
2055 : : {
2056 : 4 : OAD_PolymerUnit_Free(pd->units[k]);
2057 : : }
2058 [ + - ]: 4 : inchi_free(pd->units);
2059 : 4 : pd->units = NULL;
2060 : 4 : pd->n = 0;
2061 : : }
2062 [ + - ]: 4 : inchi_free(pd);
2063 : 4 : pd = NULL;
2064 : : }
2065 : :
2066 : 491 : return;
2067 : : }
2068 : :
2069 : :
2070 : : /****************************************************************************/
2071 : 0 : void OAD_PolymerUnit_UnlinkCapsAndConnectEndAtoms(OAD_PolymerUnit* unit,
2072 : : ORIG_ATOM_DATA* orig_inp_data,
2073 : : int* err,
2074 : : char* pStrErr)
2075 : : {
2076 : : int bond_type, bond_stereo;
2077 : :
2078 : 0 : *err = 0;
2079 [ # # ]: 0 : if ( !unit->cyclizable )
2080 : : {
2081 : 0 : return;
2082 : : }
2083 : :
2084 [ # # ]: 0 : if ( unit->cyclizable == CLOSING_SRU_RING )
2085 : : {
2086 : : /* Disconnect both star atoms */
2087 : 0 : OrigAtData_RemoveBond(unit->cap1 - 1, unit->end_atom1 - 1, orig_inp_data->at,
2088 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2089 : :
2090 : 0 : OrigAtData_RemoveBond(unit->cap2 - 1, unit->end_atom2 - 1, orig_inp_data->at,
2091 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2092 : :
2093 : 0 : OrigAtData_AddSingleStereolessBond(unit->end_atom1 - 1, unit->end_atom2 - 1,
2094 : : orig_inp_data->at, &orig_inp_data->num_inp_bonds);
2095 : : }
2096 : :
2097 [ # # ]: 0 : else if ( unit->cyclizable == CLOSING_SRU_HIGHER_ORDER_BOND )
2098 : : {
2099 : : int elevated; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
2100 : 0 : elevated = OrigAtData_IncreaseBondOrder(unit->end_atom1 - 1, unit->end_atom2 - 1, orig_inp_data->at); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
2101 : : #if 0
2102 : : /* the bond may already be broken at metal disconnection, so ignore the result here */
2103 : : if ( !elevated )
2104 : : {
2105 : : /* *err = 1; */
2106 : : WarningMessage(pStrErr, "SRU closure via higher order bond failed");
2107 : : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2108 : : return;
2109 : : }
2110 : : #endif
2111 : 0 : OrigAtData_RemoveBond(unit->cap1 - 1, unit->end_atom1 - 1, orig_inp_data->at,
2112 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2113 : 0 : OrigAtData_RemoveBond(unit->cap2 - 1, unit->end_atom2 - 1, orig_inp_data->at,
2114 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2115 : : }
2116 : :
2117 [ # # ]: 0 : else if ( unit->cyclizable == CLOSING_SRU_DIRADICAL )
2118 : : {
2119 : 0 : orig_inp_data->at[unit->end_atom1 - 1].radical = RADICAL_TRIPLET;
2120 : 0 : OrigAtData_RemoveBond(unit->cap1 - 1, unit->end_atom1 - 1, orig_inp_data->at,
2121 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2122 : 0 : OrigAtData_RemoveBond(unit->cap2 - 1, unit->end_atom2 - 1, orig_inp_data->at,
2123 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2124 : : }
2125 : :
2126 [ # # ]: 0 : if ( !*err )
2127 : : {
2128 : 0 : unit->cyclized = 1;
2129 : : }
2130 : :
2131 : 0 : return;
2132 : : }
2133 : :
2134 : :
2135 : : /****************************************************************************/
2136 : 2 : void OAD_PolymerUnit_FindEndsAndCaps(OAD_PolymerUnit* unit,
2137 : : ORIG_ATOM_DATA* orig_at_data,
2138 : : int* end1,
2139 : : int* cap1,
2140 : : int* cap1_is_star,
2141 : : int* end2,
2142 : : int* cap2,
2143 : : int* cap2_is_star,
2144 : : int* err,
2145 : : char* pStrErr)
2146 : : {
2147 : 2 : int i, j, i_inside = 0, j_inside = 0;
2148 : 2 : int num_atoms = orig_at_data->num_inp_atoms;
2149 : :
2150 : 2 : *end1 = *end2 = *cap1 = *cap2 = 0;
2151 : 2 : *cap1_is_star = *cap2_is_star = 0;
2152 : 2 : *err = 0;
2153 : :
2154 [ + - - + ]: 2 : if ( !unit->blist || unit->nb < 1 )
2155 : : {
2156 : 0 : return;
2157 : : }
2158 : : /* Left crossing bond */
2159 : 2 : i = unit->blist[0];
2160 : 2 : j = unit->blist[1];
2161 : 2 : i_inside = (NULL != is_in_the_ilist(unit->alist, i, unit->na));
2162 : 2 : j_inside = (NULL != is_in_the_ilist(unit->alist, j, unit->na));
2163 [ - + - - ]: 2 : if ( i_inside && j_inside )
2164 : : {
2165 [ # # ]: 0 : TREAT_ERR(*err, 9032, "Polymer CRU cap(s) lie inside CRU");
2166 : 0 : return;
2167 : : }
2168 [ - + ]: 2 : if ( i_inside )
2169 : : {
2170 : 0 : *end1 = i;
2171 : 0 : *cap1 = j;
2172 : : }
2173 : : else
2174 : : {
2175 : 2 : *end1 = j;
2176 : 2 : *cap1 = i;
2177 : : }
2178 [ - + ]: 2 : if ( !strcmp(orig_at_data->at[*cap1 - 1].elname, "Zz") )
2179 : : {
2180 : 0 : *cap1_is_star = 1;
2181 : : }
2182 : : /* Right crossing bond */
2183 : 2 : i = unit->blist[2];
2184 : 2 : j = unit->blist[3];
2185 : 2 : i_inside = NULL != is_in_the_ilist(unit->alist, i, unit->na);
2186 : 2 : j_inside = NULL != is_in_the_ilist(unit->alist, j, unit->na);
2187 [ + - - + ]: 2 : if ( i_inside && j_inside )
2188 : : {
2189 [ # # ]: 0 : TREAT_ERR(*err, 9032, "Polymer CRU cap(s) lie inside CRU");
2190 : : }
2191 [ + - ]: 2 : if ( i_inside )
2192 : : {
2193 : 2 : *end2 = i;
2194 : 2 : *cap2 = j;
2195 : : }
2196 : : else
2197 : : {
2198 : 0 : *end2 = j;
2199 : 0 : *cap2 = i;
2200 : : }
2201 [ - + ]: 2 : if ( !strcmp(orig_at_data->at[*cap2 - 1].elname, "Zz") )
2202 : : {
2203 : 0 : *cap2_is_star = 1;
2204 : : }
2205 : : /* Checks */
2206 [ + - + - : 2 : if ( *end1 <= 0 || *end1 > num_atoms || *cap1 <= 0 || *cap1 > num_atoms )
+ - - + ]
2207 : : {
2208 [ # # ]: 0 : TREAT_ERR(*err, 9090, "Invalid polymer CRU crossing bond");
2209 : 0 : return;
2210 : : }
2211 [ + - + - : 2 : if ( *end2 <= 0 || *end2 > num_atoms || *cap2 <= 0 || *cap2 > num_atoms )
+ - - + ]
2212 : : {
2213 [ # # ]: 0 : TREAT_ERR(*err, 9091, "Invalid polymer CRU crossing bond");
2214 : 0 : return;
2215 : : }
2216 [ - + ]: 2 : if ( *cap1 == *cap2 ) /* || (*end1 == *end2 && unit->na>1) */
2217 : : {
2218 [ # # ]: 0 : TREAT_ERR(*err, 9090, "Invalid polymer CRU surrounding");
2219 : 0 : return;
2220 : : }
2221 : :
2222 : : /* Paranoia, 2020-05-22 */
2223 : 2 : unit->end_atom1 = *end1;
2224 : 2 : unit->end_atom2 = *end2;
2225 : 2 : unit->cap1 = *cap1;
2226 : 2 : unit->cap2 = *cap2;
2227 : :
2228 : 2 : *err = 0;
2229 : 2 : return;
2230 : : }
2231 : :
2232 : :
2233 : : /****************************************************************************
2234 : : Reveal and store CRU caps and ends ('stars and partners')
2235 : : ****************************************************************************/
2236 : 2 : void OAD_PolymerUnit_SetEndsAndCaps(OAD_PolymerUnit* unit,
2237 : : ORIG_ATOM_DATA* orig_at_data,
2238 : : int* err,
2239 : : char* pStrErr)
2240 : : {
2241 : : int k;
2242 : :
2243 : 2 : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2244 : 2 : unit->end_atom1 = unit->end_atom2 = unit->cap1 = unit->cap2 = -1;
2245 : 2 : unit->cap1_is_undef = unit->cap2_is_undef = 0;
2246 : :
2247 : 2 : OAD_PolymerUnit_FindEndsAndCaps(unit, orig_at_data,
2248 : : &unit->end_atom1, &unit->cap1, &unit->cap1_is_undef,
2249 : : &unit->end_atom2, &unit->cap2, &unit->cap2_is_undef,
2250 : : err, pStrErr);
2251 : :
2252 [ - + ]: 2 : if ( *err )
2253 : : {
2254 : 0 : goto exit_function;
2255 : : }
2256 : :
2257 : : #if ( defined(DEBUG_POLYMERS) && ( DEBUG_POLYMERS != 0 ) )
2258 : : ITRACE_("Cap-end_atom pairs (numbers are from 1) are: %-d-%-d and %-d-%-d\n",
2259 : : unit->cap1, unit->end_atom1, unit->cap2, unit->end_atom2);
2260 : : #endif
2261 : :
2262 [ + - + - ]: 2 : if ( !unit->cap1_is_undef && !unit->cap2_is_undef )
2263 : : {
2264 : 2 : goto exit_function;
2265 : : }
2266 : :
2267 : : /* The rest is applicable only to *---SRU---* case */
2268 : :
2269 : : /* Stars are separated by one atom - that's not error but do nothing */
2270 [ # # ]: 0 : if ( unit->end_atom1 == unit->end_atom2 )
2271 : : {
2272 : : #ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL
2273 : 0 : unit->cyclizable = CLOSING_SRU_DIRADICAL;
2274 : : #else
2275 : : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2276 : : #endif
2277 : 0 : goto exit_function;
2278 : : }
2279 : :
2280 : : /* Stars are separated by two atoms - that's not error but do nothing */
2281 [ # # ]: 0 : for ( k = 0; k < orig_at_data->at[unit->end_atom1 - 1].valence; k++ )
2282 : : {
2283 [ # # ]: 0 : if ( orig_at_data->at[unit->end_atom1 - 1].neighbor[k] == unit->end_atom2 - 1 )
2284 : : {
2285 : : #ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND
2286 : 0 : unit->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;
2287 : : #else
2288 : : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2289 : : #endif
2290 : 0 : goto exit_function;
2291 : : }
2292 : : }
2293 : :
2294 : 0 : unit->cyclizable = CLOSING_SRU_RING;
2295 : :
2296 : 2 : exit_function:
2297 : :
2298 : 2 : return;
2299 : : }
2300 : :
2301 : :
2302 : : /****************************************************************************
2303 : : Replace original atom numbers in polymer data with (canonical num + 1)
2304 : : Then prepare:
2305 : : units2 a copy of original polymer units (p->units) with
2306 : : atomic numbers changed to curr canonical ones;
2307 : : atoms in alists sorted; atoms in blists
2308 : : and blists themselves are sorted
2309 : : unum numbers of units (0..p->n) as they go when
2310 : : sorted by alist's in lexicographic orders
2311 : : ****************************************************************************/
2312 : 2 : int OAD_Polymer_PrepareWorkingSet(OAD_Polymer* p,
2313 : : int* cano_nums,
2314 : : int* compnt_nums,
2315 : : OAD_PolymerUnit** units2, /* allocd by caller, to be filled */
2316 : : int* unum) /* allocd by caller, to be filled */
2317 : :
2318 : : {
2319 : 2 : int i, k, err = 0, cano_num1 = -1, cano_num2 = -1;
2320 : : OAD_PolymerUnit* u;
2321 : :
2322 : : /*OAD_Polymer_DebugTrace( p );*/
2323 : :
2324 : : /* Replace original atom numbers in polymer data with canonical plus 1. */
2325 : : /* Note that we use 'cano1 nums', that is, 1-based (InChI internal 'cano nums' are 0-based)*/
2326 : : /* Also remove from the list atoms who mapped to cano number 0 ( == -1 + 1_offset ), */
2327 : : /* they are explicit H's which have already been deleted. */
2328 [ - + ]: 2 : for ( k = 0; k < p->n_pzz; k++ )
2329 : : {
2330 : 0 : cano_num1 = cano_nums[p->pzz[k]] + 1;
2331 [ # # ]: 0 : if ( cano_num1 == 0 )
2332 : : {
2333 : : /* we shouldn't arrive here */
2334 : 0 : err = 10;
2335 : 0 : goto exit_function;
2336 : : }
2337 : 0 : p->pzz[k] = cano_num1;
2338 : : }
2339 : :
2340 [ + + ]: 4 : for ( i = 0; i < p->n; i++ )
2341 : : {
2342 : 2 : int na_new = -1;
2343 : 2 : u = units2[i];
2344 : :
2345 [ + + ]: 8 : for ( k = 0; k < u->na; k++ )
2346 : : {
2347 : 6 : cano_num1 = cano_nums[u->alist[k]] + 1;
2348 [ - + ]: 6 : if ( cano_num1 == 0 )
2349 : : {
2350 : 0 : continue;
2351 : : }
2352 : 6 : u->alist[++na_new] = cano_num1;
2353 : : }
2354 : 2 : u->na = na_new + 1;
2355 [ + + ]: 10 : for ( k = 0; k < 2 * u->nb; k++ )
2356 : : {
2357 : 8 : cano_num1 = cano_nums[u->blist[k]] + 1;
2358 [ - + ]: 8 : if ( cano_num1 == 0 )
2359 : : {
2360 : : /* Can not proceed further as one of PU crossing bond ends
2361 : : leads to explicit H which has been removed already */
2362 : 0 : err = 11;
2363 : 0 : goto exit_function;
2364 : : }
2365 : 8 : u->blist[k] = cano_num1;
2366 : : }
2367 : :
2368 : 2 : cano_num1 = cano_nums[u->cap1] + 1;
2369 [ - + ]: 2 : if ( cano_num1 == 0 )
2370 : : {
2371 : 0 : err = 11;
2372 : 0 : goto exit_function;
2373 : : }
2374 : 2 : u->cap1 = cano_num1;
2375 : :
2376 : 2 : cano_num1 = cano_nums[u->cap2] + 1;
2377 [ - + ]: 2 : if ( cano_num1 == 0 )
2378 : : {
2379 : 0 : err = 11;
2380 : 0 : goto exit_function;
2381 : : }
2382 : 2 : u->cap2 = cano_num1;
2383 : :
2384 : 2 : cano_num1 = cano_nums[u->end_atom1] + 1;
2385 [ - + ]: 2 : if ( cano_num1 == 0 )
2386 : : {
2387 : 0 : err = 11;
2388 : 0 : goto exit_function;
2389 : : }
2390 : 2 : u->end_atom1 = cano_num1;
2391 : :
2392 : 2 : cano_num1 = cano_nums[u->end_atom2] + 1;
2393 [ - + ]: 2 : if ( cano_num1 == 0 )
2394 : : {
2395 : 0 : err = 11;
2396 : 0 : goto exit_function;
2397 : : }
2398 : 2 : u->end_atom2 = cano_num1;
2399 : :
2400 [ - + ]: 2 : for ( k = 0; k < u->nbkbonds; k++ )
2401 : : {
2402 : 0 : cano_num1 = cano_nums[u->bkbonds[k][0]] + 1;
2403 [ # # ]: 0 : if ( cano_num1 == 0 )
2404 : : {
2405 : 0 : continue;
2406 : : }
2407 : 0 : cano_num2 = cano_nums[u->bkbonds[k][1]] + 1;
2408 [ # # ]: 0 : if ( cano_num2 == 0 )
2409 : : {
2410 : 0 : continue;
2411 : : }
2412 : 0 : u->bkbonds[k][0] = inchi_min(cano_num1, cano_num2);
2413 : 0 : u->bkbonds[k][1] = inchi_max(cano_num1, cano_num2);
2414 : : }
2415 : : }
2416 : :
2417 : : /* Sort the atoms and the bonds in all units */
2418 [ + + ]: 4 : for ( i = 0; i < p->n; i++ )
2419 : : {
2420 : 2 : u = units2[i];
2421 : :
2422 : : /* sort atoms (alist) */
2423 : 2 : iisort(u->alist, u->na);
2424 : :
2425 : : /*ITRACE_( "\n*** Polymer unit %-d : ( ", i );
2426 : : for (k = 0; k < u->na - 1; k++)
2427 : : {
2428 : : ITRACE_( "%-d-", u->alist[k] );
2429 : : }
2430 : : ITRACE_( "%-d )\n", u->alist[u->na - 1] );*/
2431 : :
2432 : : /* Sort bonds (blist) */
2433 : 2 : err = OAD_PolymerUnit_OrderBondAtomsAndBondsThemselves(u, p->n_pzz, p->pzz);
2434 [ - + ]: 2 : if ( err )
2435 : : {
2436 : : /* crossing bonds in blist are invalid */
2437 : 0 : err = 12;
2438 : 0 : goto exit_function;
2439 : : }
2440 : :
2441 : : /* Check each unit for >1 connected components */
2442 : : #if 0
2443 : : {
2444 : : int icompnt;
2445 : : icompnt = compnt_nums[u->alist[0] - 1];
2446 : : for ( k = 1; k < u->na; k++ )
2447 : : {
2448 : : if ( compnt_nums[u->alist[k] - 1] != icompnt )
2449 : : {
2450 : : u->disjoint = 1;
2451 : : break;
2452 : : }
2453 : : }
2454 : : }
2455 : : #endif
2456 : :
2457 : : }
2458 : :
2459 : : /* Sort all units in modified alist's lexicographic order
2460 : : (modification is: longer list always go first ) */
2461 [ + + ]: 4 : for ( i = 0; i < p->n; i++ )
2462 : : {
2463 : 2 : unum[i] = i;
2464 : : }
2465 [ - + ]: 2 : for ( i = 1; i < p->n; i++ )
2466 : : {
2467 : 0 : int tmp = unum[i];
2468 : 0 : int j = i - 1;
2469 [ # # # # ]: 0 : while ( j >= 0 && OAD_PolymerUnit_CompareAtomListsMod(units2[unum[j]], units2[tmp]) > 0 )
2470 : : /*while ( j >= 0 && OAD_PolymerUnit_CompareAtomLists( units2[ unum[j] ], units2[ tmp ] ) > 0 )*/
2471 : : {
2472 : 0 : unum[j + 1] = unum[j];
2473 : 0 : j--;
2474 : : }
2475 : 0 : unum[j + 1] = tmp;
2476 : : }
2477 : :
2478 : 2 : exit_function:
2479 : :
2480 : 2 : return err;
2481 : : }
2482 : :
2483 : :
2484 : : /****************************************************************************
2485 : : Helper for cyclizing CRU. NB: 0-based
2486 : : ****************************************************************************/
2487 : 0 : int OrigAtData_RemoveHalfBond(int this_atom,
2488 : : int other_atom,
2489 : : inp_ATOM* at,
2490 : : int* bond_type,
2491 : : int* bond_stereo)
2492 : : {
2493 : : int k, kk;
2494 : : /* djb-rwth: fixing oss-fuzz issues #68286, #30342 */
2495 [ # # # # : 0 : if ( at && (this_atom >= 0) && (other_atom >= 0) )
# # ]
2496 : : {
2497 : 0 : inp_ATOM* a = &(at[this_atom]);
2498 [ # # ]: 0 : if ( a )
2499 : : {
2500 [ # # ]: 0 : for ( k = 0; k < a->valence; k++ )
2501 : : {
2502 [ # # ]: 0 : if ( a->neighbor[k] != other_atom )
2503 : : {
2504 : 0 : continue;
2505 : : }
2506 : :
2507 : 0 : *bond_type = a->bond_type[k];
2508 : 0 : *bond_stereo = a->bond_stereo[k];
2509 : :
2510 : 0 : a->neighbor[k] = a->bond_type[k] = a->bond_stereo[k] = 0;
2511 : :
2512 [ # # ]: 0 : for ( kk = k + 1; kk < a->valence; kk++ )
2513 : : {
2514 : 0 : a->neighbor[kk - 1] = a->neighbor[kk];
2515 : 0 : a->bond_type[kk - 1] = a->bond_type[kk];
2516 : 0 : a->bond_stereo[kk - 1] = a->bond_stereo[kk];
2517 : : }
2518 [ # # ]: 0 : for ( kk = a->valence - 1; kk < MAXVAL; kk++ )
2519 : : {
2520 : 0 : a->neighbor[kk] = 0;
2521 : 0 : a->bond_type[kk] = (U_CHAR)0;
2522 : 0 : a->bond_stereo[kk] = (S_CHAR)0;
2523 : : }
2524 : 0 : return 1;
2525 : : } /* k */
2526 : : }
2527 : : }
2528 : :
2529 : 0 : return 0;
2530 : : }
2531 : :
2532 : :
2533 : : /****************************************************************************/
2534 : 0 : int OrigAtData_RemoveAtom(ORIG_ATOM_DATA* orig_at_data, int iatom)
2535 : : {
2536 : :
2537 : : if ( 0 )
2538 : : {
2539 : : return 1;
2540 : : }
2541 : :
2542 : 0 : return 0;
2543 : : }
2544 : :
2545 : :
2546 : : /****************************************************************************/
2547 : 0 : int OrigAtData_RemoveBond(int this_atom,
2548 : : int other_atom,
2549 : : inp_ATOM* at,
2550 : : int* bond_type,
2551 : : int* bond_stereo,
2552 : : int* num_inp_bonds)
2553 : : {
2554 : 0 : int del = 0;
2555 : :
2556 [ # # # # : 0 : if ( at && (this_atom >= 0) && (other_atom >= 0) ) /* djb-rwth: fixing oss-fuzz issue #68329, #68286 */
# # ]
2557 : : {
2558 : 0 : del = OrigAtData_RemoveHalfBond(this_atom, other_atom, at, bond_type, bond_stereo);
2559 : 0 : del += OrigAtData_RemoveHalfBond(other_atom, this_atom, at, bond_type, bond_stereo);
2560 : :
2561 [ # # ]: 0 : if ( del == 2 )
2562 : : {
2563 : 0 : (*num_inp_bonds)--;
2564 : 0 : at[this_atom].valence--;
2565 : 0 : at[this_atom].chem_bonds_valence -= *bond_type;
2566 : 0 : at[other_atom].valence--;
2567 : 0 : at[other_atom].chem_bonds_valence -= *bond_type;
2568 : 0 : return 1;
2569 : : }
2570 : : }
2571 : :
2572 : 0 : return 0;
2573 : : }
2574 : :
2575 : :
2576 : : /****************************************************************************/
2577 : 0 : int OrigAtData_AddBond(int this_atom,
2578 : : int other_atom,
2579 : : inp_ATOM* at,
2580 : : int bond_type,
2581 : : int bond_stereo,
2582 : : int* num_bonds)
2583 : : {
2584 [ # # ]: 0 : if ( at )
2585 : : {
2586 : : /* djb-rwth: fixing oss-fuzz issue #68286 */
2587 : : int i, k, already_here;
2588 : 0 : inp_ATOM* a = &(at[this_atom]);
2589 : :
2590 [ # # ]: 0 : if ( at[this_atom].valence >= MAXVAL ||
2591 [ # # ]: 0 : at[other_atom].valence >= MAXVAL )
2592 : : {
2593 : 0 : return 0;
2594 : : }
2595 : :
2596 [ # # # # ]: 0 : if ( bond_type != INCHI_BOND_TYPE_DOUBLE && bond_type != INCHI_BOND_TYPE_TRIPLE )
2597 : : {
2598 : 0 : bond_type = INCHI_BOND_TYPE_SINGLE;
2599 : : }
2600 : :
2601 : 0 : k = a->valence;
2602 : 0 : already_here = 0;
2603 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2604 : : {
2605 [ # # ]: 0 : if ( a->neighbor[i] == other_atom )
2606 : : {
2607 : 0 : already_here = 1; break;
2608 : : }
2609 : : }
2610 : :
2611 [ # # ]: 0 : if ( !already_here )
2612 : : {
2613 : 0 : a->neighbor[k] = other_atom;
2614 : 0 : a->bond_type[k] = (U_CHAR)bond_type;
2615 : 0 : a->bond_stereo[k] = (S_CHAR)bond_stereo;
2616 : 0 : a->chem_bonds_valence += bond_type;
2617 : 0 : a->valence++;
2618 : : }
2619 : :
2620 : 0 : a = &(at[other_atom]);
2621 : 0 : k = a->valence;
2622 : 0 : already_here = 0;
2623 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2624 : : {
2625 [ # # ]: 0 : if ( a->neighbor[i] == this_atom )
2626 : : {
2627 : 0 : already_here = 1; break;
2628 : : }
2629 : : }
2630 : :
2631 [ # # # # ]: 0 : if ( !already_here && (k < MAXVAL) ) /* djb-rwth: condition added to prevent buffer overrun */
2632 : : {
2633 : 0 : a->neighbor[k] = this_atom;
2634 : 0 : a->bond_type[k] = (U_CHAR)bond_type;
2635 : 0 : a->bond_stereo[k] = (S_CHAR)bond_stereo;
2636 : 0 : a->chem_bonds_valence += bond_type;
2637 : 0 : a->valence++;
2638 : : }
2639 : :
2640 : 0 : (*num_bonds)++;
2641 : :
2642 : 0 : return 1;
2643 : : }
2644 : : else
2645 : : {
2646 : 0 : return 0;
2647 : : }
2648 : : }
2649 : :
2650 : :
2651 : : /****************************************************************************/
2652 : 0 : int OrigAtData_AddSingleStereolessBond(int this_atom,
2653 : : int other_atom,
2654 : : inp_ATOM* at,
2655 : : int* num_bonds)
2656 : : {
2657 : 0 : return OrigAtData_AddBond(this_atom, other_atom, at, INCHI_BOND_TYPE_SINGLE, 0, num_bonds);
2658 : : }
2659 : :
2660 : :
2661 : : /****************************************************************************/
2662 : 0 : int OrigAtData_IncreaseBondOrder(int this_atom, int other_atom, inp_ATOM* at)
2663 : : {
2664 : 0 : int i, k, n_up = 0;
2665 : : inp_ATOM* a;
2666 : :
2667 [ # # ]: 0 : if ( at[this_atom].valence >= MAXVAL ||
2668 [ # # ]: 0 : at[other_atom].valence >= MAXVAL )
2669 : : {
2670 : 0 : return 0;
2671 : : }
2672 : :
2673 : 0 : a = &(at[this_atom]);
2674 [ # # ]: 0 : if ( a->chem_bonds_valence > MAXVAL - 1 )
2675 : : {
2676 : 0 : return 0;
2677 : : }
2678 : :
2679 : 0 : k = a->valence;
2680 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2681 : : {
2682 [ # # ]: 0 : if ( a->neighbor[i] != other_atom )
2683 : 0 : continue;
2684 [ # # ]: 0 : if ( a->bond_type[i] > 3 )
2685 : 0 : return 0;
2686 : 0 : a->bond_type[i]++;
2687 : 0 : a->chem_bonds_valence++;
2688 : 0 : n_up++;
2689 : 0 : break;
2690 : : }
2691 : :
2692 : 0 : a = &(at[other_atom]);
2693 [ # # ]: 0 : if ( a->chem_bonds_valence > MAXVAL - 1 )
2694 : : {
2695 : 0 : return 0;
2696 : : }
2697 : 0 : k = a->valence;
2698 : :
2699 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2700 : : {
2701 [ # # ]: 0 : if ( a->neighbor[i] != this_atom )
2702 : : {
2703 : 0 : continue;
2704 : : }
2705 [ # # ]: 0 : if ( a->bond_type[i] > 3 )
2706 : : {
2707 : 0 : return 0;
2708 : : }
2709 : 0 : a->bond_type[i]++;
2710 : 0 : a->chem_bonds_valence++;
2711 : 0 : n_up++;
2712 : 0 : break;
2713 : : }
2714 : :
2715 : 0 : return n_up;
2716 : : }
2717 : :
2718 : :
2719 : : /****************************************************************************/
2720 : 0 : int OrigAtData_DecreaseBondOrder(int this_atom,
2721 : : int other_atom,
2722 : : inp_ATOM* at)
2723 : : {
2724 : 0 : int i, k, n_dn = 0;
2725 : : inp_ATOM* a;
2726 : :
2727 : 0 : a = &(at[this_atom]);
2728 [ # # ]: 0 : if ( a->chem_bonds_valence > MAXVAL - 1 )
2729 : : {
2730 : 0 : return 0;
2731 : : }
2732 : :
2733 : 0 : k = a->valence;
2734 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2735 : : {
2736 [ # # ]: 0 : if ( a->neighbor[i] != other_atom )
2737 : : {
2738 : 0 : continue;
2739 : : }
2740 [ # # ]: 0 : if ( a->bond_type[i] < 2 )
2741 : : {
2742 : 0 : return 0;
2743 : : }
2744 : 0 : a->bond_type[i]--;
2745 : 0 : a->chem_bonds_valence--;
2746 : 0 : n_dn++;
2747 : 0 : break;
2748 : : }
2749 : :
2750 : 0 : a = &(at[other_atom]);
2751 : 0 : k = a->valence;
2752 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2753 : : {
2754 [ # # ]: 0 : if ( a->neighbor[i] != this_atom )
2755 : : {
2756 : 0 : continue;
2757 : : }
2758 [ # # ]: 0 : if ( a->bond_type[i] < 2 )
2759 : : {
2760 : 0 : return 0;
2761 : : }
2762 : 0 : a->bond_type[i]--;
2763 : 0 : a->chem_bonds_valence--;
2764 : 0 : n_dn++;
2765 : 0 : break;
2766 : : }
2767 : :
2768 : 0 : return n_dn;
2769 : : }
2770 : :
2771 : :
2772 : : /****************************************************************************
2773 : : Collect bonds and optionally atoms of fragment
2774 : : ****************************************************************************/
2775 : 0 : void OAD_CollectFragmentBondsAndAtoms(ORIG_ATOM_DATA* orig_at_data,
2776 : : int nforbidden, /* number of edges forbidden for traversal */
2777 : : int* forbidden_orig, /* atom nums of forbidden edges */
2778 : : /* [edge1at1,edge1at2, edge2at1, edge2at2, ... ] */
2779 : : int* n_fragbonds,
2780 : : int** fragbonds,
2781 : : int* n_fragatoms,
2782 : : int* fragatoms,
2783 : : int* err,
2784 : : char* pStrErr)
2785 : : {
2786 : : int i;
2787 : 0 : int max_atoms = orig_at_data->num_inp_atoms;
2788 : 0 : int* atnums = NULL;
2789 : 0 : subgraf* sg = NULL;
2790 : 0 : subgraf_pathfinder* spf = NULL;
2791 : :
2792 : 0 : *err = 0;
2793 : 0 : atnums = (int*)inchi_calloc(max_atoms, sizeof(int));
2794 [ # # ]: 0 : if ( !atnums )
2795 : : {
2796 [ # # ]: 0 : TREAT_ERR(*err, 9045, "Not enough memory");
2797 : 0 : goto exit_function;
2798 : : }
2799 [ # # ]: 0 : for ( i = 0; i < max_atoms; i++ )
2800 : : {
2801 : 0 : atnums[i] = orig_at_data->at[i].orig_at_number; /* i+1 normally*/
2802 : : }
2803 : :
2804 : 0 : sg = subgraf_new(orig_at_data, max_atoms, atnums);
2805 [ # # ]: 0 : if ( !sg )
2806 : : {
2807 [ # # ]: 0 : TREAT_ERR(*err, 9045, "Not enough memory");
2808 : 0 : goto exit_function;
2809 : : }
2810 : 0 : spf = subgraf_pathfinder_new(sg, orig_at_data, 0, 0); /* start = end = 0th node */
2811 [ # # ]: 0 : if ( !spf )
2812 : : {
2813 [ # # ]: 0 : TREAT_ERR(*err, 9045, "Not enough memory");
2814 : 0 : goto exit_function;
2815 : : }
2816 : :
2817 : 0 : spf->seen[0] = spf->start;
2818 : 0 : spf->nseen = 1;
2819 : 0 : *n_fragbonds = 0;
2820 : 0 : *n_fragatoms = 0;
2821 : :
2822 : 0 : subgraf_pathfinder_run(spf,
2823 : : nforbidden, forbidden_orig, /* this corrects cinnectivity of subgraf... */
2824 : : n_fragbonds, fragbonds,
2825 : : n_fragatoms, fragatoms);
2826 : :
2827 : :
2828 : 0 : exit_function:
2829 : 0 : subgraf_free(sg);
2830 : 0 : subgraf_pathfinder_free(spf);
2831 [ # # ]: 0 : if ( atnums )
2832 : : {
2833 [ # # ]: 0 : inchi_free(atnums);
2834 : : }
2835 : 0 : return;
2836 : : }
2837 : :
2838 : :
2839 : : /****************************************************************************
2840 : : For all CRUs detect the bonds potentially involved in frame shift
2841 : : ****************************************************************************/
2842 : 0 : void OAD_Polymer_FindBackbones(ORIG_ATOM_DATA* at_data,
2843 : : COMP_ATOM_DATA* composite_norm_data,
2844 : : int* err,
2845 : : char* pStrErr)
2846 : : {
2847 : : int i;
2848 : :
2849 : 0 : *err = 0;
2850 [ # # ]: 0 : for ( i = 0; i < at_data->polymer->n; i++ )
2851 : : {
2852 [ # # ]: 0 : if ( !at_data->polymer->units[i]->cyclizable )
2853 : : {
2854 : 0 : continue;
2855 : : }
2856 : :
2857 : 0 : OAD_CollectBackboneBonds(at_data,
2858 : 0 : at_data->polymer->units[i]->na,
2859 : 0 : at_data->polymer->units[i]->alist,
2860 : 0 : at_data->polymer->units[i]->end_atom1,
2861 : 0 : at_data->polymer->units[i]->end_atom2,
2862 : 0 : &(at_data->polymer->units[i]->nbkbonds),
2863 : 0 : at_data->polymer->units[i]->bkbonds,
2864 : : err, pStrErr);
2865 [ # # ]: 0 : if ( *err )
2866 : : {
2867 : 0 : at_data->polymer->units[i]->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2868 : 0 : continue;
2869 : : }
2870 [ # # ]: 0 : if ( at_data->polymer->units[i]->nbkbonds < 1 )
2871 : : {
2872 : 0 : continue;
2873 : : }
2874 [ # # ]: 0 : if ( at_data->polymer->units[i]->nbkbonds == 1 )
2875 : : {
2876 : : /* Special case: we got only one bond between end_atom1 and */
2877 : : /* end_atom2 (this may be the result of metal disconnection) */
2878 : 0 : continue;
2879 : : }
2880 : :
2881 : 0 : OAD_PolymerUnit_DelistIntraRingBackboneBonds(at_data->polymer->units[i], at_data, err, pStrErr);
2882 [ # # ]: 0 : if ( *err )
2883 : : {
2884 : 0 : continue;
2885 : : }
2886 : :
2887 : 0 : OAD_PolymerUnit_DelistHighOrderBackboneBonds(at_data->polymer->units[i],
2888 : : at_data, composite_norm_data,
2889 : : err, pStrErr);
2890 [ # # ]: 0 : if ( *err )
2891 : : {
2892 : 0 : continue;
2893 : : }
2894 [ # # ]: 0 : if ( at_data->polymer->units[i]->nbkbonds == 0 )
2895 : : {
2896 : : /* We already cyclized frame-shiftable unit and preprocessed it (in 'prep_inp_data'). */
2897 : : /* Despite that, now we discovered that there are no bonds eligible for frame shift */
2898 : : /* (as all potentially eligible in-unit bonds are either in-ring or alternate ones). */
2899 : : /* We can not simply restore original connections as the structure may have been already heavily touched. */
2900 : : /* The most viable action is to hold a single frame-shift bond (between original caps of CRU ends). */
2901 : : /* It is for sure will be converted to original bonds to star atoms on possible inchi2struct. */
2902 : 0 : at_data->polymer->units[i]->cyclizable = 1;
2903 : 0 : at_data->polymer->units[i]->nbkbonds = 1;
2904 : 0 : at_data->polymer->units[i]->bkbonds[0][0] = at_data->polymer->units[i]->end_atom1;
2905 : 0 : at_data->polymer->units[i]->bkbonds[0][1] = at_data->polymer->units[i]->end_atom2;
2906 : : }
2907 : : }
2908 : :
2909 : 0 : return;
2910 : : }
2911 : :
2912 : :
2913 : : /****************************************************************************
2914 : : Collect all backbone atoms - of main chain(s), side chains being ignored
2915 : : ****************************************************************************/
2916 : 0 : void OAD_CollectBackboneAtoms(ORIG_ATOM_DATA* at_data,
2917 : : int na,
2918 : : int* alist,
2919 : : int end_atom1,
2920 : : int end_atom2,
2921 : : int* nbkatoms,
2922 : : int* bkatoms,
2923 : : int* err,
2924 : : char* pStrErr)
2925 : : {
2926 : 0 : int start = 0, end = 0;
2927 : 0 : subgraf* sg = NULL;
2928 : 0 : subgraf_pathfinder* spf = NULL;
2929 : 0 : int nbkbonds = 0;
2930 : 0 : int** bkbonds = NULL; /* list of [breakable] backbone bonds [ (a1,a2), (a3,a4), ... ] */
2931 : : int maxbkbonds;
2932 : :
2933 : 0 : *nbkatoms = 0;
2934 : 0 : maxbkbonds = at_data->num_inp_bonds + 2;
2935 : 0 : *err = imat_new(maxbkbonds, 2, &(bkbonds));
2936 : : /* djb-rwth: addressing coverity ID #499570 -- TREAT_ERR properly used in all cases */
2937 [ # # ]: 0 : if (*err)
2938 : : {
2939 [ # # ]: 0 : TREAT_ERR(*err, 9034, "Not enough memory (polymers)");
2940 : 0 : goto exit_function;
2941 : : }
2942 : 0 : nbkbonds = 0;
2943 : 0 : sg = subgraf_new(at_data, na, alist);
2944 [ # # ]: 0 : if ( !sg )
2945 : : {
2946 [ # # ]: 0 : TREAT_ERR(*err, 9037, "Not enough memory (polymers)");
2947 : 0 : goto exit_function;
2948 : : }
2949 : :
2950 : 0 : start = sg->orig2node[end_atom1]; end = sg->orig2node[end_atom2];
2951 [ # # ]: 0 : if ( start > end )
2952 : : {
2953 : 0 : int tmp = end;
2954 : 0 : end = start;
2955 : 0 : start = tmp;
2956 : : }
2957 : 0 : spf = subgraf_pathfinder_new(sg, at_data, start, end);
2958 [ # # ]: 0 : if ( !spf )
2959 : : {
2960 [ # # ]: 0 : TREAT_ERR(*err, 9039, "Not enough memory (polymers)");
2961 : 0 : goto exit_function;
2962 : : }
2963 : :
2964 : 0 : spf->seen[0] = spf->start; spf->nseen = 1;
2965 : 0 : nbkbonds = 0;
2966 : 0 : *nbkatoms = 0;
2967 : :
2968 : 0 : subgraf_pathfinder_run(spf, 0, NULL, &nbkbonds, bkbonds, nbkatoms, bkatoms);
2969 : :
2970 : 0 : subgraf_free(sg);
2971 : 0 : subgraf_pathfinder_free(spf);
2972 : 0 : *err = 0;
2973 : :
2974 : :
2975 : 0 : exit_function:
2976 [ # # ]: 0 : if ( bkbonds )
2977 : : {
2978 : 0 : imat_free(maxbkbonds, bkbonds);
2979 : 0 : bkbonds = NULL;
2980 : : }
2981 : :
2982 : 0 : return;
2983 : : }
2984 : :
2985 : :
2986 : : /****************************************************************************
2987 : : Collect all atoms reachable from start_atom
2988 : : ****************************************************************************/
2989 : 0 : int OAD_CollectReachableAtoms(ORIG_ATOM_DATA* orig_at_data,
2990 : : int start_atom,
2991 : : int nforbidden_bonds,
2992 : : int* forbidden_bond_atoms,
2993 : : int* n_reachable,
2994 : : int* reachable,
2995 : : int* err,
2996 : : char* pStrErr)
2997 : : {
2998 : 0 : int iatom, natnums, max_atoms, j, ret = _IS_OKAY;
2999 : 0 : int max_n_reachable = *n_reachable;
3000 : 0 : int* atnums = NULL;
3001 : 0 : subgraf* sg = NULL;
3002 : 0 : subgraf_pathfinder* spf = NULL;
3003 : :
3004 : : /* djb-rwth: removing redundant code */
3005 : 0 : max_atoms = orig_at_data->num_inp_atoms;
3006 : 0 : iatom = start_atom - 1;
3007 : 0 : *n_reachable = 0;
3008 : :
3009 : 0 : atnums = (int*)inchi_calloc(max_atoms, sizeof(int));
3010 [ # # ]: 0 : if ( !atnums )
3011 : : {
3012 : 0 : ret = _IS_ERROR;
3013 : 0 : goto exit_function;
3014 : : }
3015 [ # # ]: 0 : for ( j = 0; j < max_atoms; j++ )
3016 : : {
3017 : 0 : atnums[j] = orig_at_data->at[j].orig_at_number;
3018 : : }
3019 : 0 : sg = subgraf_new(orig_at_data, max_atoms, atnums);
3020 [ # # ]: 0 : if ( !sg )
3021 : : {
3022 : 0 : ret = _IS_ERROR;
3023 : 0 : goto exit_function;
3024 : : }
3025 : 0 : spf = subgraf_pathfinder_new(sg, orig_at_data, iatom, iatom);
3026 [ # # ]: 0 : if ( !spf )
3027 : : {
3028 : 0 : ret = _IS_ERROR;
3029 : 0 : goto exit_function;
3030 : : }
3031 : :
3032 : : /* move from orig# to node# */
3033 : 0 : spf->start = iatom;
3034 [ # # ]: 0 : for ( j = 0; j < nforbidden_bonds; j++ )
3035 : : {
3036 : 0 : forbidden_bond_atoms[2 * j] = sg->orig2node[forbidden_bond_atoms[2 * j]];
3037 : 0 : forbidden_bond_atoms[2 * j + 1] = sg->orig2node[forbidden_bond_atoms[2 * j + 1]];
3038 : : }
3039 : :
3040 : : /*memset(atnums, -1, max_atoms * sizeof(int));*/
3041 [ # # ]: 0 : for ( j = 0; j < max_atoms; j++ )
3042 : : {
3043 : 0 : atnums[j] = -1;
3044 : : }
3045 : :
3046 : 0 : spf->nseen = 0;
3047 : 0 : natnums = subgraf_pathfinder_collect_all(spf, nforbidden_bonds, forbidden_bond_atoms, atnums);
3048 : :
3049 [ # # ]: 0 : if ( natnums )
3050 : : {
3051 [ # # ]: 0 : if ( natnums > max_n_reachable )
3052 : : {
3053 : 0 : ret = _IS_ERROR;
3054 : 0 : goto exit_function;
3055 : : }
3056 : :
3057 [ # # # # ]: 0 : for (j = 0; j < natnums && j < max_atoms; j++) /* djb-rwth: fixing buffer overruns */
3058 : : {
3059 : 0 : reachable[(*n_reachable)++ ] = atnums[j];
3060 : : }
3061 : : }
3062 : :
3063 : 0 : exit_function:
3064 : 0 : subgraf_free(sg);
3065 : 0 : subgraf_pathfinder_free(spf);
3066 [ # # ]: 0 : if ( atnums )
3067 : : {
3068 [ # # ]: 0 : inchi_free(atnums);
3069 : : }
3070 : :
3071 : 0 : return ret;
3072 : : }
3073 : :
3074 : :
3075 : : /****************************************************************************
3076 : : Collect all backbone bonds - of main chain(s), side chains being ignored
3077 : : (for polymer CRU, these are the bonds potentially involved in frame shift)
3078 : : ****************************************************************************/
3079 : 0 : void OAD_CollectBackboneBonds(ORIG_ATOM_DATA* at_data,
3080 : : int na,
3081 : : int* alist,
3082 : : int end_atom1,
3083 : : int end_atom2,
3084 : : int* nbkbonds,
3085 : : int** bkbonds,
3086 : : int* err,
3087 : : char* pStrErr)
3088 : : {
3089 : 0 : int start = 0, end = 0, dummy;
3090 : 0 : subgraf* sg = NULL;
3091 : 0 : subgraf_pathfinder* spf = NULL;
3092 : : /* Establish subgraph for na atoms of the alist */
3093 : 0 : *nbkbonds = 0;
3094 : 0 : sg = subgraf_new(at_data, na, alist);
3095 [ # # ]: 0 : if ( !sg )
3096 : : {
3097 [ # # ]: 0 : TREAT_ERR(*err, 9037, "Not enough memory (polymers)");
3098 : : /* unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE; */
3099 : 0 : return;
3100 : : }
3101 : 0 : start = sg->orig2node[end_atom1];
3102 : 0 : end = sg->orig2node[end_atom2];
3103 : : #if 0
3104 : : if ( start > end )
3105 : : {
3106 : : int tmp = end;
3107 : : end = start;
3108 : : start = tmp;
3109 : : }
3110 : : #endif
3111 : 0 : spf = subgraf_pathfinder_new(sg, at_data, start, end);
3112 [ # # ]: 0 : if ( !spf )
3113 : : {
3114 [ # # ]: 0 : TREAT_ERR( *err, 9039, "Not enough memory (polymers)" ); /* djb-rwth: addressing coverity ID #499539 -- TREAT_ERR properly used */
3115 : : /*unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;*/
3116 : 0 : return;
3117 : : }
3118 : 0 : spf->seen[0] = spf->start;
3119 : 0 : spf->nseen = 1;
3120 : 0 : *nbkbonds = 0;
3121 : 0 : subgraf_pathfinder_run(spf, 0, NULL,
3122 : : nbkbonds,
3123 : : bkbonds, /* we will collect backbone CRU bonds here */
3124 : : &dummy,
3125 : : NULL);
3126 : :
3127 : 0 : subgraf_free(sg);
3128 : 0 : subgraf_pathfinder_free(spf);
3129 : 0 : *err = 0;
3130 : :
3131 : 0 : return;
3132 : : }
3133 : :
3134 : :
3135 : : /****************************************************************************
3136 : : Remove intra-ring bonds from the list of frame-shiftable bonds
3137 : : ****************************************************************************/
3138 : 0 : void OAD_PolymerUnit_DelistIntraRingBackboneBonds(OAD_PolymerUnit* unit,
3139 : : ORIG_ATOM_DATA* at_data,
3140 : : int* err,
3141 : : char* pStrErr)
3142 : : {
3143 : 0 : int nrings = 0;
3144 : 0 : int* num_ring_sys = NULL;
3145 : :
3146 [ # # ]: 0 : if ( !unit )
3147 : : {
3148 : 0 : return;
3149 : : }
3150 [ # # ]: 0 : if ( unit->nbkbonds < 1 )
3151 : : {
3152 : 0 : return;
3153 : : }
3154 : :
3155 : : /* Establish ring systems assignments for all related atoms */
3156 : :
3157 : 0 : *err = 1;
3158 : 0 : num_ring_sys = (int*)inchi_calloc((long long)at_data->num_inp_atoms + 1, sizeof(int)); /* djb-rwth: cast operator added */
3159 [ # # ]: 0 : if ( !num_ring_sys )
3160 : : {
3161 : 0 : goto exit_function;
3162 : : }
3163 : :
3164 : 0 : *err = 0;
3165 : :
3166 : 0 : nrings = OAD_Polymer_FindRingSystems(at_data->polymer, at_data->at, at_data->num_inp_atoms, &at_data->num_inp_bonds,
3167 : 0 : num_ring_sys, NULL, unit->end_atom1 - 1); /* NB: start dfs within connected compt! */
3168 : :
3169 [ # # ]: 0 : if ( nrings == 0 )
3170 : : {
3171 : 0 : goto exit_function;
3172 : : }
3173 : : else
3174 : : {
3175 : 0 : int at1, at2, j = 0;
3176 : 0 : repeatj:
3177 : 0 : at1 = unit->bkbonds[j][0];
3178 : 0 : at2 = unit->bkbonds[j][1];
3179 [ # # # # ]: 0 : if ( (num_ring_sys[at1] == num_ring_sys[at2]) && (num_ring_sys[at1] != -1) )
3180 : : {
3181 : 0 : OAD_PolymerUnit_RemoveLinkFromCRUChain(at1, at2, &unit->nbkbonds, unit->bkbonds);
3182 : : }
3183 : : else
3184 : : {
3185 : 0 : ++j;
3186 : : }
3187 [ # # ]: 0 : if ( j < unit->nbkbonds )
3188 : : {
3189 : 0 : goto repeatj;
3190 : : }
3191 : : }
3192 : :
3193 : 0 : exit_function:
3194 [ # # ]: 0 : if ( num_ring_sys )
3195 : : {
3196 [ # # ]: 0 : inchi_free(num_ring_sys);
3197 : : }
3198 : :
3199 : 0 : return;
3200 : : }
3201 : :
3202 : :
3203 : : /****************************************************************************
3204 : : Find ring systems (exclude possible cyclizing bonds) in all polymer SRU's
3205 : : ****************************************************************************/
3206 : 2 : int OAD_Polymer_FindRingSystems(OAD_Polymer* pd,
3207 : : inp_ATOM* at,
3208 : : int nat,
3209 : : int* num_inp_bonds,
3210 : : int* num_ring_sys,
3211 : : int* size_ring_sys,
3212 : : int start)
3213 : : {
3214 : 2 : int i, j, nrings = 0, bond_type, bond_stereo;
3215 : :
3216 [ - + ]: 2 : if ( NULL == num_ring_sys )
3217 : : {
3218 : 0 : return 0;
3219 : : }
3220 : :
3221 : : /* Remove polymer SRU 'cyclizing' bonds if any */
3222 [ + + ]: 4 : for ( j = 0; j < pd->n; j++ )
3223 : : {
3224 [ - + ]: 2 : if ( pd->units[j]->cyclized )
3225 : : {
3226 : 0 : OrigAtData_RemoveBond(pd->units[j]->end_atom1 - 1,
3227 : 0 : pd->units[j]->end_atom2 - 1,
3228 : : at, &bond_type, &bond_stereo,
3229 : : num_inp_bonds);
3230 : : }
3231 : : }
3232 : :
3233 : 2 : MarkRingSystemsInp(at, nat, start); /*0 );*/
3234 : :
3235 [ + + ]: 14 : for ( i = 0; i <= nat; i++ )
3236 : : {
3237 : 12 : num_ring_sys[i] = -1;
3238 : : }
3239 [ + + ]: 12 : for ( i = 0; i < nat; i++ )
3240 : : {
3241 [ - + ]: 10 : if ( at[i].nNumAtInRingSystem > 2 )
3242 : : {
3243 : 0 : int atnum = at[i].orig_at_number;
3244 : 0 : num_ring_sys[atnum] = at[i].nRingSystem;
3245 [ # # ]: 0 : if ( NULL != size_ring_sys )
3246 : : {
3247 : 0 : size_ring_sys[atnum] = at[i].nNumAtInRingSystem;
3248 : : }
3249 : : }
3250 : : }
3251 : :
3252 : 2 : UnMarkRingSystemsInp(at, nat);
3253 : :
3254 [ + + ]: 12 : for ( i = 0; i < nat; i++ )
3255 : : {
3256 [ - + ]: 10 : if ( num_ring_sys[i] > -1 )
3257 : : {
3258 : 0 : nrings++;
3259 : : }
3260 : : }
3261 : :
3262 : : /* Restore polymer SRU 'cyclizing' bonds if applicable */
3263 [ + + ]: 4 : for ( j = 0; j < pd->n; j++ )
3264 : : {
3265 [ - + ]: 2 : if ( pd->units[j]->cyclized )
3266 : : {
3267 : 0 : OrigAtData_AddSingleStereolessBond(pd->units[j]->end_atom1 - 1,
3268 : 0 : pd->units[j]->end_atom2 - 1,
3269 : : at,
3270 : : num_inp_bonds);
3271 : : }
3272 : : }
3273 : :
3274 : 2 : return nrings;
3275 : : }
3276 : :
3277 : :
3278 : : /****************************************************************************
3279 : : Fill atomic properties (OrigAtData ) necessary
3280 : : to calc seniority in polymer SRUs
3281 : : ****************************************************************************/
3282 : 2 : void OAD_Polymer_SetAtProps(OAD_Polymer* pd,
3283 : : inp_ATOM* at,
3284 : : int nat,
3285 : : int* num_inp_bonds,
3286 : : OAD_AtProps* aprops,
3287 : : int* cano_nums)
3288 : : {
3289 : : /* Max rank for in-ring atom is 216 which is achieved for N (element number 7 in Periodic system & erank_rule2[] ),*/
3290 : : /* then goes O with rank 215 (element number 8), and so on... lowest rank is 1 for H . */
3291 : : /* */
3292 : : /* This follows to IUPAC rule 2 [Pure Appl. Chem., Vol. 74, No. 10, 2002, p. 1926] which states: */
3293 : : /* a. a ring or ring system containing nitrogen; */
3294 : : /* b. a ring or ring system containing the heteroatom occurring earliest in the order given in Rule 4; */
3295 : : /* ( which is O > S > Se > Te > N > P > As > Sb > Bi > Si > Ge > Sn > Pb > B > Hg ) */
3296 : : /* ... */
3297 : :
3298 : 2 : int erank_rule2[] = { 0,1,198,197,196,202,2,216,215,191,190,189,188,187,206,210,214,183,182,181,180,179,178,177,176,
3299 : : 175,174,173,172,171,170,169,205,209,213,165,164,163,162,161,160,159,158,157,156,155,154,153,152,
3300 : : 151,204,208,212,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,
3301 : : 127,126,125,124,123,122,121,201,119,203,207,116,115,114,113,112,111,110,109,108,107,106,105,104,
3302 : : 103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81 };
3303 : :
3304 : :
3305 : :
3306 : : /* Max rank for chain atom is 215 which is achieved for O (element number 8 in Periodic system & erank_rule4[] ), */
3307 : : /* then goes N with rank 212 (element number 8), and so on... lowest rank is 1 for H . */
3308 : : /* */
3309 : : /* This follows to IUPAC rule 4 [Pure Appl. Chem., Vol. 74, No. 10, 2002, p. 1927] which states: */
3310 : : /* O > S > Se > Te > N > P > As > Sb > Bi > Si > Ge > Sn > Pb > B > Hg */
3311 : : /* Note: Other heteroatoms may be placed within this order as indicated by their positions in the */
3312 : : /* periodic table [5]. */
3313 : :
3314 : 2 : int erank_rule4[] = { 0,1,198,197,196,202,2,211,215,191,190,189,188,187,206,210,214,183,182,181,180,179,178,177,176,
3315 : : 175,174,173,172,171,170,169,205,209,213,165,164,163,162,161,160,159,158,157,156,155,154,153,152,
3316 : : 151,204,208,212,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,
3317 : : 127,126,125,124,123,122,121,201,119,203,207,116,115,114,113,112,111,110,109,108,107,106,105,104,
3318 : : 103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81 };
3319 : :
3320 : :
3321 : 2 : int i, j, k, nrings = 0;
3322 : 2 : int a1, a2, dummy = 0, bond_type = -1, bond_stereo = -1;
3323 : 2 : int* num_ring_sys = NULL, * size_ring_sys = NULL;
3324 : : /* djb-rwth: fixing oss-fuzz issue #68112 */
3325 : 2 : int err2_len = sizeof(erank_rule2) / sizeof(erank_rule2[0]);
3326 : 2 : int err4_len = sizeof(erank_rule4) / sizeof(erank_rule4[0]);
3327 : :
3328 [ + - + - : 2 : if ( (NULL == aprops) || !at || !pd ) /* djb-rwth: fixing oss-fuzz issue #68329, #68286 */
- + ]
3329 : : {
3330 : 0 : return;
3331 : : }
3332 : :
3333 : : /* Establish element ranks for atoms */
3334 [ + + ]: 12 : for ( k = 0; k < nat; k++ )
3335 : : {
3336 : 10 : int atnum = at[k].orig_at_number, index = k;
3337 : 10 : U_CHAR err4_ind = at[k].el_number;
3338 [ + - ]: 10 : if ( cano_nums )
3339 : : {
3340 : 10 : index = cano_nums[atnum];
3341 : : }
3342 [ + - + - ]: 10 : if ( index >= 0 && err4_ind < err4_len )
3343 : : {
3344 : 10 : aprops[index].erank = erank_rule4[err4_ind];
3345 : 10 : aprops[index].ring_erank = 0;
3346 : 10 : aprops[index].ring_size = 0;
3347 : 10 : aprops[index].ring_num = -1;
3348 : : }
3349 : : else
3350 : : {
3351 : : /* deleted H's go here */
3352 : : ;
3353 : : }
3354 : :
3355 : : }
3356 : :
3357 : : /* Establish ring systems assignments for atoms */
3358 : 2 : num_ring_sys = (int*)inchi_calloc((long long)nat + 1, sizeof(int)); /* djb-rwth: cast operator added */
3359 [ - + ]: 2 : if ( NULL == num_ring_sys )
3360 : : {
3361 : 0 : goto exit_function;
3362 : : }
3363 : 2 : size_ring_sys = (int*)inchi_calloc((long long)nat + 1, sizeof(int)); /* djb-rwth: cast operator added */
3364 [ - + ]: 2 : if ( NULL == size_ring_sys )
3365 : : {
3366 : 0 : goto exit_function;
3367 : : }
3368 : :
3369 : : /* Note that we get here on the way of InChI2Struct conversion. */
3370 : : /* Break temporarily any of (actually, the first) SRU 'cyclizing' bonds */
3371 [ + + ]: 4 : for ( j = 0; j < pd->n; j++ )
3372 : : {
3373 [ + - - + ]: 2 : if ( pd->units[j]->na > 2 && pd->units[j]->nbkbonds > 0 &&
3374 [ # # ]: 0 : pd->units[j]->cyclized == 0 &&
3375 [ # # ]: 0 : pd->units[j]->cyclizable == CLOSING_SRU_RING )
3376 : : {
3377 : 0 : a1 = pd->units[j]->bkbonds[0][0] - 1;
3378 : 0 : a2 = pd->units[j]->bkbonds[0][1] - 1;
3379 : 0 : OrigAtData_RemoveBond(a1, a2, at, &bond_type, &bond_stereo, &dummy);
3380 : : }
3381 : : }
3382 : :
3383 : 2 : nrings = OAD_Polymer_FindRingSystems(pd, at, nat, num_inp_bonds, num_ring_sys, size_ring_sys, 0);
3384 : :
3385 : : /* Immediately restore just broken bond(s) */
3386 [ + + ]: 4 : for ( j = 0; j < pd->n; j++ )
3387 : : {
3388 [ + - ]: 2 : if ( pd->units[j]->na > 2 &&
3389 [ - + ]: 2 : pd->units[j]->nbkbonds > 0 &&
3390 [ # # ]: 0 : pd->units[j]->cyclized == 0 &&
3391 [ # # ]: 0 : pd->units[j]->cyclizable == CLOSING_SRU_RING )
3392 : : {
3393 : 0 : a1 = pd->units[j]->bkbonds[0][0] - 1;
3394 : 0 : a2 = pd->units[j]->bkbonds[0][1] - 1;
3395 : : /* OrigAtData_AddSingleStereolessBond( a1, a2, at, &dummy ); */
3396 : 0 : OrigAtData_AddBond(a1, a2, at, bond_type, bond_stereo, &dummy);
3397 : : }
3398 : : }
3399 : :
3400 [ + - ]: 2 : if ( nrings )
3401 : : {
3402 : 0 : int max_ring_num = 0;
3403 : : /* SRU contains ring[s], proceed with them following (not totally) the IUPAC guidelines */
3404 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
3405 : : {
3406 : : /* Browse 0-based original atoms, go to 1-based cano nums domain if cano_nums mapping is suppied */
3407 : 0 : int atnum = at[k].orig_at_number, index = k;
3408 [ # # ]: 0 : if ( cano_nums )
3409 : : {
3410 : 0 : index = cano_nums[atnum] + 1;
3411 : : }
3412 [ # # ]: 0 : if ( num_ring_sys[atnum] >= 0 )
3413 : : {
3414 : 0 : aprops[index].ring_num = num_ring_sys[atnum]; /* temporarily */
3415 : :
3416 [ # # ]: 0 : if ( max_ring_num < aprops[index].ring_num )
3417 : 0 : max_ring_num = aprops[index].ring_num; /* NB: OAD_Polymer_FindRingSystems may return num_ring_sys[] */
3418 : : /* which is not a list of consecutive numbers */
3419 : :
3420 : 0 : aprops[index].ring_size = size_ring_sys[atnum]; /* Size of ring system which includes the atom k . */
3421 : : /* It is used as an additional score for in-ring */
3422 : : /* atoms' prioritizing (instead of criteria in */
3423 : : /* 2c-2h of IUPAC rule 2 which deal with ring sizes). */
3424 : : }
3425 : : }
3426 : :
3427 [ # # ]: 0 : for ( i = 0; i <= max_ring_num; i++ )
3428 : : {
3429 : 0 : int erank, max_erank = 0;
3430 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
3431 : : {
3432 : 0 : int atnum = at[k].orig_at_number, index = k;
3433 [ # # ]: 0 : if ( cano_nums )
3434 : : {
3435 : 0 : index = cano_nums[atnum] + 1;
3436 : : }
3437 [ # # ]: 0 : if ( aprops[index].ring_num == i )
3438 : : {
3439 : 0 : erank = erank_rule2[at[k].el_number];
3440 [ # # ]: 0 : if ( erank > max_erank )
3441 : 0 : max_erank = erank;
3442 : : }
3443 : : }
3444 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
3445 : : {
3446 : 0 : int atnum = at[k].orig_at_number, index = k;
3447 [ # # ]: 0 : if ( cano_nums )
3448 : : {
3449 : 0 : index = cano_nums[atnum] + 1;
3450 : : }
3451 [ # # ]: 0 : if ( aprops[index].ring_num == i )
3452 : : {
3453 [ # # ]: 0 : if ( aprops[index].ring_size > 2 )
3454 : : {
3455 : 0 : aprops[index].ring_erank = max_erank;
3456 : : }
3457 : : }
3458 : : }
3459 : : }
3460 : : }
3461 : :
3462 : 2 : exit_function:
3463 [ + - ]: 2 : if ( num_ring_sys )
3464 : : {
3465 [ + - ]: 2 : inchi_free(num_ring_sys);
3466 : : }
3467 [ + - ]: 2 : if ( size_ring_sys )
3468 : : {
3469 [ + - ]: 2 : inchi_free(size_ring_sys);
3470 : : }
3471 : :
3472 : 2 : return;
3473 : : }
3474 : :
3475 : :
3476 : : /****************************************************************************
3477 : : Exclude higher order bonds from list of bkbonds
3478 : : ****************************************************************************/
3479 : 0 : void OAD_PolymerUnit_DelistHighOrderBackboneBonds(OAD_PolymerUnit* unit,
3480 : : ORIG_ATOM_DATA* orig_at_data,
3481 : : COMP_ATOM_DATA* composite_norm_data,
3482 : : int* err,
3483 : : char* pStrErr)
3484 : : {
3485 : 0 : int at1, at2, j = 0, k, check_taut = 0, remove; /* djb-rwth: removing redundant variables/code */
3486 : :
3487 : 0 : int* orig_num = NULL, * curr_num = NULL;
3488 : 0 : int bond_is_untouchable = 0, btype; /* DT: moved from below 2024-09-01 DT */
3489 : :
3490 [ # # ]: 0 : if ( unit->na < 2 )
3491 : : {
3492 : 0 : return;
3493 : : }
3494 [ # # ]: 0 : if ( unit->nb < 2 )
3495 : : {
3496 : 0 : return;
3497 : : }
3498 [ # # ]: 0 : if ( unit->nbkbonds < 1 )
3499 : : {
3500 : 0 : return;
3501 : : }
3502 : :
3503 : : /* Care on tautomeric bonds */
3504 [ # # ]: 0 : if ( composite_norm_data )
3505 : : {
3506 : 0 : check_taut = 1;
3507 : 0 : orig_num = (int*)inchi_calloc((long long)orig_at_data->num_inp_atoms + 2, sizeof(int)); /* djb-rwth: cast operator added */
3508 : 0 : curr_num = (int*)inchi_calloc((long long)orig_at_data->num_inp_atoms + 2, sizeof(int)); /* djb-rwth: cast operator added */
3509 [ # # # # ]: 0 : if ( orig_num && curr_num )
3510 : : {
3511 : 0 : check_taut = 1;
3512 : 0 : CompAtomData_GetNumMapping(composite_norm_data, orig_num, curr_num);
3513 : : }
3514 : : }
3515 : 0 : repeatj:
3516 : 0 : remove = 0;
3517 : 0 : at1 = unit->bkbonds[j][0];
3518 : 0 : at2 = unit->bkbonds[j][1];
3519 : : /* djb-rwth: removing redundant code */
3520 [ # # ]: 0 : for ( k = 0; k < orig_at_data->at[at1 - 1].valence; k++ )
3521 : : {
3522 [ # # ]: 0 : if ( orig_at_data->at[at1 - 1].neighbor[k] != at2 - 1 )
3523 : 0 : continue;
3524 : : /* djb-rwth: removing redundant code */
3525 : : }
3526 : : /*if ( border > 1 ) */
3527 : : /* djb-rwth: removing redundant code */
3528 : 0 : bond_is_untouchable = 0;
3529 [ # # # # : 0 : if ( check_taut && composite_norm_data && composite_norm_data->at && curr_num ) /* djb-rwth: fixing a NULL pointer dereference */
# # # # ]
3530 : : {
3531 [ # # ]: 0 : for ( k = 0; k < composite_norm_data->at[curr_num[at1]].valence; k++ )
3532 : : {
3533 [ # # ]: 0 : if ( composite_norm_data->at[curr_num[at1]].neighbor[k] != curr_num[at2] )
3534 : : {
3535 : 0 : continue;
3536 : : }
3537 : 0 : btype = composite_norm_data->at[curr_num[at1]].bond_type[k];
3538 : 0 : bond_is_untouchable = (btype == BOND_TAUTOM); /*|| btype == BOND_ALTERN );*/
3539 : 0 : break;
3540 : : }
3541 : : }
3542 [ # # ]: 0 : if ( bond_is_untouchable )
3543 : : {
3544 : 0 : remove = 1;
3545 : : }
3546 : :
3547 [ # # ]: 0 : if ( remove )
3548 : : {
3549 : 0 : OAD_PolymerUnit_RemoveLinkFromCRUChain(at1, at2, &unit->nbkbonds, unit->bkbonds);
3550 : : }
3551 : : else
3552 : : {
3553 : 0 : ++j;
3554 : : }
3555 : :
3556 [ # # ]: 0 : if ( j < unit->nbkbonds )
3557 : : {
3558 : 0 : goto repeatj;
3559 : : }
3560 : :
3561 [ # # ]: 0 : if ( orig_num )
3562 : : {
3563 [ # # ]: 0 : inchi_free(orig_num);
3564 : : }
3565 [ # # ]: 0 : if ( curr_num )
3566 : : {
3567 [ # # ]: 0 : inchi_free(curr_num);
3568 : : }
3569 : :
3570 : 0 : return;
3571 : : }
3572 : :
3573 : :
3574 : : /****************************************************************************
3575 : : Remove bond (at1, at2)
3576 : : ****************************************************************************/
3577 : 0 : void OAD_PolymerUnit_RemoveLinkFromCRUChain(int at1,
3578 : : int at2,
3579 : : int* nbonds,
3580 : : int** bonds)
3581 : : {
3582 : : int p, q;
3583 : : #if 0
3584 : : if ( at1 > at2 )
3585 : : {
3586 : : int tmp = at1;
3587 : : at1 = at2;
3588 : : at2 = tmp;
3589 : : }
3590 : : #endif
3591 [ # # ]: 0 : for ( p = 0; p < *nbonds; p++ )
3592 : : {
3593 [ # # # # ]: 0 : if ( bonds[p][0] == at1 && bonds[p][1] == at2 )
3594 : : {
3595 [ # # ]: 0 : for ( q = p + 1; q < *nbonds; q++ )
3596 : : {
3597 : 0 : bonds[q - 1][0] = bonds[q][0];
3598 : 0 : bonds[q - 1][1] = bonds[q][1];
3599 : : }
3600 : 0 : (*nbonds)--;
3601 : 0 : break;
3602 : : }
3603 : : }
3604 : :
3605 : 0 : return;
3606 : : }
3607 : :
3608 : :
3609 : : /****************************************************************************
3610 : : Debug print polymer data for a given SRU
3611 : : ****************************************************************************/
3612 : 6 : void OAD_PolymerUnit_DebugTrace(OAD_PolymerUnit* u)
3613 : : {
3614 : 6 : char* conn = "ABSENT", * typ = "ABSENT", * styp = "ABSENT";
3615 : :
3616 [ - + ]: 6 : if ( !u )
3617 : : {
3618 : 0 : return;
3619 : : }
3620 : :
3621 [ + - ]: 6 : if ( u->conn == 1 )
3622 : : {
3623 : 6 : conn = "HT"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3624 : : }
3625 [ # # ]: 0 : else if ( u->conn == 2 )
3626 : : {
3627 : 0 : conn = "HH"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3628 : : }
3629 [ # # ]: 0 : else if ( u->conn == 3 )
3630 : : {
3631 : 0 : conn = "EU"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3632 : : }
3633 : :
3634 [ - + ]: 6 : if ( u->type == 0 )
3635 : : {
3636 : 0 : typ = "NONE"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3637 : : }
3638 [ + - ]: 6 : else if ( u->type == 1 )
3639 : : {
3640 : 6 : typ = "SRU"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3641 : : }
3642 [ # # ]: 0 : else if ( u->type == 2 )
3643 : : {
3644 : 0 : typ = "MON"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3645 : : }
3646 [ # # ]: 0 : else if ( u->type == 3 )
3647 : : {
3648 : 0 : typ = "COP"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3649 : : }
3650 [ # # ]: 0 : else if ( u->type == 4 )
3651 : : {
3652 : 0 : typ = "MOD"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3653 : : }
3654 [ # # ]: 0 : else if ( u->type == 5 )
3655 : : {
3656 : 0 : typ = "MER"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3657 : : }
3658 : :
3659 [ - + ]: 6 : if ( u->subtype == 1 )
3660 : : {
3661 : 0 : styp = "ALT"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3662 : : }
3663 [ - + ]: 6 : else if ( u->subtype == 2 )
3664 : : {
3665 : 0 : styp = "RAN"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3666 : : }
3667 [ - + ]: 6 : else if ( u->subtype == 3 )
3668 : : {
3669 : 0 : styp = "BLK"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3670 : : }
3671 : :
3672 : : {
3673 : : int i, k;
3674 : : int na, nb;
3675 : :
3676 : : ITRACE_("\n\nPOLYMER UNIT @ %-p", u);
3677 : :
3678 : : ITRACE_("\n\tid=%-d label=%-d type=%-s subtype=%-s conn=%-s subscr='%-s'\n",
3679 : : u->id, u->label, typ, styp, conn, u->smt);
3680 : :
3681 : : ITRACE_("\tBracket1 coords: %-f, %-f, %-f, %-f\n", u->xbr1[0], u->xbr1[1], u->xbr1[2], u->xbr1[3]);
3682 : : ITRACE_("\tBracket2 coords: %-f, %-f, %-f, %-f\n", u->xbr2[0], u->xbr2[1], u->xbr2[2], u->xbr2[3]);
3683 : :
3684 : 6 : na = u->na;
3685 : : ITRACE_("\t%-d atoms { ", na);
3686 [ + + ]: 18 : for ( k = 0; k < na - 1; k++ )
3687 : : {
3688 : : ITRACE_(" %-d, ", u->alist[k]);
3689 : : }
3690 : : ITRACE_(" %-d }\n", u->alist[na - 1]);
3691 : :
3692 : 6 : nb = u->nb;
3693 : : ITRACE_("\t%-d bonds crossing unit borders { ", nb);
3694 : :
3695 [ + + ]: 18 : for ( k = 0; k < nb; k++ )
3696 : : {
3697 : : ITRACE_(" %-d-%-d ", u->blist[2 * k], u->blist[2 * k + 1]);
3698 : : }
3699 : : ITRACE_("}\n");
3700 : :
3701 : : ITRACE_("\tCRU caps and end atoms { ");
3702 : :
3703 : : ITRACE_("*%-d-[-%-d(end1) ... ", u->cap1, u->end_atom1);
3704 : : ITRACE_("%-d(end2)-]-*%-d", u->end_atom2, u->cap2);
3705 : : ITRACE_(" }\n");
3706 : :
3707 : : ITRACE_("\tBackbone bonds (may include 'artificially cyclizing' one) : %-d bonds ", u->nbkbonds);
3708 [ - + ]: 6 : if ( u->nbkbonds )
3709 : : {
3710 : : ITRACE_(" { ");
3711 [ # # ]: 0 : for ( i = 0; i < u->nbkbonds; i++ )
3712 : : {
3713 : : ITRACE_("(%-d, %-d) ", u->bkbonds[i][0], u->bkbonds[i][1]);
3714 : : }
3715 : : ITRACE_("}\n");
3716 : : }
3717 : : }
3718 : :
3719 : :
3720 : 6 : return;
3721 : : }
3722 : :
3723 : :
3724 : : /****************************************************************************
3725 : : Debug print the whole polymer data
3726 : : ****************************************************************************/
3727 : 0 : void OAD_Polymer_DebugTrace(OAD_Polymer* p)
3728 : : {
3729 : : int i;
3730 : :
3731 [ # # ]: 0 : if ( !p )
3732 : : {
3733 : 0 : return;
3734 : : }
3735 : :
3736 : : ITRACE_("\n\n* POLYMER INFO @ %-p (%-d group(s))", p, p->n);
3737 : : ITRACE_("\n\n* %-d star atoms: ", p->n_pzz);
3738 [ # # ]: 0 : for ( i = 0; i < p->n_pzz; i++ )
3739 : : {
3740 : : ITRACE_(" %-d", p->pzz[i]);
3741 : : }
3742 : :
3743 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
3744 : : {
3745 : : ITRACE_("\n* Polymer unit %-d", i);
3746 : 0 : OAD_PolymerUnit_DebugTrace(p->units[i]);
3747 : : }
3748 : : ITRACE_("\n* Really-do-PS = %-d", p->really_do_frame_shift);
3749 : : ITRACE_("\n* Frame_shift_scheme = %-d", p->frame_shift_scheme);
3750 : : ITRACE_("\n* Edit-repeats = %-d", p->edit_repeats);
3751 : : ITRACE_("\n* End POLYMER INFO\n");
3752 : 0 : return;
3753 : :
3754 : : }
3755 : :
3756 : :
3757 : : /****************************************************************************/
3758 : 2 : int OAD_Polymer_GetRepresentation(OAD_Polymer* p)
3759 : : {
3760 : 2 : int i, n_source_based_units = 0, n_structure_based_units = 0;
3761 : :
3762 [ - + ]: 2 : if ( !p )
3763 : : {
3764 : 0 : return NO_POLYMER;
3765 : : }
3766 : :
3767 [ + + ]: 4 : for ( i = 0; i < p->n; i++ )
3768 : : {
3769 [ - + - - ]: 2 : if ( p->units[i]->nb == 2 || p->units[i]->nbkbonds > 0 ||
3770 [ # # # # ]: 0 : ((p->units[i]->cap1 > 0) && (p->units[i]->cap2 > 0)) )
3771 : : {
3772 : 2 : p->units[i]->representation = POLYMER_REPRESENTATION_STRUCTURE_BASED;
3773 : 2 : n_structure_based_units++;
3774 : : }
3775 [ # # ]: 0 : else if ( p->units[i]->nb == 0 )
3776 : : {
3777 : 0 : p->units[i]->representation = POLYMER_REPRESENTATION_SOURCE_BASED;
3778 : 0 : n_source_based_units++;
3779 : : }
3780 : : }
3781 [ - + ]: 2 : if ( p->n == n_source_based_units )
3782 : : {
3783 : 0 : return POLYMER_REPRESENTATION_SOURCE_BASED;
3784 : : }
3785 [ + - ]: 2 : else if ( p->n == n_structure_based_units )
3786 : : {
3787 : 2 : return POLYMER_REPRESENTATION_STRUCTURE_BASED;
3788 : : }
3789 [ # # # # ]: 0 : else if ( n_source_based_units &&
3790 : 0 : n_structure_based_units &&
3791 [ # # ]: 0 : (n_source_based_units + n_structure_based_units) == p->n )
3792 : : {
3793 : : /* TODO: check if SRU/MON are embedded in a single COP (is this check really necessary? ??) */
3794 : 0 : return POLYMER_REPRESENTATION_MIXED;
3795 : : }
3796 : : #if 0
3797 : : else if ( p->n == (n_source_based_units + n_structure_based_units) )
3798 : : {
3799 : : /*
3800 : : Structure based presentation may include no-crossing bond units
3801 : : which only serve as embedding for ( >1 ) structure-based SRU's.
3802 : : The code below accounts for this.
3803 : : */
3804 : : if ( n_source_based_units < n_structure_based_units )
3805 : : {
3806 : : int j, atom, atom_is_shared_with_struct_based_unit = 0;
3807 : : for ( i = 0; i < p->n; i++ )
3808 : : {
3809 : : int k;
3810 : : if ( p->units[i]->representation != POLYMER_REPRESENTATION_SOURCE_BASED )
3811 : : continue;
3812 : : for ( k = 0; k < p->units[i]->na; k++ )
3813 : : {
3814 : : atom = p->units[i]->alist[k];
3815 : : if ( is_in_the_ilist(p->pzz, atom, p->n_pzz) )
3816 : : continue;
3817 : : atom_is_shared_with_struct_based_unit = 0;
3818 : : for ( j = 0; j < p->n; j++ )
3819 : : {
3820 : : if ( p->units[j]->representation != POLYMER_REPRESENTATION_STRUCTURE_BASED )
3821 : : continue;
3822 : : if ( is_in_the_ilist(p->units[j]->alist, atom, p->units[j]->na) )
3823 : : {
3824 : : atom_is_shared_with_struct_based_unit = 1;
3825 : : break;
3826 : : }
3827 : : }
3828 : : if ( !atom_is_shared_with_struct_based_unit )
3829 : : break;
3830 : : }
3831 : : if ( !atom_is_shared_with_struct_based_unit )
3832 : : break;
3833 : : }
3834 : : if ( atom_is_shared_with_struct_based_unit )
3835 : : return POLYMER_REPRESENTATION_STRUCTURE_BASED;
3836 : : }
3837 : : return POLYMER_REPRESENTATION_MIXED;
3838 : : }
3839 : : #endif
3840 : :
3841 : 0 : return POLYMER_REPRESENTATION_UNRECOGNIZED;
3842 : : }
3843 : :
3844 : :
3845 : : /****************************************************************************
3846 : : Open pre-cyclized CRUs appropriately (i.e., make frame shift)
3847 : : ****************************************************************************/
3848 : 0 : void OAD_Polymer_SmartReopenCyclizedUnits(OAD_Polymer* p,
3849 : : inp_ATOM* at,
3850 : : int nat,
3851 : : int* num_inp_bonds)
3852 : : {
3853 : : int i;
3854 : : /* djb-rwth: fixing oss-fuzz issue #68329 */
3855 : 0 : OAD_AtProps* aprops = (OAD_AtProps*)inchi_calloc((long long)nat + 1, sizeof(OAD_AtProps)); /* djb-rwth: cast operator added */
3856 : : /* nat + 1: add extra element for possibe 1-based indexing */
3857 : :
3858 [ # # ]: 0 : if ( !p )
3859 : : {
3860 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3861 : 0 : return;
3862 : : }
3863 [ # # ]: 0 : if ( p->n < 1 )
3864 : : {
3865 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3866 : 0 : return;
3867 : : }
3868 [ # # ]: 0 : if ( !p->really_do_frame_shift )
3869 : : {
3870 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3871 : 0 : return;
3872 : : }
3873 : : /* djb-rwth: fixing oss-fuzz issue #68329 */
3874 [ # # ]: 0 : if ( nat <= 0 )
3875 : : {
3876 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3877 : 0 : return;
3878 : : }
3879 : :
3880 : : /*ITRACE_( "\n\n*********************************************************************\n* ENTERING OAD_Polymer_SmartReopenCyclizedUnits()" );
3881 : : OAD_Polymer_DebugTrace( p );*/
3882 : :
3883 : : /* Set atom properties for sorting */
3884 [ # # # # ]: 0 : if ( !aprops || !at ) /* djb-rwth: fixing oss-fuzz issue #68329, #68286 */
3885 : : {
3886 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3887 : 0 : return;
3888 : : }
3889 : 0 : OAD_Polymer_SetAtProps(p, at, nat, num_inp_bonds, aprops, NULL); /* NULL as we alredy are in 1-based cano_nums while at i2s/i2i*/
3890 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
3891 : : {
3892 [ # # ]: 0 : if ( p->units[i] ) /* djb-rwth: fixing oss-fuzz issue #68329 */
3893 : : {
3894 : 0 : OAD_PolymerUnit* u = p->units[i];
3895 [ # # ]: 0 : if ( p->frame_shift_scheme == FSS_NONE )
3896 : : {
3897 : 0 : continue;
3898 : : }
3899 : 0 : if ( /* !u->cyclizable || u->cyclized || */
3900 [ # # ]: 0 : u->nbkbonds < 1 ||
3901 [ # # # # ]: 0 : u->cap1 < 1 || u->cap2 < 1 ||
3902 [ # # # # ]: 0 : u->cap1 > nat || u->cap2 > nat )
3903 : : {
3904 : 0 : continue;
3905 : : }
3906 [ # # ]: 0 : if ( OAD_PolymerUnit_SetReopeningDetails(u, at) )
3907 : : {
3908 : : int senior_bond;
3909 : 0 : OAD_PolymerUnit_SortBackboneBondsAndSetSeniors(u, at, aprops, &senior_bond);
3910 : : }
3911 : 0 : OAD_PolymerUnit_ReopenCyclized(u, at, aprops, nat, num_inp_bonds);
3912 : : }
3913 : : }
3914 : :
3915 : 0 : p->really_do_frame_shift = 0;
3916 [ # # ]: 0 : inchi_free(aprops);
3917 : :
3918 : 0 : return;
3919 : : }
3920 : :
3921 : :
3922 : : /****************************************************************************/
3923 : 0 : void OAD_PolymerUnit_ReopenCyclized(OAD_PolymerUnit* u,
3924 : : inp_ATOM* at,
3925 : : OAD_AtProps* aprops,
3926 : : int nat,
3927 : : int* num_inp_bonds)
3928 : : {
3929 : : int bond_type, bond_stereo;
3930 : :
3931 [ # # ]: 0 : if ( u->cyclizable == CLOSING_SRU_RING )
3932 : : {
3933 : : /* Decyclize artificially introduced bond */
3934 : 0 : OrigAtData_RemoveBond(u->end_atom1 - 1, u->end_atom2 - 1,
3935 : : at, &bond_type, &bond_stereo, num_inp_bonds);
3936 : : }
3937 [ # # ]: 0 : else if ( u->cyclizable == CLOSING_SRU_HIGHER_ORDER_BOND )
3938 : : {
3939 : 0 : OrigAtData_DecreaseBondOrder(u->end_atom1 - 1, u->end_atom2 - 1, at);
3940 : : }
3941 [ # # ]: 0 : else if ( u->cyclizable == CLOSING_SRU_DIRADICAL )
3942 : : {
3943 [ # # ]: 0 : if ( at[u->end_atom1 - 1].radical == RADICAL_TRIPLET )
3944 : : {
3945 : 0 : at[u->end_atom1 - 1].radical = 0;
3946 : : }
3947 : : }
3948 : :
3949 : : /* Add explicitly connections to star atoms */
3950 : 0 : OrigAtData_AddSingleStereolessBond(u->cap1 - 1, u->end_atom1 - 1,
3951 : : at, num_inp_bonds);
3952 : 0 : OrigAtData_AddSingleStereolessBond(u->cap2 - 1, u->end_atom2 - 1,
3953 : : at, num_inp_bonds);
3954 : :
3955 : : /* Create crossing bonds */
3956 : 0 : u->nb = 2;
3957 : 0 : u->nbkbonds = 0;
3958 [ # # ]: 0 : if ( !u->blist )
3959 : : {
3960 : 0 : u->blist = (int*)inchi_calloc(2 * (long long)u->nb, sizeof(int)); /* djb-rwth: cast operator added */
3961 : : }
3962 [ # # ]: 0 : if ( !u->blist )
3963 : : {
3964 : 0 : return;
3965 : : }
3966 : :
3967 : 0 : u->blist[0] = u->cap1;
3968 : 0 : u->blist[1] = u->end_atom1;
3969 : 0 : u->blist[2] = u->cap2;
3970 : 0 : u->blist[3] = u->end_atom2;
3971 : :
3972 : 0 : return;
3973 : : }
3974 : :
3975 : :
3976 : : /****************************************************************************/
3977 : 0 : int OAD_PolymerUnit_SetReopeningDetails(OAD_PolymerUnit* u, inp_ATOM* at)
3978 : : {
3979 : : int k;
3980 : :
3981 : : /* Check reopening type */
3982 : :
3983 : : /* Caps are separated by one atom - that's not error but do nothing */
3984 [ # # ]: 0 : if ( u->nbkbonds == 0 )
3985 : : {
3986 : : ;
3987 : : }
3988 [ # # ]: 0 : else if ( u->nbkbonds == 1 )
3989 : : {
3990 : 0 : u->end_atom1 = u->bkbonds[0][0];
3991 : 0 : u->end_atom2 = u->bkbonds[0][1];
3992 : :
3993 [ # # ]: 0 : if ( u->end_atom1 == u->end_atom2 )
3994 : : {
3995 : : #ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL
3996 : 0 : u->cyclizable = CLOSING_SRU_DIRADICAL;
3997 : : #else
3998 : : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
3999 : : #endif
4000 : : }
4001 : : else
4002 : : {
4003 : : /* If caps are separated by two atoms - that's not error but do nothing */
4004 [ # # ]: 0 : for ( k = 0; k < at[u->end_atom1 - 1].valence; k++ )
4005 : : {
4006 [ # # ]: 0 : if ( at[u->end_atom1 - 1].neighbor[k] == u->end_atom2 - 1 )
4007 : : {
4008 [ # # ]: 0 : if ( at[u->end_atom1 - 1].bond_type[k] > 1 )
4009 : : #ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND
4010 : 0 : u->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;
4011 : : #else
4012 : : /* u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;*/
4013 : : #endif
4014 : 0 : break;
4015 : : }
4016 : : }
4017 : : }
4018 : : } /* */
4019 : :
4020 : 0 : return u->nbkbonds;
4021 : : }
4022 : :
4023 : :
4024 : :
4025 : : /****************************************************************************/
4026 : 0 : void OAD_PolymerUnit_SortBackboneBondsAndSetSeniors(OAD_PolymerUnit* u,
4027 : : inp_ATOM* at,
4028 : : OAD_AtProps* aprops,
4029 : : int* senior_bond)
4030 : : {
4031 : 0 : int j, * bnum = NULL;
4032 : :
4033 : 0 : *senior_bond = 0;
4034 : :
4035 : : /* Sort backbone (== frame shiftable) bonds if necessary */
4036 [ # # ]: 0 : if ( u->nbkbonds > 1 )
4037 : : {
4038 : 0 : bnum = (int*)inchi_calloc(u->nbkbonds, sizeof(int));
4039 [ # # ]: 0 : if ( bnum )
4040 : : {
4041 [ # # ]: 0 : for ( j = 0; j < u->nbkbonds; j++ )
4042 : : {
4043 : 0 : bnum[j] = j;
4044 : : }
4045 : 0 : OAD_PolymerUnit_SortBackboneBonds(u, aprops, bnum);
4046 : 0 : *senior_bond = bnum[0];
4047 [ # # ]: 0 : inchi_free(bnum);
4048 : : }
4049 : : }
4050 : :
4051 : : /* v. 1.05+ : place senior atom the first ("left") in the senior bond */
4052 [ # # ]: 0 : if ( OAD_Polymer_IsFirstAtomRankLower(u->bkbonds[*senior_bond][0], u->bkbonds[*senior_bond][1], aprops) == 1 )
4053 : : {
4054 : 0 : int tmp = u->bkbonds[*senior_bond][0];
4055 : 0 : u->bkbonds[*senior_bond][0] = u->bkbonds[*senior_bond][1];
4056 : 0 : u->bkbonds[*senior_bond][1] = tmp;
4057 : : }
4058 : :
4059 : 0 : u->end_atom1 = u->bkbonds[*senior_bond][0];
4060 : 0 : u->end_atom2 = u->bkbonds[*senior_bond][1];
4061 : :
4062 : 0 : return;
4063 : : }
4064 : :
4065 : :
4066 : : /****************************************************************************/
4067 : 0 : void OAD_PolymerUnit_SortBackboneBonds(OAD_PolymerUnit* u,
4068 : : OAD_AtProps* aprops,
4069 : : int* bnum)
4070 : : {
4071 : : int i, j, tmp;
4072 : 0 : int n = u->nbkbonds;
4073 [ # # ]: 0 : if ( NULL == bnum )
4074 : : {
4075 : 0 : return;
4076 : : }
4077 [ # # ]: 0 : for ( i = 1; i < n; i++ )
4078 : : {
4079 : 0 : tmp = bnum[i];
4080 : 0 : j = i - 1;
4081 [ # # # # ]: 0 : while ( j >= 0 && OAD_Polymer_CompareBackboneBondsSeniority(u->bkbonds[bnum[j]], u->bkbonds[tmp], aprops) > 0 )
4082 : : {
4083 : 0 : bnum[j + 1] = bnum[j];
4084 : 0 : j--;
4085 : : }
4086 : 0 : bnum[j + 1] = tmp;
4087 : : }
4088 : :
4089 : 0 : return;
4090 : : }
4091 : :
4092 : :
4093 : : /****************************************************************************
4094 : : For sorting SRU cyclizing bonds (PS=='frame-shift') in descending order
4095 : : In general: favor greater max-rank end
4096 : : if max ends are the same, favor lesser min-rank end
4097 : : ****************************************************************************/
4098 : 0 : int OAD_Polymer_CompareBackboneBondsSeniority(int* b1, int* b2, OAD_AtProps* aprops)
4099 : : {
4100 : 0 : int b1min, b1max, b2min, b2max, tmp, cmp = 0;
4101 : :
4102 : : /* Find min and max ext-ranked ends of the both bonds */
4103 : 0 : b1max = b1[0]; b1min = b1[1];
4104 : 0 : b2max = b2[0]; b2min = b2[1];
4105 [ # # ]: 0 : if ( OAD_Polymer_IsFirstAtomRankLower(b1min, b1max, aprops) == -1 )
4106 : : {
4107 : 0 : tmp = b1max;
4108 : 0 : b1max = b1min;
4109 : 0 : b1min = tmp;
4110 : : }
4111 [ # # ]: 0 : if ( OAD_Polymer_IsFirstAtomRankLower(b2min, b2max, aprops) == -1 )
4112 : : {
4113 : 0 : tmp = b2max;
4114 : 0 : b2max = b2min;
4115 : 0 : b2min = tmp;
4116 : : }
4117 : :
4118 : : /* Compare bonds' seniority */
4119 : :
4120 : : /* First, favor the bond which has greater ext-rank end
4121 : : NB: the result may be 0, that is, equal max ext. ranks */
4122 : :
4123 : 0 : cmp = OAD_Polymer_CompareRanksOfTwoAtoms(b1max, b2max, aprops);
4124 [ # # ]: 0 : if ( cmp == 1 )
4125 : : {
4126 : 0 : return 1; /* rank(b1max) < rank(b2max), so bond2 is senior */
4127 : : }
4128 [ # # ]: 0 : else if ( cmp == -1 )
4129 : : {
4130 : 0 : return -1; /* rank(b1max) > rank(b2max), so bond1 is senior */
4131 : : }
4132 : :
4133 : : /* Max ends are of the same rank, so favor the bond with lesser min-rank end
4134 : : NB: the result may NOT be 0, that is, the case is always resolved */
4135 : :
4136 : 0 : cmp = OAD_Polymer_CompareRanksOfTwoAtoms(b1min, b2min, aprops); /*OAD_Polymer_IsFirstAtomRankLower( b1min, b2min, aprops );*/
4137 : :
4138 [ # # ]: 0 : if ( cmp == 1 )
4139 : : {
4140 : 0 : return -1; /* rank(b1min) < rank(b2min), so bond1 is senior */
4141 : : }
4142 [ # # ]: 0 : else if ( cmp == -1 )
4143 : : {
4144 : 0 : return 1; /* rank(b1min) > rank(b2min), so bond2 is senior */
4145 : : }
4146 : :
4147 : : /* Min ends are of the same rank. Here is the time to compare directly
4148 : : which canonical number is larger of max-ends ... */
4149 : :
4150 [ # # ]: 0 : if ( b1max < b2max )
4151 : : {
4152 : 0 : return 1;
4153 : : }
4154 [ # # ]: 0 : if ( b1max > b2max )
4155 : : {
4156 : 0 : return -1;
4157 : : }
4158 : :
4159 : : /* ... they are the same, so compare which canonical number is larger for min-ends ... */
4160 : :
4161 [ # # ]: 0 : if ( b1min < b2min )
4162 : : {
4163 : 0 : return -1; /* b1min < b2min, so bond1 is senior */
4164 : : }
4165 [ # # ]: 0 : if ( b1min > b2min )
4166 : : {
4167 : 0 : return 1; /* b1min > b2min, so bond2 is senior */
4168 : : }
4169 : :
4170 : 0 : return 0; /* we should not reach there */
4171 : : }
4172 : :
4173 : :
4174 : : /****************************************************************************
4175 : : Compare seniority of two atoms in polymer SRU
4176 : : loosely following IUPAC guidelines.
4177 : : NB: no last resort check here, so 0 (=='same seniority') may be returned
4178 : : ****************************************************************************/
4179 : 2 : int OAD_Polymer_CompareRanksOfTwoAtoms(int atom1, int atom2, OAD_AtProps* aprops)
4180 : : {
4181 : 2 : const int HETEROCYC = 3, HETEROAT = 2, CARBOCYC = 1, CARBOAT = 0;
4182 : : /* NB: Carbon's rank is always 2, next to the lowest */
4183 : :
4184 : 2 : int a1 = atom1 - 1;
4185 : 2 : int a2 = atom2 - 1;
4186 : 2 : int a1typ = CARBOAT;
4187 : 2 : int a2typ = CARBOAT;
4188 : :
4189 : : /* djb-rwth: fixing oss-fuzz issue #69501, #68277 */
4190 [ + - - + ]: 2 : if ( (a1 < 0) || (a2 < 0) )
4191 : : {
4192 : 0 : return 0;
4193 : : }
4194 : :
4195 [ - + ]: 2 : if ( aprops[a1].ring_size > 2 )
4196 : : {
4197 [ # # ]: 0 : if ( aprops[a1].ring_erank <= 2 )
4198 : : {
4199 : 0 : a1typ = CARBOCYC;
4200 : : }
4201 : : else
4202 : : {
4203 : 0 : a1typ = HETEROCYC;
4204 : : }
4205 : : }
4206 : : else
4207 : : {
4208 [ + - ]: 2 : if ( aprops[a1].erank == 2 )
4209 : : {
4210 : 2 : a1typ = CARBOAT;
4211 : : }
4212 : : else
4213 : : {
4214 : 0 : a1typ = HETEROAT;
4215 : : }
4216 : : }
4217 : :
4218 [ - + ]: 2 : if ( aprops[a2].ring_size > 2 )
4219 : : {
4220 [ # # ]: 0 : if ( aprops[a2].ring_erank <= 2 )
4221 : : {
4222 : 0 : a2typ = CARBOCYC;
4223 : : }
4224 : : else
4225 : : {
4226 : 0 : a2typ = HETEROCYC;
4227 : : }
4228 : : }
4229 : : else
4230 : : {
4231 [ - + ]: 2 : if ( aprops[a2].erank == 2 )
4232 : : {
4233 : 0 : a2typ = CARBOAT;
4234 : : }
4235 : : else
4236 : : {
4237 : 2 : a2typ = HETEROAT;
4238 : : }
4239 : : }
4240 : :
4241 : : /* Compare */
4242 : :
4243 : : /*
4244 : : Follow IUPAC Rule 1
4245 : : 'The basic order of seniority of subunits is:
4246 : : heterocyclic rings and ring systems > heteroatom chains >
4247 : : > carbocyclic rings and ring systems > acyclic carbon chains'
4248 : : */
4249 : :
4250 [ - + - - ]: 2 : if ( a1typ == HETEROCYC && a2typ == HETEROCYC ) /* a1 and a2 are HETEROCYC */
4251 : : {
4252 : : /* Try resolving by senior-heteroatom ring */
4253 [ # # ]: 0 : if ( aprops[a1].ring_erank < aprops[a2].ring_erank )
4254 : : {
4255 : 0 : return 1;
4256 : : }
4257 [ # # ]: 0 : if ( aprops[a1].ring_erank > aprops[a2].ring_erank )
4258 : : {
4259 : 0 : return -1;
4260 : : }
4261 : : /* Same senior-heteroatom rings, try resolving by total ring size */
4262 [ # # ]: 0 : if ( aprops[a1].ring_size < aprops[a2].ring_size )
4263 : : {
4264 : 0 : return 1;
4265 : : }
4266 [ # # ]: 0 : if ( aprops[a1].ring_size > aprops[a2].ring_size )
4267 : : {
4268 : 0 : return -1;
4269 : : }
4270 : : /* Could not resolve... */
4271 : 0 : return 0;
4272 : : }
4273 [ - + ]: 2 : else if ( a1typ == HETEROCYC )
4274 : : {
4275 : 0 : return -1; /* a1 is HETEROCYC, a2 is any other (==junior) */
4276 : : }
4277 [ - + ]: 2 : else if ( a2typ == HETEROCYC )
4278 : : {
4279 : 0 : return 1; /* a2 is HETEROCYC, a1 is any other (==junior) */
4280 : : }
4281 : :
4282 : : /* HETEROCYC left out */
4283 : :
4284 [ - + - - ]: 2 : if ( a1typ == HETEROAT && a2typ == HETEROAT ) /* a1 and a2 are HETEROAT */
4285 : : {
4286 [ # # ]: 0 : if ( aprops[a1].erank < aprops[a2].erank )
4287 : : {
4288 : 0 : return 1;
4289 : : }
4290 [ # # ]: 0 : if ( aprops[a1].erank > aprops[a2].erank )
4291 : : {
4292 : 0 : return -1;
4293 : : }
4294 : : /* Could not resolve... */
4295 : 0 : return 0;
4296 : : }
4297 [ - + ]: 2 : else if ( a1typ == HETEROAT )
4298 : : {
4299 : 0 : return -1; /* a1 is HETEROAT, a2 is any other (==junior) */
4300 : : }
4301 [ + - ]: 2 : else if ( a2typ == HETEROAT )
4302 : : {
4303 : 2 : return 1; /* a2 is HETEROAT, a1 is any other (==junior) */
4304 : : }
4305 : :
4306 : : /* HETEROAT left out */
4307 [ # # # # ]: 0 : if ( a1typ == CARBOCYC && a2typ == CARBOCYC ) /* a1 and a2 are CARBOCYC */
4308 : : {
4309 : : /* Same senior-atom (C) ring, try resolving by total ring size */
4310 [ # # ]: 0 : if ( aprops[a1].ring_size < aprops[a2].ring_size )
4311 : : {
4312 : 0 : return 1;
4313 : : }
4314 [ # # ]: 0 : if ( aprops[a1].ring_size > aprops[a2].ring_size )
4315 : : {
4316 : 0 : return -1;
4317 : : }
4318 : : /* Could not resolve... */
4319 : 0 : return 0;
4320 : : }
4321 [ # # ]: 0 : else if ( a1typ == CARBOCYC )
4322 : : {
4323 : 0 : return -1;
4324 : : }
4325 [ # # ]: 0 : else if ( a2typ == CARBOCYC )
4326 : : {
4327 : 0 : return 1;
4328 : : }
4329 : :
4330 : 0 : return 0; /* 0 means unresolved. It is legal here */
4331 : : }
4332 : :
4333 : :
4334 : : /****************************************************************************
4335 : : Compare seniority of two atoms in polymer SRU
4336 : : loosely following IUPAC guidelines.
4337 : : Always return non-zero result, that is, resolve the case.
4338 : : ****************************************************************************/
4339 : 2 : int OAD_Polymer_IsFirstAtomRankLower(int atom1, int atom2, OAD_AtProps* aprops)
4340 : : {
4341 : : /* Compare ext-ranks */
4342 : 2 : int result = OAD_Polymer_CompareRanksOfTwoAtoms(atom1, atom2, aprops);
4343 : :
4344 [ + - ]: 2 : if ( result )
4345 : : {
4346 : 2 : return result;
4347 : : }
4348 : :
4349 : : /* Could not resolve who is junior by extended-ranks... */
4350 : : /* As a last resort, simply check which canonical number is lesser */
4351 [ # # ]: 0 : if ( atom1 < atom2 )
4352 : : {
4353 : 0 : return 1;
4354 : : }
4355 [ # # ]: 0 : if ( atom1 > atom2 )
4356 : : {
4357 : 0 : return -1;
4358 : : }
4359 : :
4360 : : /* should not reach there */
4361 : 0 : return 0;
4362 : : }
4363 : :
4364 : :
4365 : : /****************************************************************************/
4366 : 122 : void OAD_ValidateAndSortOutPseudoElementAtoms(ORIG_ATOM_DATA* orig_at_data,
4367 : : int treat_polymers,
4368 : : int bNPZz,
4369 : : int* err,
4370 : : char* pStrErr)
4371 : : {
4372 : 122 : int i, k, n_pseudo = 0;
4373 : 122 : int nsgroups = 0, nzz = 0;
4374 : 122 : int nat = orig_at_data->num_inp_atoms;
4375 : 122 : OAD_Polymer* pd = orig_at_data->polymer;
4376 : 122 : OAD_PolymerUnit* u = NULL;
4377 : :
4378 [ + + - + ]: 122 : int pseudos_allowed = (bNPZz == 1) || (treat_polymers != POLYMERS_NO);
4379 : :
4380 [ + + ]: 929 : for ( k = 0; k < nat; k++ )
4381 : : {
4382 : 807 : int is_zz = 0, is_star = 0, is_zy = 0;
4383 : :
4384 : :
4385 : : /* Though "Zy" is present in our internal Periodic Table,
4386 : : _input_ "Zy" atoms are prohibited.
4387 : : */
4388 [ - + ]: 807 : if ( !strcmp(orig_at_data->at[k].elname, "Zy") )
4389 : : {
4390 : 0 : is_zy = 1;
4391 : : #if 0
4392 : : Disabled 2020 - 04 - 07
4393 : : TREAT_ERR(*err, (70 + 5), "Invalid element(s):");
4394 : : TREAT_ERR(*err, (70 + 5), orig_at_data->at[k].elname);
4395 : : continue;
4396 : : #endif
4397 : : }
4398 : 807 : is_star = !strcmp(orig_at_data->at[k].elname, "*");
4399 [ + - ]: 807 : if ( !is_star )
4400 : : {
4401 : 807 : is_zz = !strcmp(orig_at_data->at[k].elname, "Zz");
4402 : : }
4403 : :
4404 [ + - + - : 807 : if ( is_star || is_zz || is_zy )
- + ]
4405 : : {
4406 : 0 : n_pseudo++;
4407 [ # # ]: 0 : if ( 0 == pseudos_allowed )
4408 : : {
4409 : : /* That's an error */
4410 [ # # ]: 0 : TREAT_ERR(*err, (70 + 5), "Invalid element(s):");
4411 [ # # ]: 0 : TREAT_ERR(*err, (70 + 5), orig_at_data->at[k].elname);
4412 : 0 : continue;
4413 : : }
4414 : :
4415 : : /* Now check if valid pseudoelement atom */
4416 : :
4417 : : /* Should be strictly univalent and single-bonded */
4418 : : /* Should not have isotopic enrichment */
4419 [ # # ]: 0 : if ( orig_at_data->at[k].valence > 1 ||
4420 [ # # ]: 0 : orig_at_data->at[k].chem_bonds_valence > 1 /* || orig_at_data->at[k].iso_atw_diff != 0 */
4421 : : )
4422 : : {
4423 [ # # ]: 0 : TREAT_ERR(*err, (70 + 7), "Invalid pseudo element(s) bonding");
4424 : : /*TREAT_ERR( *err, ( 70 + 7 ), orig_at_data->at[k].elname );*/
4425 : 0 : continue;
4426 : : }
4427 : :
4428 : :
4429 : : /* Now convert both "*" and "Zz" temporarily to "Zy" */
4430 : 0 : mystrncpy(orig_at_data->at[k].elname, "Zy", sizeof("Zy"));
4431 : : }
4432 : : }
4433 : :
4434 : 122 : orig_at_data->n_zy = 0;
4435 : 122 : nzz = 0;
4436 [ + + ]: 122 : if ( orig_at_data->valid_polymer )
4437 : : {
4438 : 2 : nsgroups = pd->n;
4439 : : /* If applicable, check each CRU and back-convert "Zy" to "Zz" (polymer-related pseudoelement atoms)
4440 : : if they are from valid paired CRU crossing bond out-of-bracket caps */
4441 [ + + ]: 4 : for ( i = 0; i < nsgroups; i++ )
4442 : : {
4443 : 2 : u = pd->units[i];
4444 [ + - ]: 2 : if ( u )
4445 : : {
4446 [ - + - - ]: 2 : if ( u->cap1_is_undef && u->cap2_is_undef )
4447 : : {
4448 : : /* valid pair: CRU is capped with two undefined-nature atoms, call them "Zz", finally */
4449 : 0 : strcpy(orig_at_data->at[u->cap1 - 1].elname, "Zz");
4450 : 0 : strcpy(orig_at_data->at[u->cap2 - 1].elname, "Zz");
4451 : 0 : nzz += 2;
4452 : : }
4453 : : }
4454 : : }
4455 : 2 : orig_at_data->polymer->n_pzz = nzz;
4456 : : }
4457 : :
4458 : 122 : orig_at_data->n_zy = n_pseudo - nzz;
4459 [ - + ]: 122 : if ( orig_at_data->n_zy )
4460 : : {
4461 : : /* Have non-polymer-related pseudoelement atoms */
4462 [ # # ]: 0 : if ( 0 == bNPZz )
4463 : : {
4464 [ # # ]: 0 : TREAT_ERR(*err, (70 + 4), "Polymer-unrelated pseudoatoms are not allowed");
4465 : : }
4466 : : }
4467 : :
4468 [ - + ]: 122 : if ( *err )
4469 : : {
4470 : 0 : orig_at_data->valid_polymer = 0;
4471 : : }
4472 : :
4473 : 122 : }
4474 : :
4475 : :
4476 : : /****************************************************************************/
4477 : 0 : int Inp_Atom_GetBondType(inp_ATOM* at, int iatom1, int iatom2)
4478 : : {
4479 : : int i;
4480 : :
4481 [ # # ]: 0 : for ( i = 0; i < at[iatom1].valence; i++ )
4482 : : {
4483 [ # # ]: 0 : if ( at[iatom1].neighbor[i] == iatom2 )
4484 : : {
4485 : 0 : return at[iatom1].bond_type[i];
4486 : : }
4487 : : }
4488 : :
4489 : 0 : return -1;
4490 : : }
|