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