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 : 54 : int OrigAtData_bCheckUnusualValences(ORIG_ATOM_DATA* orig_at_data,
90 : : int bAddIsoH,
91 : : char* pStrErrStruct,
92 : : int bNoWarnings)
93 : : {
94 : 54 : int i, val, num_found = 0;
95 : : char msg[32];
96 : : int len, num_H;
97 : :
98 [ + - + - ]: 54 : int already_here = (orig_at_data && orig_at_data->num_inp_atoms > 0);
99 : :
100 [ + - ]: 54 : inp_ATOM* at = already_here ? orig_at_data->at : NULL;
101 : :
102 [ + - ]: 54 : if ( at )
103 : : {
104 [ + + ]: 673 : for ( i = 0, num_found = 0; i < orig_at_data->num_inp_atoms; i++ )
105 : : {
106 [ + - ]: 619 : num_H = bAddIsoH ? NUMH(at, i) : at[i].num_H;
107 : :
108 : 619 : val = detect_unusual_el_valence(at[i].el_number,
109 : 619 : at[i].charge,
110 : 619 : at[i].radical,
111 : 619 : at[i].chem_bonds_valence,
112 : : num_H,
113 : 619 : at[i].valence);
114 [ + + ]: 619 : if ( val )
115 : : {
116 : 13 : num_found++;
117 [ + - ]: 13 : if ( !bNoWarnings )
118 : : {
119 : 13 : WarningMessage(pStrErrStruct, "Accepted unusual valence(s):");
120 : : }
121 : 13 : len = sprintf(msg, "%s", at[i].elname);
122 [ + + ]: 13 : if ( at[i].charge )
123 : : {
124 : 2 : len += sprintf(msg + len, "%+d", at[i].charge);
125 : : }
126 [ - + ]: 13 : 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 : 13 : len += sprintf(msg + len, "(%d)", val);
133 [ + - ]: 13 : if ( !bNoWarnings )
134 : : {
135 : 13 : WarningMessage(pStrErrStruct, msg);
136 : : }
137 : : }
138 : : }
139 : : }
140 : :
141 : 54 : return num_found;
142 : : }
143 : :
144 : :
145 : : /****************************************************************************
146 : : Make a copy of ORIG_ATOM_DATA
147 : : ****************************************************************************/
148 : 54 : int OrigAtData_Duplicate(ORIG_ATOM_DATA* new_orig_atom,
149 : : ORIG_ATOM_DATA* orig_atom)
150 : : {
151 : 54 : inp_ATOM* at = NULL;
152 : 54 : AT_NUMB* nCurAtLen = NULL;
153 : 54 : AT_NUMB* nOldCompNumber = NULL;
154 : : int k, m, nn;
155 : 54 : int orig_nat = orig_atom->num_inp_atoms;
156 : :
157 : 54 : int ret = -1; /* fail; 0 - OK */
158 : :
159 [ - + ]: 54 : if ( new_orig_atom->at &&
160 [ # # ]: 0 : new_orig_atom->num_inp_atoms >= orig_nat )
161 : : {
162 : 0 : at = new_orig_atom->at;
163 : : }
164 : : else
165 : : {
166 : 54 : at = (inp_ATOM*)inchi_calloc((long long)orig_nat + 1, sizeof(at[0])); /* djb-rwth: cast operator added */
167 [ - + ]: 54 : if ( !at )
168 : : {
169 : 0 : goto exit_function;
170 : : }
171 : : }
172 : :
173 [ - + ]: 54 : if ( new_orig_atom->nOldCompNumber &&
174 [ # # ]: 0 : new_orig_atom->num_components >= orig_atom->num_components )
175 : : {
176 : 0 : nCurAtLen = new_orig_atom->nCurAtLen;
177 : : }
178 : : else
179 : : {
180 : 54 : nCurAtLen = (AT_NUMB*)inchi_calloc((long long)orig_atom->num_components + 1, sizeof(nCurAtLen[0])); /* djb-rwth: cast operator added */
181 [ - + ]: 54 : if ( !nCurAtLen )
182 : : {
183 : 0 : goto exit_function;
184 : : }
185 : : }
186 : :
187 [ - + ]: 54 : if ( new_orig_atom->nCurAtLen &&
188 [ # # ]: 0 : new_orig_atom->num_components >= orig_atom->num_components )
189 : : {
190 : 0 : nOldCompNumber = new_orig_atom->nOldCompNumber;
191 : : }
192 : : else
193 : : {
194 : 54 : nOldCompNumber = (AT_NUMB*)inchi_calloc((long long)orig_atom->num_components + 1,
195 : : sizeof(nOldCompNumber[0])); /* djb-rwth: cast operator added */
196 [ - + ]: 54 : if ( !nOldCompNumber )
197 : : {
198 : 0 : goto exit_function;
199 : : }
200 : : }
201 : :
202 [ - + - + : 54 : if ( at && nCurAtLen && nOldCompNumber )
- + ]
203 : : {
204 : : /* Copy */
205 [ + - ]: 54 : if ( orig_atom->at )
206 : : {
207 : 54 : memcpy(at, orig_atom->at,
208 : : orig_nat * sizeof(new_orig_atom->at[0]));
209 : : }
210 [ - + ]: 54 : if ( orig_atom->nCurAtLen )
211 : : {
212 : 0 : memcpy(nCurAtLen, orig_atom->nCurAtLen,
213 : 0 : orig_atom->num_components * sizeof(nCurAtLen[0]));
214 : : }
215 [ - + ]: 54 : if ( orig_atom->nOldCompNumber )
216 : : {
217 : 0 : memcpy(nOldCompNumber, orig_atom->nOldCompNumber,
218 : 0 : orig_atom->num_components * sizeof(nOldCompNumber[0]));
219 : : }
220 : :
221 : : /* Deallocate */
222 [ - + - - ]: 54 : if ( new_orig_atom->at && new_orig_atom->at != at )
223 : : {
224 [ # # ]: 0 : inchi_free(new_orig_atom->at);
225 : : }
226 [ - + - - ]: 54 : if ( new_orig_atom->nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen )
227 : : {
228 [ # # ]: 0 : inchi_free(new_orig_atom->nCurAtLen);
229 : : }
230 [ - + ]: 54 : if ( new_orig_atom->nOldCompNumber &&
231 [ # # ]: 0 : new_orig_atom->nOldCompNumber != nOldCompNumber )
232 : : {
233 [ # # ]: 0 : inchi_free(new_orig_atom->nOldCompNumber);
234 : : }
235 : :
236 : 54 : *new_orig_atom = *orig_atom;
237 : 54 : new_orig_atom->at = at;
238 : 54 : new_orig_atom->nCurAtLen = nCurAtLen;
239 : 54 : new_orig_atom->nOldCompNumber = nOldCompNumber;
240 : :
241 : : /* Data that are not to be copied */
242 : 54 : new_orig_atom->nNumEquSets = 0;
243 : 54 : memset(new_orig_atom->bSavedInINCHI_LIB, 0, sizeof(new_orig_atom->bSavedInINCHI_LIB)); /* djb-rwth: memset_s C11/Annex K variant? */
244 : 54 : memset(new_orig_atom->bPreprocessed, 0, sizeof(new_orig_atom->bPreprocessed)); /* djb-rwth: memset_s C11/Annex K variant? */
245 : :
246 : :
247 : :
248 : 54 : new_orig_atom->szCoord = NULL;
249 [ + + ]: 54 : if ( orig_atom->szCoord )
250 : : {
251 : 6 : new_orig_atom->szCoord = (MOL_COORD*)inchi_calloc(orig_nat, sizeof(new_orig_atom->szCoord[0]));
252 [ - + ]: 6 : if ( !new_orig_atom->szCoord )
253 : : {
254 : 0 : goto exit_function;
255 : : }
256 : 6 : 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 : 54 : new_orig_atom->nEquLabels = NULL;
263 : 54 : new_orig_atom->nSortedOrder = NULL;
264 : :
265 : 54 : new_orig_atom->polymer = NULL;
266 [ - + ]: 54 : if ( orig_atom->polymer )
267 : : {
268 : : /* Polymer stuff -- deep copy */
269 : 0 : OAD_Polymer* oldp = orig_atom->polymer;
270 : 0 : OAD_Polymer* newp = NULL;
271 : :
272 : 0 : newp = (OAD_Polymer*)inchi_calloc(1, sizeof(OAD_Polymer));
273 [ # # ]: 0 : if ( !newp )
274 : : {
275 [ # # ]: 0 : inchi_free(newp); /* djb-rwth: avoiding memory leak */
276 : 0 : goto exit_function;
277 : : }
278 : 0 : memcpy(newp, orig_atom->polymer, sizeof(OAD_Polymer));
279 : 0 : newp->units = (OAD_PolymerUnit**)inchi_calloc(newp->n, sizeof(OAD_PolymerUnit*)); /* djb-rwth: inchi_calloc must return OAD_PolymerUnit** */
280 [ # # ]: 0 : if ( !newp->units )
281 : : {
282 [ # # ]: 0 : inchi_free(newp); /* djb-rwth: avoiding memory leak */
283 : 0 : goto exit_function;
284 : : }
285 [ # # ]: 0 : for ( k = 0; k < orig_atom->polymer->n; k++ )
286 : : {
287 : 0 : newp->units[k] = OAD_PolymerUnit_CreateCopy(orig_atom->polymer->units[k]);
288 : : }
289 [ # # ]: 0 : 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 : 0 : new_orig_atom->polymer = newp;
302 : : }
303 : :
304 : 54 : new_orig_atom->v3000 = NULL;
305 [ + + ]: 54 : if ( orig_atom->v3000 )
306 : : {
307 : : /* V3000 features -- deep copy */
308 : 48 : OAD_V3000* new_v3000 = NULL;
309 : 48 : new_v3000 = (OAD_V3000*)inchi_calloc(1, sizeof(OAD_V3000));
310 [ - + ]: 48 : if ( !new_v3000 )
311 : : {
312 [ # # ]: 0 : inchi_free(new_v3000); /* djb-rwth: avoiding memory leak */
313 : 0 : goto exit_function;
314 : : }
315 : 48 : memcpy(new_v3000, orig_atom->v3000, sizeof(OAD_V3000));
316 [ + - ]: 48 : if ( orig_atom->v3000->atom_index_orig )
317 : : {
318 : 48 : 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 [ - + ]: 48 : 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 : 48 : memcpy(new_v3000->atom_index_orig, orig_atom->v3000->atom_index_orig, orig_nat * sizeof(int));
326 : : }
327 [ + - ]: 48 : if ( orig_atom->v3000->atom_index_fin )
328 : : {
329 : 48 : 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 [ - + ]: 48 : 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 : 48 : memcpy(new_v3000->atom_index_fin, orig_atom->v3000->atom_index_fin, orig_nat * sizeof(int));
337 : : }
338 [ + + + - ]: 48 : 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 [ + + + - ]: 48 : 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 [ + + + - ]: 48 : 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 [ + + + - ]: 48 : 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 : 48 : new_orig_atom->v3000 = new_v3000;
439 : : }
440 : :
441 : : /* Success */
442 : 54 : ret = 0;
443 : : }
444 : :
445 : 0 : exit_function:
446 : :
447 [ - + ]: 54 : 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 : 54 : 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 : 54 : 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 : 54 : INCHI_MODE bTautFlags = 0;
496 : 54 : INCHI_MODE bTautFlagsDone = 0;
497 : :
498 : : /* 1. Copy orig_inp_data --> prep_inp_data */
499 : :
500 [ - + ]: 54 : 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 [ - + ]: 54 : 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 [ + + ]: 54 : if ( ip->bMolecularInorganics )
556 : : {
557 : : ;
558 : : }
559 [ + - ]: 48 : else if ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS )
560 : : {
561 : 48 : 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 [ + - ]: 54 : if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD )
571 : : {
572 : 54 : i = (0 != (ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD));
573 : 54 : bMayDisconnectMetals(prep_inp_data, i, &bTautFlagsDone); /* changes prep_inp_data->bDisconnectCoord */
574 : 54 : 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 : 54 : orig_inp_data->bDisconnectSalts = prep_inp_data->bDisconnectSalts;
588 : 54 : 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 [ + - - + : 54 : 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 : 54 : prep_inp_data->bDisconnectSalts = 0;
620 : : }
621 : :
622 : : /* Mark the (disconnected) components in prep_inp_data */
623 : :
624 : 54 : prep_inp_data->num_components = MarkDisconnectedComponents(prep_inp_data, 0);
625 : :
626 [ - + ]: 54 : 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 [ - + ]: 54 : 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 : 54 : 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 [ + + ]: 54 : if ( ip->bMolecularInorganics )
667 : : {
668 : 6 : return 0; /* Skipping over current functionality */
669 : : }
670 [ + + ]: 48 : else if ( prep_inp_data->bDisconnectCoord )
671 : : {
672 : :
673 : 1 : prep_inp_data->num_components = MarkDisconnectedComponents(prep_inp_data, 0);
674 [ - + ]: 1 : 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 [ - + ]: 1 : 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 : 1 : 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 [ + - ]: 1 : if ( 0 < (i = DisconnectMetals(prep_inp_data, i, &bTautFlagsDone)) )
715 : : {
716 [ + - ]: 1 : if ( !ip->bNoWarnings )
717 : : {
718 : 1 : WarningMessage(sd->pStrErrStruct, "Metal was disconnected");
719 : : }
720 : 1 : sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_COORD_DONE;
721 [ - + ]: 1 : if ( sd->nErrorType < _IS_WARNING )
722 : : {
723 : 0 : 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 : 1 : prep_inp_data->num_components = MarkDisconnectedComponents(prep_inp_data, 1);
732 : :
733 [ - + ]: 1 : 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 : 1 : inp_ATOM* at = (prep_inp_data)->at;
745 : 1 : int num_at = (prep_inp_data)->num_inp_atoms;
746 [ + + ]: 12 : for ( k = 0; k < num_at; k++ )
747 : : {
748 [ + - - + ]: 11 : 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 [ - + ]: 1 : 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 [ - + ]: 1 : 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 : 47 : inp_ATOM* at = (prep_inp_data)->at;
800 : 47 : int num_at = (prep_inp_data)->num_inp_atoms;
801 [ + + ]: 627 : for ( k = 0; k < num_at; k++ )
802 : : {
803 [ + - - + ]: 580 : 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 : 47 : exit_function:
811 : :
812 [ + - + - ]: 48 : if ( sd->nErrorType < _IS_ERROR && prep_inp_data )
813 : : {
814 [ - + ]: 48 : 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 [ + + ]: 48 : if ( (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) &&
827 [ - + - - ]: 1 : (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 : 48 : sd->bTautFlags[INCHI_BAS] |= bTautFlags; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */
846 : 48 : sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */
847 : :
848 : 48 : 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 : 0 : OAD_PolymerUnit* OAD_PolymerUnit_CreateCopy(OAD_PolymerUnit* u)
1271 : : {
1272 : 0 : int k, err = 0;
1273 : 0 : OAD_PolymerUnit* u2 = NULL;
1274 : :
1275 : 0 : u2 = (OAD_PolymerUnit*)inchi_calloc(1, sizeof(OAD_PolymerUnit));
1276 [ # # ]: 0 : if ( NULL == u2 )
1277 : : {
1278 : 0 : err = 1;
1279 : 0 : goto exit_function;
1280 : : }
1281 : 0 : u2->id = u->id;
1282 : 0 : u2->type = u->type;
1283 : 0 : u2->subtype = u->subtype;
1284 : 0 : u2->conn = u->conn;
1285 : 0 : u2->label = u->label;
1286 : 0 : u2->na = u->na;
1287 : 0 : u2->nb = u->nb;
1288 : 0 : u2->cyclizable = u->cyclizable;
1289 : 0 : u2->cyclized = u->cyclized;
1290 : 0 : u2->cap1_is_undef = u->cap1_is_undef;
1291 : 0 : u2->cap2_is_undef = u->cap2_is_undef;
1292 : :
1293 [ # # ]: 0 : for ( k = 0; k < 4; k++ )
1294 : : {
1295 : 0 : u2->xbr1[k] = u->xbr1[k];
1296 : 0 : u2->xbr2[k] = u->xbr2[k];
1297 : : }
1298 : :
1299 : 0 : strcpy(u2->smt, u->smt);
1300 : :
1301 : 0 : u2->cap1 = u->cap1;
1302 : 0 : u2->end_atom1 = u->end_atom1;
1303 : 0 : u2->cap2 = u->cap2;
1304 : 0 : u2->end_atom2 = u->end_atom2;
1305 : 0 : u2->nbkbonds = u->nbkbonds;
1306 : 0 : u2->maxbkbonds = inchi_max(u->maxbkbonds, u->nbkbonds);
1307 : :
1308 : 0 : u2->alist = (int*)inchi_calloc(u2->na, sizeof(int));
1309 [ # # ]: 0 : if ( !u2->alist )
1310 : : {
1311 : 0 : err = 2;
1312 : 0 : goto exit_function;
1313 : : }
1314 [ # # ]: 0 : for ( k = 0; k < u2->na; k++ )
1315 : : {
1316 : 0 : u2->alist[k] = u->alist[k];
1317 : : }
1318 : :
1319 : 0 : u2->blist = (int*)inchi_calloc(2 * (long long)u2->nb, sizeof(int)); /* djb-rwth: cast operator added */
1320 [ # # ]: 0 : if ( !u2->blist )
1321 : : {
1322 : 0 : err = 2;
1323 : 0 : goto exit_function;
1324 : : }
1325 [ # # ]: 0 : for ( k = 0; k < 2 * u2->nb; k++ )
1326 : : {
1327 : 0 : u2->blist[k] = u->blist[k];
1328 : : }
1329 : :
1330 : 0 : err = imat_new(u2->maxbkbonds, 2, &(u2->bkbonds));
1331 [ # # ]: 0 : if ( !err )
1332 : : {
1333 [ # # ]: 0 : 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 : 0 : exit_function:
1341 [ # # ]: 0 : if ( err )
1342 : : {
1343 : 0 : OAD_PolymerUnit_Free(u2);
1344 : 0 : return NULL;
1345 : : }
1346 : :
1347 : 0 : return u2;
1348 : : }
1349 : :
1350 : :
1351 : : /****************************************************************************/
1352 : 0 : void OAD_PolymerUnit_Free(OAD_PolymerUnit* unit)
1353 : : {
1354 : :
1355 : : ITRACE_("\n************** About to free OAD_PolymerUnit @ %-p\n", unit);
1356 : 0 : OAD_PolymerUnit_DebugTrace(unit);
1357 : :
1358 [ # # ]: 0 : if ( unit )
1359 : : {
1360 [ # # ]: 0 : if ( unit->alist )
1361 : : {
1362 [ # # ]: 0 : inchi_free(unit->alist);
1363 : 0 : unit->alist = NULL;
1364 : : }
1365 [ # # ]: 0 : if ( unit->blist )
1366 : : {
1367 [ # # ]: 0 : inchi_free(unit->blist);
1368 : 0 : unit->blist = NULL;
1369 : : }
1370 [ # # ]: 0 : if ( unit->bkbonds )
1371 : : {
1372 : 0 : imat_free(unit->maxbkbonds, unit->bkbonds);
1373 : 0 : unit->bkbonds = NULL;
1374 : : }
1375 : : }
1376 : :
1377 [ # # ]: 0 : inchi_free(unit);
1378 : :
1379 : 0 : 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 : 0 : 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 [ # # ]: 0 : for ( k = 0; k < u->nb; k++ )
1455 : : {
1456 : : /* Place not-in-unit bond end to first place */
1457 : 0 : int a1 = u->blist[2 * k];
1458 : 0 : int a2 = u->blist[2 * k + 1];
1459 : 0 : int a1_is_not_in_alist = 0;
1460 : 0 : int a1_is_star_atom = 0;
1461 : 0 : int a2_is_not_in_alist = 0;
1462 : 0 : int a2_is_star_atom = 0;
1463 : :
1464 [ # # ]: 0 : if ( !is_in_the_ilist(u->alist, a1, u->na) )
1465 : : {
1466 : 0 : a1_is_not_in_alist = 1;
1467 : : }
1468 [ # # ]: 0 : if ( is_in_the_ilist(star_atoms, a1, n_star_atoms) )
1469 : : {
1470 : 0 : a1_is_star_atom = 1;
1471 : : }
1472 : :
1473 [ # # ]: 0 : if ( !is_in_the_ilist(u->alist, a2, u->na) )
1474 : : {
1475 : 0 : a2_is_not_in_alist = 1;
1476 : : }
1477 [ # # ]: 0 : if ( is_in_the_ilist(star_atoms, a2, n_star_atoms) )
1478 : : {
1479 : 0 : a2_is_star_atom = 1;
1480 : : }
1481 : :
1482 [ # # # # : 0 : if ( (a1_is_not_in_alist || a1_is_star_atom) &&
# # ]
1483 [ # # ]: 0 : (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 [ # # # # ]: 0 : if ( a2_is_star_atom || a2_is_not_in_alist )
1490 : : {
1491 : 0 : u->blist[2 * k] = a2;
1492 : 0 : 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 [ # # ]: 0 : if ( u->nb == 2 ) /* two bonds in SBL */
1500 : : {
1501 : 0 : int b1a1 = u->blist[0];
1502 : 0 : int b1a2 = u->blist[1];
1503 : 0 : int b2a1 = u->blist[2];
1504 : 0 : int b2a2 = u->blist[3];
1505 [ # # ]: 0 : 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 : 0 : return 0;
1519 : : }
1520 : :
1521 : :
1522 : : /****************************************************************************
1523 : : Parse pseudoelement and polymer data
1524 : : (unit, types, subtypes, connections, etc.)
1525 : : ****************************************************************************/
1526 : 54 : int OAD_ValidatePolymerAndPseudoElementData(ORIG_ATOM_DATA* orig_at_data,
1527 : : int treat_polymers,
1528 : : int bNPZz,
1529 : : char* pStrErr,
1530 : : int bNoWarnings)
1531 : : {
1532 : 54 : int i, k, kk, type, subtype, representation, err = 0;
1533 : 54 : int nat = orig_at_data->num_inp_atoms;
1534 : 54 : int nsgroups = 0;
1535 : 54 : OAD_PolymerUnit* u = NULL;
1536 : 54 : 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 : 54 : orig_at_data->valid_polymer = 0;
1543 [ - + - - ]: 54 : if ( treat_polymers && pd )
1544 : : {
1545 : 0 : orig_at_data->valid_polymer = 1;
1546 : : }
1547 [ - + ]: 54 : if ( orig_at_data->valid_polymer )
1548 : : {
1549 : 0 : nsgroups = pd->n;
1550 : : }
1551 [ - + ]: 54 : if ( nsgroups == 1 )
1552 : : {
1553 : : /* Check if copolymer */
1554 : 0 : type = pd->units[0]->type;
1555 [ # # ]: 0 : 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 : 0 : subtype = pd->units[0]->subtype;
1562 [ # # # # : 0 : if ( subtype == POLYMER_SST_RAN || subtype == POLYMER_SST_ALT || subtype == POLYMER_SST_BLK )
# # ]
1563 : : {
1564 [ # # ]: 0 : TREAT_ERR(err, 9002, "Single polymer unit may not be RAN/ALT/BLO");
1565 : 0 : goto exit_function;
1566 : : }
1567 : : }
1568 : :
1569 : : /* For each CRU */
1570 [ - + ]: 54 : for ( i = 0; i < nsgroups; i++ )
1571 : : {
1572 : : /* Check if unit data makes sense */
1573 : 0 : u = pd->units[i];
1574 [ # # # # ]: 0 : if ( u->nb != 0 && u->nb != 2 )
1575 : : {
1576 [ # # ]: 0 : TREAT_ERR(err, 9003, "Number of crossing bonds in polymer unit is not 0 or 2");
1577 : 0 : goto exit_function;
1578 : : }
1579 [ # # ]: 0 : if ( u->na < 1 )
1580 : : {
1581 [ # # ]: 0 : TREAT_ERR(err, 9004, "Empty polymer unit");
1582 : 0 : goto exit_function;
1583 : : }
1584 [ # # ]: 0 : if ( u->na > nat )
1585 : : {
1586 [ # # ]: 0 : TREAT_ERR(err, 9005, "Too large polymer unit");
1587 : 0 : goto exit_function;
1588 : : }
1589 [ # # ]: 0 : for ( k = 0; k < u->na; k++ )
1590 : : {
1591 : 0 : int atom = u->alist[k];
1592 [ # # # # ]: 0 : if ( atom < 1 || atom > nat )
1593 : : {
1594 [ # # ]: 0 : TREAT_ERR(err, 9006, "Invalid atom number in polymer unit");
1595 : 0 : goto exit_function;
1596 : : }
1597 : : /* was not accounting for COP ...
1598 : : if (is_in_the_ilist( pd->pzz, atom, pd->n_pzz ))
1599 : : {
1600 : : TREAT_ERR( err, 9007, "Star atom inside polymer unit" );
1601 : : goto exit_function;
1602 : : }
1603 : : */
1604 : : }
1605 : :
1606 : 0 : OAD_PolymerUnit_SetEndsAndCaps(u, orig_at_data, &err, pStrErr);
1607 : : /* Reveal and store CRU caps and ends('stars and partners')
1608 : : Also set `unit->cap1_is_undef`, `unit->cap2_is_undef`, `unit->cyclizable`
1609 : : */
1610 [ # # ]: 0 : if ( err )
1611 : : {
1612 : 0 : goto exit_function;
1613 : : }
1614 : :
1615 : :
1616 : : /* Set possibly missing unit parameters */
1617 : 0 : u->nbkbonds = 0;
1618 : 0 : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1619 : 0 : u->cyclized = 0;
1620 : : }
1621 : :
1622 : :
1623 : 54 : OAD_ValidateAndSortOutPseudoElementAtoms(orig_at_data, treat_polymers, bNPZz, &err, pStrErr);
1624 : : /* Here we:
1625 : : Make more polymer and pseudoatom data checks
1626 : : Convert both "*" and "Zz" temporarily to "Zy" (polymer-unrelated interal pseudoatoms)
1627 : : If applicable, check each CRU and back-convert "Zy" to "Zz" (polymer-related
1628 : : pseudoelement atoms) if they are for valid bi-undef-end CRU
1629 : : */
1630 : :
1631 [ - + ]: 54 : if ( err )
1632 : : {
1633 : : /* already treated TREAT_ERR( err, 9040, "Improper pseudoelement atoms" ); */
1634 : 0 : goto exit_function;
1635 : : }
1636 : :
1637 : :
1638 : : /* Make more polymer and pseudoatom data checks */
1639 : :
1640 : : /* Check if non-polymer-related Zz/star atoms enabled */
1641 [ - + - - ]: 54 : if ( orig_at_data->n_zy > 0 && bNPZz == 0 )
1642 : : {
1643 [ # # ]: 0 : TREAT_ERR(err, 9, "Non-polymer-related Zz/star atoms are not allowed");
1644 : 0 : goto exit_function;
1645 : : }
1646 : :
1647 [ - + - - ]: 54 : if ( !pd || !orig_at_data->valid_polymer )
1648 : : {
1649 : 54 : goto exit_function;
1650 : : }
1651 : :
1652 [ # # ]: 0 : if ( pd->n_pzz > 0 )
1653 : : {
1654 : : /* Allocate memory for polymer-related pseudoatoms */
1655 [ # # ]: 0 : if ( pd->treat == POLYMERS_NO )
1656 : : {
1657 [ # # ]: 0 : TREAT_ERR(err, 9, "Pseudoelement endgroups are not allowed");
1658 : 0 : goto exit_function;
1659 : : }
1660 [ # # ]: 0 : if ( pd->pzz )
1661 : : {
1662 [ # # ]: 0 : inchi_free(pd->pzz);
1663 : 0 : pd->pzz = NULL;
1664 : : }
1665 : 0 : pd->pzz = (int*)inchi_calloc(pd->n_pzz, sizeof(int));
1666 [ # # ]: 0 : if ( !pd->pzz )
1667 : : {
1668 [ # # ]: 0 : TREAT_ERR(err, 9010, "Not enough memory");
1669 : 0 : goto exit_function;
1670 : : }
1671 : 0 : kk = 0;
1672 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
1673 : : {
1674 [ # # ]: 0 : if ( !strcmp(orig_at_data->at[k].elname, "Zz") )
1675 : : {
1676 : 0 : pd->pzz[kk++] = k + 1; /* djb-rwth: buffer overrun avoided implicitly */
1677 : : }
1678 : : }
1679 : : }
1680 : :
1681 : : /* Check copolymers and ensure that COP includes > 1 SRU */
1682 [ # # ]: 0 : for ( i = 0; i < pd->n; i++ )
1683 : : {
1684 : 0 : u = pd->units[i];
1685 : :
1686 [ # # ]: 0 : if ( u->type == POLYMER_STY_COP ||
1687 [ # # ]: 0 : u->type == POLYMER_STY_SRU /* what drawn as 'SRU' [xyz]n may be actually copolymer [xyz]co */
1688 : : )
1689 : : {
1690 : 0 : int j, in_units = 0;
1691 : :
1692 [ # # ]: 0 : if ( u->nb > 0 )
1693 : : {
1694 : : /* crossing bonds present, either valid SRU or invalid copolymer */
1695 [ # # ]: 0 : if ( u->type == POLYMER_STY_COP )
1696 : : {
1697 [ # # ]: 0 : TREAT_ERR(err, 9026, "Polymer COP unit contains bracket-crossing bonds, not supported");
1698 : 0 : goto exit_function;
1699 : : }
1700 : : else
1701 : : {
1702 : 0 : continue;
1703 : : }
1704 : : }
1705 : : /* now we have no crossing bonds units */
1706 [ # # ]: 0 : for ( j = 0; j < pd->n; j++ )
1707 : : {
1708 [ # # ]: 0 : if ( pd->units[j]->type == POLYMER_STY_COP )
1709 : : {
1710 : 0 : continue;
1711 : : }
1712 [ # # ]: 0 : if ( is_ilist_inside(pd->units[j]->alist, pd->units[j]->na, pd->units[i]->alist, pd->units[i]->na) )
1713 : : {
1714 : 0 : in_units++;
1715 [ # # ]: 0 : if ( in_units == 2 )
1716 : : {
1717 : 0 : break;
1718 : : }
1719 : : }
1720 : : }
1721 [ # # ]: 0 : if ( in_units > 1 )
1722 : : {
1723 [ # # ]: 0 : if ( u->type != POLYMER_STY_COP )
1724 : : {
1725 : 0 : u->type = POLYMER_STY_COP;
1726 [ # # ]: 0 : if ( !bNoWarnings )
1727 : : {
1728 : 0 : WarningMessage(pStrErr, "Convert multiple-subunits unit to copolymer");
1729 : : }
1730 : : }
1731 : : }
1732 : : else /* in_units <= 1)*/
1733 : : {
1734 [ # # ]: 0 : if ( u->type == POLYMER_STY_COP )
1735 : : {
1736 [ # # ]: 0 : TREAT_ERR(err, 9027, "Polymer COP unit contains a single SRU instead of multiple");
1737 : 0 : goto exit_function;
1738 : : }
1739 : : }
1740 : : }
1741 : : }
1742 : :
1743 : 0 : representation = OAD_Polymer_GetRepresentation(pd);
1744 : :
1745 : : /* Make more polymer data checks and perform some corrections*/
1746 [ # # ]: 0 : if ( representation == POLYMER_REPRESENTATION_SOURCE_BASED )
1747 : : {
1748 [ # # ]: 0 : for ( i = 0; i < nsgroups; i++ )
1749 : : {
1750 : : /* Replace source-based 'SRU' with 'MON' */
1751 [ # # ]: 0 : if ( pd->units[i]->type == POLYMER_STY_SRU )
1752 : : {
1753 : 0 : pd->units[i]->type = POLYMER_STY_MON;
1754 [ # # ]: 0 : if ( !bNoWarnings )
1755 : : {
1756 : 0 : WarningMessage(pStrErr, "Converted src-based polymer unit type to MON");
1757 : : }
1758 : : }
1759 [ # # ]: 0 : if ( pd->units[i]->type == POLYMER_STY_COP )
1760 : : {
1761 : : /* Set missing copolymer subtype to RAN */
1762 [ # # ]: 0 : if ( pd->units[i]->subtype == POLYMER_SST_NON )
1763 : : {
1764 : 0 : pd->units[i]->subtype = POLYMER_SST_RAN;
1765 [ # # ]: 0 : if ( !bNoWarnings )
1766 : : {
1767 : 0 : WarningMessage(pStrErr, "Set missing copolymer subtype to RAN");
1768 : : }
1769 : : }
1770 : : }
1771 : : /* Suppress connectivity ("HH", "HT", "EU") */
1772 [ # # ]: 0 : if ( pd->units[i]->conn != POLYMER_CONN_NON )
1773 : : {
1774 : 0 : pd->units[i]->conn = POLYMER_CONN_NON;
1775 [ # # ]: 0 : if ( !bNoWarnings )
1776 : : {
1777 : 0 : WarningMessage(pStrErr, "Ignore connection pattern for src-based polymer unit");
1778 : : }
1779 : : }
1780 : : }
1781 : : }
1782 : :
1783 : : #ifdef ALLOW_MIXED_SRU_AND_MON
1784 [ # # # # ]: 0 : else if ( representation == POLYMER_REPRESENTATION_STRUCTURE_BASED ||
1785 : : representation == POLYMER_REPRESENTATION_MIXED )
1786 : : #else
1787 : : else if ( representation == POLYMER_REPRESENTATION_STRUCTURE_BASED )
1788 : : #endif
1789 : : {
1790 [ # # ]: 0 : for ( i = 0; i < nsgroups; i++ )
1791 : : {
1792 : : int a1, a2, a1_is_not_in_alist, a1_is_star_atom, a2_is_not_in_alist, a2_is_star_atom;
1793 : :
1794 : 0 : u = pd->units[i];
1795 : :
1796 : : /* SRU that is copolymer unit embedding other SRU's */
1797 [ # # ]: 0 : if ( u->nb == 0 )
1798 : : {
1799 [ # # ]: 0 : if ( u->type == POLYMER_STY_COP )
1800 : : {
1801 : : ;
1802 : : }
1803 [ # # ]: 0 : else if ( u->type == POLYMER_STY_SRU )
1804 : : {
1805 : 0 : u->type = POLYMER_STY_COP;
1806 [ # # ]: 0 : if ( !bNoWarnings )
1807 : : {
1808 : 0 : WarningMessage(pStrErr, "Set copolymer embedding unit mark to COP");
1809 : : }
1810 : : }
1811 : : }
1812 [ # # ]: 0 : if ( u->type == POLYMER_STY_COP )
1813 : : {
1814 : 0 : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1815 : : /* Set possibly missing copolymer subtype to RAN */
1816 [ # # ]: 0 : if ( u->subtype == POLYMER_SST_NON )
1817 : : {
1818 : 0 : u->subtype = POLYMER_SST_RAN;
1819 [ # # ]: 0 : if ( !bNoWarnings )
1820 : : {
1821 : 0 : WarningMessage(pStrErr, "Set missing copolymer subtype to RAN");
1822 : : }
1823 : : }
1824 : 0 : continue;
1825 : : }
1826 : :
1827 : : #ifdef ALLOW_MIXED_SRU_AND_MON
1828 [ # # ]: 0 : if ( u->type == POLYMER_STY_MON )
1829 : : {
1830 : 0 : continue;
1831 : : }
1832 : : #endif
1833 : : /* SRU with endgroups or stars. Check it. */
1834 [ # # ]: 0 : for ( k = 0; k < u->nb; k++ )
1835 : : {
1836 : : /* Check that there are no H end groups */
1837 : 0 : a1 = u->blist[2 * k]; a2 = u->blist[2 * k + 1];
1838 [ # # ]: 0 : if ( !strcmp(orig_at_data->at[a1 - 1].elname, "H") ||
1839 [ # # ]: 0 : !strcmp(orig_at_data->at[a1 - 1].elname, "D") ||
1840 [ # # ]: 0 : !strcmp(orig_at_data->at[a1 - 1].elname, "T") )
1841 : : {
1842 [ # # ]: 0 : TREAT_ERR(err, 9030, "H as polymer end group is not supported");
1843 : 0 : goto exit_function;
1844 : : }
1845 [ # # ]: 0 : if ( !strcmp(orig_at_data->at[a2 - 1].elname, "H") ||
1846 [ # # ]: 0 : !strcmp(orig_at_data->at[a2 - 1].elname, "D") ||
1847 [ # # ]: 0 : !strcmp(orig_at_data->at[a2 - 1].elname, "T") )
1848 : : {
1849 [ # # ]: 0 : TREAT_ERR(err, 9031, "H as polymer end group is not supported");
1850 : 0 : goto exit_function;
1851 : : }
1852 : : /* Ensure that caps of polymer unit lie outside it */
1853 : 0 : a1_is_not_in_alist = a1_is_star_atom = 0;
1854 : 0 : a2_is_not_in_alist = a2_is_star_atom = 0;
1855 [ # # ]: 0 : if ( !is_in_the_ilist(u->alist, a1, u->na) )
1856 : : {
1857 : 0 : a1_is_not_in_alist = 1;
1858 : : }
1859 [ # # ]: 0 : if ( is_in_the_ilist(pd->pzz, a1, pd->n_pzz) )
1860 : : {
1861 : 0 : a1_is_star_atom = 1;
1862 : : }
1863 [ # # ]: 0 : if ( !is_in_the_ilist(u->alist, a2, u->na) )
1864 : : {
1865 : 0 : a2_is_not_in_alist = 1;
1866 : : }
1867 [ # # ]: 0 : if ( is_in_the_ilist(pd->pzz, a2, pd->n_pzz) )
1868 : : {
1869 : 0 : a2_is_star_atom = 1;
1870 : : }
1871 [ # # # # : 0 : if ( (a1_is_not_in_alist || a1_is_star_atom) &&
# # ]
1872 [ # # ]: 0 : (a2_is_not_in_alist || a2_is_star_atom) )
1873 : : {
1874 [ # # ]: 0 : TREAT_ERR(err, 9032, "Caps of polymer unit lie inside it");
1875 : 0 : goto exit_function;
1876 : : }
1877 : : }
1878 : :
1879 [ # # # # ]: 0 : if ( u->type == POLYMER_STY_SRU || u->type == POLYMER_STY_MOD ||
1880 [ # # # # ]: 0 : u->type == POLYMER_STY_CRO || u->type == POLYMER_STY_MER )
1881 : : {
1882 : : /* If SRU connection is missing, set to default ('either') */
1883 [ # # ]: 0 : if ( u->conn == POLYMER_CONN_NON )
1884 : : {
1885 [ # # ]: 0 : if ( !bNoWarnings )
1886 : : {
1887 : 0 : WarningMessage(pStrErr, "Set missing copolymer unit connection to EU");
1888 : : }
1889 : 0 : u->conn = POLYMER_CONN_EU;
1890 : : }
1891 : :
1892 [ # # # # ]: 0 : if ( u->cap1 && u->cap2 )
1893 : : {
1894 : : /* Set SRU closure type */
1895 [ # # ]: 0 : if ( u->na == 1 )
1896 : : {
1897 : : #ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL
1898 : 0 : u->cyclizable = CLOSING_SRU_DIRADICAL;
1899 : : #else
1900 : : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1901 : : #ifdef CLOSING_STARRED_SRU_IS_A_MUST
1902 : : TREAT_ERR(err, 9029, "Could not perform SRU closure");
1903 : : goto exit_function;
1904 : : #endif
1905 : : #endif
1906 : : }
1907 [ # # ]: 0 : else if ( u->na == 2 )
1908 : : {
1909 : :
1910 : : #ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND
1911 : 0 : u->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;
1912 : : #else
1913 : : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1914 : : #ifdef CLOSING_STARRED_SRU_IS_A_MUST
1915 : : TREAT_ERR(err, 9029, "Could not perform SRU closure");
1916 : : goto exit_function;
1917 : : #endif
1918 : : #endif
1919 : : }
1920 : : else
1921 : : {
1922 : 0 : u->cyclizable = CLOSING_SRU_RING;
1923 : : }
1924 : : }
1925 : :
1926 [ # # ]: 0 : if ( u->conn != POLYMER_CONN_HT )
1927 : : {
1928 : : /* frame shift/SRU cyclization is for head-to-tail connections only */
1929 : 0 : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
1930 : : }
1931 : :
1932 [ # # ]: 0 : if ( u->cyclizable != CLOSING_SRU_NOT_APPLICABLE )
1933 : : {
1934 : : /* Allocate PS (frame-shiftable) bonds */
1935 [ # # ]: 0 : if ( u->bkbonds )
1936 : : {
1937 : 0 : imat_free(u->maxbkbonds, u->bkbonds);
1938 : 0 : u->bkbonds = NULL;
1939 : : }
1940 : 0 : u->maxbkbonds = orig_at_data->num_inp_bonds + 2;
1941 : 0 : err = imat_new(u->maxbkbonds, 2, &(u->bkbonds));
1942 [ # # ]: 0 : if ( err )
1943 : : {
1944 [ # # ]: 0 : TREAT_ERR(err, 9034, "Not enough memory (polymers)");
1945 : 0 : goto exit_function;
1946 : : }
1947 : : }
1948 : : }
1949 : :
1950 : : }
1951 : : }
1952 : : else
1953 : : {
1954 [ # # ]: 0 : TREAT_ERR(err, 9035, "Invalid kind of polymer representation");
1955 : 0 : goto exit_function;
1956 : : }
1957 : :
1958 : 0 : orig_at_data->valid_polymer = 1;
1959 : :
1960 : 54 : exit_function:
1961 [ - + ]: 54 : if ( err )
1962 : : {
1963 : 0 : orig_at_data->valid_polymer = 0;
1964 : : }
1965 : :
1966 : 54 : return err;
1967 : : }
1968 : :
1969 : :
1970 : : /****************************************************************************/
1971 : 0 : int UnMarkRingSystemsInp(inp_ATOM* at, int num_atoms)
1972 : : {
1973 : : int i;
1974 [ # # ]: 0 : for ( i = 0; i < num_atoms; i++ )
1975 : : {
1976 : 0 : at[i].bCutVertex = 0;
1977 : 0 : at[i].nRingSystem = 0;
1978 : 0 : at[i].nNumAtInRingSystem = 0;
1979 : 0 : at[i].nBlockSystem = 0;
1980 : : }
1981 : :
1982 : 0 : return 0;
1983 : : }
1984 : :
1985 : :
1986 : : /****************************************************************************
1987 : : Preprocess OAD_Polymer (NB: frame shift is invoked from here)
1988 : : ****************************************************************************/
1989 : 0 : int OAD_Polymer_CyclizeCloseableUnits(ORIG_ATOM_DATA* orig_at_data,
1990 : : int treat_polymers,
1991 : : char* pStrErr,
1992 : : int bNoWarnings)
1993 : : {
1994 : 0 : int i, err = 0; /* djb-rwth: removing redundant variables */
1995 : :
1996 [ # # ]: 0 : for ( i = 0; i < orig_at_data->polymer->n; i++ )
1997 : : {
1998 : 0 : OAD_PolymerUnit* unit = orig_at_data->polymer->units[i];
1999 : :
2000 [ # # ]: 0 : if ( !unit->cyclizable )
2001 : : {
2002 : 0 : continue;
2003 : : }
2004 : :
2005 : : /* Find stars and their partners */
2006 : 0 : OAD_PolymerUnit_SetEndsAndCaps(unit, orig_at_data, &err, pStrErr);
2007 : : /* Reveal and store CRU caps and ends('stars and partners')
2008 : : Also set `unit->cap1_is_undef`, `unit->cap2_is_undef`, `unit->cyclizable`
2009 : : */
2010 [ # # ]: 0 : if ( err )
2011 : : {
2012 : 0 : break;
2013 : : }
2014 [ # # ]: 0 : if ( !unit->cyclizable )
2015 : : {
2016 : 0 : continue;
2017 : : }
2018 : :
2019 : :
2020 [ # # ]: 0 : if ( OAD_PolymerUnit_HasMetal(unit, orig_at_data->at) )
2021 : : {
2022 : : /*unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;*/
2023 [ # # ]: 0 : if ( unit->cyclizable == CLOSING_SRU_RING )
2024 : : {
2025 : : /*unit->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;*/
2026 [ # # ]: 0 : if ( !bNoWarnings )
2027 : : {
2028 : 0 : WarningMessage(pStrErr, "Frame shift in metallated polymer unit may be missed");
2029 : : }
2030 : : }
2031 : : }
2032 : :
2033 : : /* Now remove bonds to cap ("star atoms and cyclize a SRU */
2034 : 0 : OAD_PolymerUnit_UnlinkCapsAndConnectEndAtoms(unit, orig_at_data, &err, pStrErr);
2035 : :
2036 [ # # ]: 0 : if ( err )
2037 : : {
2038 : 0 : break;
2039 : : }
2040 [ # # ]: 0 : if ( !unit->cyclizable )
2041 : : {
2042 : 0 : continue;
2043 : : }
2044 : :
2045 : : /* djb-rwth: removing redundant code */
2046 : : }
2047 : :
2048 : : /*
2049 : : if ( ncyclized )
2050 : : {
2051 : : if (!bNoWarnings)
2052 : : {
2053 : : WarningMessage( pStrErr, "Made provision for frame shift in polymer unit(s)" );
2054 : : }
2055 : : }
2056 : : */
2057 : :
2058 : 0 : return err;
2059 : : }
2060 : :
2061 : :
2062 : : /****************************************************************************
2063 : : Check if SRU contains metal
2064 : : ****************************************************************************/
2065 : 0 : int OAD_PolymerUnit_HasMetal(OAD_PolymerUnit* u, inp_ATOM* at)
2066 : : {
2067 : : int i;
2068 [ # # ]: 0 : for ( i = 0; i < u->na; i++ )
2069 : : {
2070 [ # # ]: 0 : if ( is_el_a_metal(at[u->alist[i] - 1].el_number) )
2071 : : {
2072 : 0 : return 1;
2073 : : }
2074 : : }
2075 : :
2076 : 0 : return 0;
2077 : : }
2078 : :
2079 : :
2080 : : /****************************************************************************/
2081 : 216 : void OAD_Polymer_Free(OAD_Polymer* pd)
2082 : : {
2083 [ - + ]: 216 : if ( pd )
2084 : : {
2085 [ # # ]: 0 : if ( pd->pzz )
2086 : : {
2087 [ # # ]: 0 : inchi_free(pd->pzz);
2088 : 0 : pd->pzz = NULL;
2089 : 0 : pd->n_pzz = 0;
2090 : : }
2091 [ # # # # ]: 0 : if ( pd->n && pd->units )
2092 : : {
2093 : : int k;
2094 [ # # ]: 0 : for ( k = 0; k < pd->n; k++ )
2095 : : {
2096 : 0 : OAD_PolymerUnit_Free(pd->units[k]);
2097 : : }
2098 [ # # ]: 0 : inchi_free(pd->units);
2099 : 0 : pd->units = NULL;
2100 : 0 : pd->n = 0;
2101 : : }
2102 [ # # ]: 0 : inchi_free(pd);
2103 : 0 : pd = NULL;
2104 : : }
2105 : :
2106 : 216 : return;
2107 : : }
2108 : :
2109 : :
2110 : : /****************************************************************************/
2111 : 0 : void OAD_PolymerUnit_UnlinkCapsAndConnectEndAtoms(OAD_PolymerUnit* unit,
2112 : : ORIG_ATOM_DATA* orig_inp_data,
2113 : : int* err,
2114 : : char* pStrErr)
2115 : : {
2116 : : int bond_type, bond_stereo;
2117 : :
2118 : 0 : *err = 0;
2119 [ # # ]: 0 : if ( !unit->cyclizable )
2120 : : {
2121 : 0 : return;
2122 : : }
2123 : :
2124 [ # # ]: 0 : if ( unit->cyclizable == CLOSING_SRU_RING )
2125 : : {
2126 : : /* Disconnect both star atoms */
2127 : 0 : OrigAtData_RemoveBond(unit->cap1 - 1, unit->end_atom1 - 1, orig_inp_data->at,
2128 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2129 : :
2130 : 0 : OrigAtData_RemoveBond(unit->cap2 - 1, unit->end_atom2 - 1, orig_inp_data->at,
2131 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2132 : :
2133 : 0 : OrigAtData_AddSingleStereolessBond(unit->end_atom1 - 1, unit->end_atom2 - 1,
2134 : : orig_inp_data->at, &orig_inp_data->num_inp_bonds);
2135 : : }
2136 : :
2137 [ # # ]: 0 : else if ( unit->cyclizable == CLOSING_SRU_HIGHER_ORDER_BOND )
2138 : : {
2139 : : int elevated; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
2140 : 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 */
2141 : : #if 0
2142 : : /* the bond may already be broken at metal disconnection, so ignore the result here */
2143 : : if ( !elevated )
2144 : : {
2145 : : /* *err = 1; */
2146 : : WarningMessage(pStrErr, "SRU closure via higher order bond failed");
2147 : : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2148 : : return;
2149 : : }
2150 : : #endif
2151 : 0 : OrigAtData_RemoveBond(unit->cap1 - 1, unit->end_atom1 - 1, orig_inp_data->at,
2152 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2153 : 0 : OrigAtData_RemoveBond(unit->cap2 - 1, unit->end_atom2 - 1, orig_inp_data->at,
2154 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2155 : : }
2156 : :
2157 [ # # ]: 0 : else if ( unit->cyclizable == CLOSING_SRU_DIRADICAL )
2158 : : {
2159 : 0 : orig_inp_data->at[unit->end_atom1 - 1].radical = RADICAL_TRIPLET;
2160 : 0 : OrigAtData_RemoveBond(unit->cap1 - 1, unit->end_atom1 - 1, orig_inp_data->at,
2161 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2162 : 0 : OrigAtData_RemoveBond(unit->cap2 - 1, unit->end_atom2 - 1, orig_inp_data->at,
2163 : : &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds);
2164 : : }
2165 : :
2166 [ # # ]: 0 : if ( !*err )
2167 : : {
2168 : 0 : unit->cyclized = 1;
2169 : : }
2170 : :
2171 : 0 : return;
2172 : : }
2173 : :
2174 : :
2175 : : /****************************************************************************/
2176 : 0 : void OAD_PolymerUnit_FindEndsAndCaps(OAD_PolymerUnit* unit,
2177 : : ORIG_ATOM_DATA* orig_at_data,
2178 : : int* end1,
2179 : : int* cap1,
2180 : : int* cap1_is_star,
2181 : : int* end2,
2182 : : int* cap2,
2183 : : int* cap2_is_star,
2184 : : int* err,
2185 : : char* pStrErr)
2186 : : {
2187 : 0 : int i, j, i_inside = 0, j_inside = 0;
2188 : 0 : int num_atoms = orig_at_data->num_inp_atoms;
2189 : :
2190 : 0 : *end1 = *end2 = *cap1 = *cap2 = 0;
2191 : 0 : *cap1_is_star = *cap2_is_star = 0;
2192 : 0 : *err = 0;
2193 : :
2194 [ # # # # ]: 0 : if ( !unit->blist || unit->nb < 1 )
2195 : : {
2196 : 0 : return;
2197 : : }
2198 : : /* Left crossing bond */
2199 : 0 : i = unit->blist[0];
2200 : 0 : j = unit->blist[1];
2201 : 0 : i_inside = (NULL != is_in_the_ilist(unit->alist, i, unit->na));
2202 : 0 : j_inside = (NULL != is_in_the_ilist(unit->alist, j, unit->na));
2203 [ # # # # ]: 0 : if ( i_inside && j_inside )
2204 : : {
2205 [ # # ]: 0 : TREAT_ERR(*err, 9032, "Polymer CRU cap(s) lie inside CRU");
2206 : 0 : return;
2207 : : }
2208 [ # # ]: 0 : if ( i_inside )
2209 : : {
2210 : 0 : *end1 = i;
2211 : 0 : *cap1 = j;
2212 : : }
2213 : : else
2214 : : {
2215 : 0 : *end1 = j;
2216 : 0 : *cap1 = i;
2217 : : }
2218 [ # # ]: 0 : if ( !strcmp(orig_at_data->at[*cap1 - 1].elname, "Zz") )
2219 : : {
2220 : 0 : *cap1_is_star = 1;
2221 : : }
2222 : : /* Right crossing bond */
2223 : 0 : i = unit->blist[2];
2224 : 0 : j = unit->blist[3];
2225 : 0 : i_inside = NULL != is_in_the_ilist(unit->alist, i, unit->na);
2226 : 0 : j_inside = NULL != is_in_the_ilist(unit->alist, j, unit->na);
2227 [ # # # # ]: 0 : if ( i_inside && j_inside )
2228 : : {
2229 [ # # ]: 0 : TREAT_ERR(*err, 9032, "Polymer CRU cap(s) lie inside CRU");
2230 : : }
2231 [ # # ]: 0 : if ( i_inside )
2232 : : {
2233 : 0 : *end2 = i;
2234 : 0 : *cap2 = j;
2235 : : }
2236 : : else
2237 : : {
2238 : 0 : *end2 = j;
2239 : 0 : *cap2 = i;
2240 : : }
2241 [ # # ]: 0 : if ( !strcmp(orig_at_data->at[*cap2 - 1].elname, "Zz") )
2242 : : {
2243 : 0 : *cap2_is_star = 1;
2244 : : }
2245 : : /* Checks */
2246 [ # # # # : 0 : if ( *end1 <= 0 || *end1 > num_atoms || *cap1 <= 0 || *cap1 > num_atoms )
# # # # ]
2247 : : {
2248 [ # # ]: 0 : TREAT_ERR(*err, 9090, "Invalid polymer CRU crossing bond");
2249 : 0 : return;
2250 : : }
2251 [ # # # # : 0 : if ( *end2 <= 0 || *end2 > num_atoms || *cap2 <= 0 || *cap2 > num_atoms )
# # # # ]
2252 : : {
2253 [ # # ]: 0 : TREAT_ERR(*err, 9091, "Invalid polymer CRU crossing bond");
2254 : 0 : return;
2255 : : }
2256 [ # # ]: 0 : if ( *cap1 == *cap2 ) /* || (*end1 == *end2 && unit->na>1) */
2257 : : {
2258 [ # # ]: 0 : TREAT_ERR(*err, 9090, "Invalid polymer CRU surrounding");
2259 : 0 : return;
2260 : : }
2261 : :
2262 : : /* Paranoia, 2020-05-22 */
2263 : 0 : unit->end_atom1 = *end1;
2264 : 0 : unit->end_atom2 = *end2;
2265 : 0 : unit->cap1 = *cap1;
2266 : 0 : unit->cap2 = *cap2;
2267 : :
2268 : 0 : *err = 0;
2269 : 0 : return;
2270 : : }
2271 : :
2272 : :
2273 : : /****************************************************************************
2274 : : Reveal and store CRU caps and ends ('stars and partners')
2275 : : ****************************************************************************/
2276 : 0 : void OAD_PolymerUnit_SetEndsAndCaps(OAD_PolymerUnit* unit,
2277 : : ORIG_ATOM_DATA* orig_at_data,
2278 : : int* err,
2279 : : char* pStrErr)
2280 : : {
2281 : : int k;
2282 : :
2283 : 0 : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2284 : 0 : unit->end_atom1 = unit->end_atom2 = unit->cap1 = unit->cap2 = -1;
2285 : 0 : unit->cap1_is_undef = unit->cap2_is_undef = 0;
2286 : :
2287 : 0 : OAD_PolymerUnit_FindEndsAndCaps(unit, orig_at_data,
2288 : : &unit->end_atom1, &unit->cap1, &unit->cap1_is_undef,
2289 : : &unit->end_atom2, &unit->cap2, &unit->cap2_is_undef,
2290 : : err, pStrErr);
2291 : :
2292 [ # # ]: 0 : if ( *err )
2293 : : {
2294 : 0 : goto exit_function;
2295 : : }
2296 : :
2297 : : #if ( defined(DEBUG_POLYMERS) && ( DEBUG_POLYMERS != 0 ) )
2298 : : ITRACE_("Cap-end_atom pairs (numbers are from 1) are: %-d-%-d and %-d-%-d\n",
2299 : : unit->cap1, unit->end_atom1, unit->cap2, unit->end_atom2);
2300 : : #endif
2301 : :
2302 [ # # # # ]: 0 : if ( !unit->cap1_is_undef && !unit->cap2_is_undef )
2303 : : {
2304 : 0 : goto exit_function;
2305 : : }
2306 : :
2307 : : /* The rest is applicable only to *---SRU---* case */
2308 : :
2309 : : /* Stars are separated by one atom - that's not error but do nothing */
2310 [ # # ]: 0 : if ( unit->end_atom1 == unit->end_atom2 )
2311 : : {
2312 : : #ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL
2313 : 0 : unit->cyclizable = CLOSING_SRU_DIRADICAL;
2314 : : #else
2315 : : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2316 : : #endif
2317 : 0 : goto exit_function;
2318 : : }
2319 : :
2320 : : /* Stars are separated by two atoms - that's not error but do nothing */
2321 [ # # ]: 0 : for ( k = 0; k < orig_at_data->at[unit->end_atom1 - 1].valence; k++ )
2322 : : {
2323 [ # # ]: 0 : if ( orig_at_data->at[unit->end_atom1 - 1].neighbor[k] == unit->end_atom2 - 1 )
2324 : : {
2325 : : #ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND
2326 : 0 : unit->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;
2327 : : #else
2328 : : unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2329 : : #endif
2330 : 0 : goto exit_function;
2331 : : }
2332 : : }
2333 : :
2334 : 0 : unit->cyclizable = CLOSING_SRU_RING;
2335 : :
2336 : 0 : exit_function:
2337 : :
2338 : 0 : return;
2339 : : }
2340 : :
2341 : :
2342 : : /****************************************************************************
2343 : : Replace original atom numbers in polymer data with (canonical num + 1)
2344 : : Then prepare:
2345 : : units2 a copy of original polymer units (p->units) with
2346 : : atomic numbers changed to curr canonical ones;
2347 : : atoms in alists sorted; atoms in blists
2348 : : and blists themselves are sorted
2349 : : unum numbers of units (0..p->n) as they go when
2350 : : sorted by alist's in lexicographic orders
2351 : : ****************************************************************************/
2352 : 0 : int OAD_Polymer_PrepareWorkingSet(OAD_Polymer* p,
2353 : : int* cano_nums,
2354 : : int* compnt_nums,
2355 : : OAD_PolymerUnit** units2, /* allocd by caller, to be filled */
2356 : : int* unum) /* allocd by caller, to be filled */
2357 : :
2358 : : {
2359 : 0 : int i, k, err = 0, cano_num1 = -1, cano_num2 = -1;
2360 : : OAD_PolymerUnit* u;
2361 : :
2362 : : /*OAD_Polymer_DebugTrace( p );*/
2363 : :
2364 : : /* Replace original atom numbers in polymer data with canonical plus 1. */
2365 : : /* Note that we use 'cano1 nums', that is, 1-based (InChI internal 'cano nums' are 0-based)*/
2366 : : /* Also remove from the list atoms who mapped to cano number 0 ( == -1 + 1_offset ), */
2367 : : /* they are explicit H's which have already been deleted. */
2368 [ # # ]: 0 : for ( k = 0; k < p->n_pzz; k++ )
2369 : : {
2370 : 0 : cano_num1 = cano_nums[p->pzz[k]] + 1;
2371 [ # # ]: 0 : if ( cano_num1 == 0 )
2372 : : {
2373 : : /* we shouldn't arrive here */
2374 : 0 : err = 10;
2375 : 0 : goto exit_function;
2376 : : }
2377 : 0 : p->pzz[k] = cano_num1;
2378 : : }
2379 : :
2380 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
2381 : : {
2382 : 0 : int na_new = -1;
2383 : 0 : u = units2[i];
2384 : :
2385 [ # # ]: 0 : for ( k = 0; k < u->na; k++ )
2386 : : {
2387 : 0 : cano_num1 = cano_nums[u->alist[k]] + 1;
2388 [ # # ]: 0 : if ( cano_num1 == 0 )
2389 : : {
2390 : 0 : continue;
2391 : : }
2392 : 0 : u->alist[++na_new] = cano_num1;
2393 : : }
2394 : 0 : u->na = na_new + 1;
2395 [ # # ]: 0 : for ( k = 0; k < 2 * u->nb; k++ )
2396 : : {
2397 : 0 : cano_num1 = cano_nums[u->blist[k]] + 1;
2398 [ # # ]: 0 : if ( cano_num1 == 0 )
2399 : : {
2400 : : /* Can not proceed further as one of PU crossing bond ends
2401 : : leads to explicit H which has been removed already */
2402 : 0 : err = 11;
2403 : 0 : goto exit_function;
2404 : : }
2405 : 0 : u->blist[k] = cano_num1;
2406 : : }
2407 : :
2408 : 0 : cano_num1 = cano_nums[u->cap1] + 1;
2409 [ # # ]: 0 : if ( cano_num1 == 0 )
2410 : : {
2411 : 0 : err = 11;
2412 : 0 : goto exit_function;
2413 : : }
2414 : 0 : u->cap1 = cano_num1;
2415 : :
2416 : 0 : cano_num1 = cano_nums[u->cap2] + 1;
2417 [ # # ]: 0 : if ( cano_num1 == 0 )
2418 : : {
2419 : 0 : err = 11;
2420 : 0 : goto exit_function;
2421 : : }
2422 : 0 : u->cap2 = cano_num1;
2423 : :
2424 : 0 : cano_num1 = cano_nums[u->end_atom1] + 1;
2425 [ # # ]: 0 : if ( cano_num1 == 0 )
2426 : : {
2427 : 0 : err = 11;
2428 : 0 : goto exit_function;
2429 : : }
2430 : 0 : u->end_atom1 = cano_num1;
2431 : :
2432 : 0 : cano_num1 = cano_nums[u->end_atom2] + 1;
2433 [ # # ]: 0 : if ( cano_num1 == 0 )
2434 : : {
2435 : 0 : err = 11;
2436 : 0 : goto exit_function;
2437 : : }
2438 : 0 : u->end_atom2 = cano_num1;
2439 : :
2440 [ # # ]: 0 : for ( k = 0; k < u->nbkbonds; k++ )
2441 : : {
2442 : 0 : cano_num1 = cano_nums[u->bkbonds[k][0]] + 1;
2443 [ # # ]: 0 : if ( cano_num1 == 0 )
2444 : : {
2445 : 0 : continue;
2446 : : }
2447 : 0 : cano_num2 = cano_nums[u->bkbonds[k][1]] + 1;
2448 [ # # ]: 0 : if ( cano_num2 == 0 )
2449 : : {
2450 : 0 : continue;
2451 : : }
2452 : 0 : u->bkbonds[k][0] = inchi_min(cano_num1, cano_num2);
2453 : 0 : u->bkbonds[k][1] = inchi_max(cano_num1, cano_num2);
2454 : : }
2455 : : }
2456 : :
2457 : : /* Sort the atoms and the bonds in all units */
2458 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
2459 : : {
2460 : 0 : u = units2[i];
2461 : :
2462 : : /* sort atoms (alist) */
2463 : 0 : iisort(u->alist, u->na);
2464 : :
2465 : : /*ITRACE_( "\n*** Polymer unit %-d : ( ", i );
2466 : : for (k = 0; k < u->na - 1; k++)
2467 : : {
2468 : : ITRACE_( "%-d-", u->alist[k] );
2469 : : }
2470 : : ITRACE_( "%-d )\n", u->alist[u->na - 1] );*/
2471 : :
2472 : : /* Sort bonds (blist) */
2473 : 0 : err = OAD_PolymerUnit_OrderBondAtomsAndBondsThemselves(u, p->n_pzz, p->pzz);
2474 [ # # ]: 0 : if ( err )
2475 : : {
2476 : : /* crossing bonds in blist are invalid */
2477 : 0 : err = 12;
2478 : 0 : goto exit_function;
2479 : : }
2480 : :
2481 : : /* Check each unit for >1 connected components */
2482 : : #if 0
2483 : : {
2484 : : int icompnt;
2485 : : icompnt = compnt_nums[u->alist[0] - 1];
2486 : : for ( k = 1; k < u->na; k++ )
2487 : : {
2488 : : if ( compnt_nums[u->alist[k] - 1] != icompnt )
2489 : : {
2490 : : u->disjoint = 1;
2491 : : break;
2492 : : }
2493 : : }
2494 : : }
2495 : : #endif
2496 : :
2497 : : }
2498 : :
2499 : : /* Sort all units in modified alist's lexicographic order
2500 : : (modification is: longer list always go first ) */
2501 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
2502 : : {
2503 : 0 : unum[i] = i;
2504 : : }
2505 [ # # ]: 0 : for ( i = 1; i < p->n; i++ )
2506 : : {
2507 : 0 : int tmp = unum[i];
2508 : 0 : int j = i - 1;
2509 [ # # # # ]: 0 : while ( j >= 0 && OAD_PolymerUnit_CompareAtomListsMod(units2[unum[j]], units2[tmp]) > 0 )
2510 : : /*while ( j >= 0 && OAD_PolymerUnit_CompareAtomLists( units2[ unum[j] ], units2[ tmp ] ) > 0 )*/
2511 : : {
2512 : 0 : unum[j + 1] = unum[j];
2513 : 0 : j--;
2514 : : }
2515 : 0 : unum[j + 1] = tmp;
2516 : : }
2517 : :
2518 : 0 : exit_function:
2519 : :
2520 : 0 : return err;
2521 : : }
2522 : :
2523 : :
2524 : : /****************************************************************************
2525 : : Helper for cyclizing CRU. NB: 0-based
2526 : : ****************************************************************************/
2527 : 0 : int OrigAtData_RemoveHalfBond(int this_atom,
2528 : : int other_atom,
2529 : : inp_ATOM* at,
2530 : : int* bond_type,
2531 : : int* bond_stereo)
2532 : : {
2533 : : int k, kk;
2534 : : /* djb-rwth: fixing oss-fuzz issues #68286, #30342 */
2535 [ # # # # : 0 : if ( at && (this_atom >= 0) && (other_atom >= 0) )
# # ]
2536 : : {
2537 : 0 : inp_ATOM* a = &(at[this_atom]);
2538 [ # # ]: 0 : if ( a )
2539 : : {
2540 [ # # ]: 0 : for ( k = 0; k < a->valence; k++ )
2541 : : {
2542 [ # # ]: 0 : if ( a->neighbor[k] != other_atom )
2543 : : {
2544 : 0 : continue;
2545 : : }
2546 : :
2547 : 0 : *bond_type = a->bond_type[k];
2548 : 0 : *bond_stereo = a->bond_stereo[k];
2549 : :
2550 : 0 : a->neighbor[k] = a->bond_type[k] = a->bond_stereo[k] = 0;
2551 : :
2552 [ # # ]: 0 : for ( kk = k + 1; kk < a->valence; kk++ )
2553 : : {
2554 : 0 : a->neighbor[kk - 1] = a->neighbor[kk];
2555 : 0 : a->bond_type[kk - 1] = a->bond_type[kk];
2556 : 0 : a->bond_stereo[kk - 1] = a->bond_stereo[kk];
2557 : : }
2558 [ # # ]: 0 : for ( kk = a->valence - 1; kk < MAXVAL; kk++ )
2559 : : {
2560 : 0 : a->neighbor[kk] = 0;
2561 : 0 : a->bond_type[kk] = (U_CHAR)0;
2562 : 0 : a->bond_stereo[kk] = (S_CHAR)0;
2563 : : }
2564 : 0 : return 1;
2565 : : } /* k */
2566 : : }
2567 : : }
2568 : :
2569 : 0 : return 0;
2570 : : }
2571 : :
2572 : :
2573 : : /****************************************************************************/
2574 : 0 : int OrigAtData_RemoveAtom(ORIG_ATOM_DATA* orig_at_data, int iatom)
2575 : : {
2576 : :
2577 : : if ( 0 )
2578 : : {
2579 : : return 1;
2580 : : }
2581 : :
2582 : 0 : return 0;
2583 : : }
2584 : :
2585 : :
2586 : : /****************************************************************************/
2587 : 0 : int OrigAtData_RemoveBond(int this_atom,
2588 : : int other_atom,
2589 : : inp_ATOM* at,
2590 : : int* bond_type,
2591 : : int* bond_stereo,
2592 : : int* num_inp_bonds)
2593 : : {
2594 : 0 : int del = 0;
2595 : :
2596 [ # # # # : 0 : if ( at && (this_atom >= 0) && (other_atom >= 0) ) /* djb-rwth: fixing oss-fuzz issue #68329, #68286 */
# # ]
2597 : : {
2598 : 0 : del = OrigAtData_RemoveHalfBond(this_atom, other_atom, at, bond_type, bond_stereo);
2599 : 0 : del += OrigAtData_RemoveHalfBond(other_atom, this_atom, at, bond_type, bond_stereo);
2600 : :
2601 [ # # ]: 0 : if ( del == 2 )
2602 : : {
2603 : 0 : (*num_inp_bonds)--;
2604 : 0 : at[this_atom].valence--;
2605 : 0 : at[this_atom].chem_bonds_valence -= *bond_type;
2606 : 0 : at[other_atom].valence--;
2607 : 0 : at[other_atom].chem_bonds_valence -= *bond_type;
2608 : 0 : return 1;
2609 : : }
2610 : : }
2611 : :
2612 : 0 : return 0;
2613 : : }
2614 : :
2615 : :
2616 : : /****************************************************************************/
2617 : 0 : int OrigAtData_AddBond(int this_atom,
2618 : : int other_atom,
2619 : : inp_ATOM* at,
2620 : : int bond_type,
2621 : : int bond_stereo,
2622 : : int* num_bonds)
2623 : : {
2624 [ # # ]: 0 : if ( at )
2625 : : {
2626 : : /* djb-rwth: fixing oss-fuzz issue #68286 */
2627 : : int i, k, already_here;
2628 : 0 : inp_ATOM* a = &(at[this_atom]);
2629 : :
2630 [ # # ]: 0 : if ( at[this_atom].valence >= MAXVAL ||
2631 [ # # ]: 0 : at[other_atom].valence >= MAXVAL )
2632 : : {
2633 : 0 : return 0;
2634 : : }
2635 : :
2636 [ # # # # ]: 0 : if ( bond_type != INCHI_BOND_TYPE_DOUBLE && bond_type != INCHI_BOND_TYPE_TRIPLE )
2637 : : {
2638 : 0 : bond_type = INCHI_BOND_TYPE_SINGLE;
2639 : : }
2640 : :
2641 : 0 : k = a->valence;
2642 : 0 : already_here = 0;
2643 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2644 : : {
2645 [ # # ]: 0 : if ( a->neighbor[i] == other_atom )
2646 : : {
2647 : 0 : already_here = 1; break;
2648 : : }
2649 : : }
2650 : :
2651 [ # # ]: 0 : if ( !already_here )
2652 : : {
2653 : 0 : a->neighbor[k] = other_atom;
2654 : 0 : a->bond_type[k] = (U_CHAR)bond_type;
2655 : 0 : a->bond_stereo[k] = (S_CHAR)bond_stereo;
2656 : 0 : a->chem_bonds_valence += bond_type;
2657 : 0 : a->valence++;
2658 : : }
2659 : :
2660 : 0 : a = &(at[other_atom]);
2661 : 0 : k = a->valence;
2662 : 0 : already_here = 0;
2663 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2664 : : {
2665 [ # # ]: 0 : if ( a->neighbor[i] == this_atom )
2666 : : {
2667 : 0 : already_here = 1; break;
2668 : : }
2669 : : }
2670 : :
2671 [ # # # # ]: 0 : if ( !already_here && (k < MAXVAL) ) /* djb-rwth: condition added to prevent buffer overrun */
2672 : : {
2673 : 0 : a->neighbor[k] = this_atom;
2674 : 0 : a->bond_type[k] = (U_CHAR)bond_type;
2675 : 0 : a->bond_stereo[k] = (S_CHAR)bond_stereo;
2676 : 0 : a->chem_bonds_valence += bond_type;
2677 : 0 : a->valence++;
2678 : : }
2679 : :
2680 : 0 : (*num_bonds)++;
2681 : :
2682 : 0 : return 1;
2683 : : }
2684 : : else
2685 : : {
2686 : 0 : return 0;
2687 : : }
2688 : : }
2689 : :
2690 : :
2691 : : /****************************************************************************/
2692 : 0 : int OrigAtData_AddSingleStereolessBond(int this_atom,
2693 : : int other_atom,
2694 : : inp_ATOM* at,
2695 : : int* num_bonds)
2696 : : {
2697 : 0 : return OrigAtData_AddBond(this_atom, other_atom, at, INCHI_BOND_TYPE_SINGLE, 0, num_bonds);
2698 : : }
2699 : :
2700 : :
2701 : : /****************************************************************************/
2702 : 0 : int OrigAtData_IncreaseBondOrder(int this_atom, int other_atom, inp_ATOM* at)
2703 : : {
2704 : 0 : int i, k, n_up = 0;
2705 : : inp_ATOM* a;
2706 : :
2707 [ # # ]: 0 : if ( at[this_atom].valence >= MAXVAL ||
2708 [ # # ]: 0 : at[other_atom].valence >= MAXVAL )
2709 : : {
2710 : 0 : return 0;
2711 : : }
2712 : :
2713 : 0 : a = &(at[this_atom]);
2714 [ # # ]: 0 : if ( a->chem_bonds_valence > MAXVAL - 1 )
2715 : : {
2716 : 0 : return 0;
2717 : : }
2718 : :
2719 : 0 : k = a->valence;
2720 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2721 : : {
2722 [ # # ]: 0 : if ( a->neighbor[i] != other_atom )
2723 : 0 : continue;
2724 [ # # ]: 0 : if ( a->bond_type[i] > 3 )
2725 : 0 : return 0;
2726 : 0 : a->bond_type[i]++;
2727 : 0 : a->chem_bonds_valence++;
2728 : 0 : n_up++;
2729 : 0 : break;
2730 : : }
2731 : :
2732 : 0 : a = &(at[other_atom]);
2733 [ # # ]: 0 : if ( a->chem_bonds_valence > MAXVAL - 1 )
2734 : : {
2735 : 0 : return 0;
2736 : : }
2737 : 0 : k = a->valence;
2738 : :
2739 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2740 : : {
2741 [ # # ]: 0 : if ( a->neighbor[i] != this_atom )
2742 : : {
2743 : 0 : continue;
2744 : : }
2745 [ # # ]: 0 : if ( a->bond_type[i] > 3 )
2746 : : {
2747 : 0 : return 0;
2748 : : }
2749 : 0 : a->bond_type[i]++;
2750 : 0 : a->chem_bonds_valence++;
2751 : 0 : n_up++;
2752 : 0 : break;
2753 : : }
2754 : :
2755 : 0 : return n_up;
2756 : : }
2757 : :
2758 : :
2759 : : /****************************************************************************/
2760 : 0 : int OrigAtData_DecreaseBondOrder(int this_atom,
2761 : : int other_atom,
2762 : : inp_ATOM* at)
2763 : : {
2764 : 0 : int i, k, n_dn = 0;
2765 : : inp_ATOM* a;
2766 : :
2767 : 0 : a = &(at[this_atom]);
2768 [ # # ]: 0 : if ( a->chem_bonds_valence > MAXVAL - 1 )
2769 : : {
2770 : 0 : return 0;
2771 : : }
2772 : :
2773 : 0 : k = a->valence;
2774 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2775 : : {
2776 [ # # ]: 0 : if ( a->neighbor[i] != other_atom )
2777 : : {
2778 : 0 : continue;
2779 : : }
2780 [ # # ]: 0 : if ( a->bond_type[i] < 2 )
2781 : : {
2782 : 0 : return 0;
2783 : : }
2784 : 0 : a->bond_type[i]--;
2785 : 0 : a->chem_bonds_valence--;
2786 : 0 : n_dn++;
2787 : 0 : break;
2788 : : }
2789 : :
2790 : 0 : a = &(at[other_atom]);
2791 : 0 : k = a->valence;
2792 [ # # ]: 0 : for ( i = 0; i < k; i++ )
2793 : : {
2794 [ # # ]: 0 : if ( a->neighbor[i] != this_atom )
2795 : : {
2796 : 0 : continue;
2797 : : }
2798 [ # # ]: 0 : if ( a->bond_type[i] < 2 )
2799 : : {
2800 : 0 : return 0;
2801 : : }
2802 : 0 : a->bond_type[i]--;
2803 : 0 : a->chem_bonds_valence--;
2804 : 0 : n_dn++;
2805 : 0 : break;
2806 : : }
2807 : :
2808 : 0 : return n_dn;
2809 : : }
2810 : :
2811 : :
2812 : : /****************************************************************************
2813 : : Collect bonds and optionally atoms of fragment
2814 : : ****************************************************************************/
2815 : 0 : void OAD_CollectFragmentBondsAndAtoms(ORIG_ATOM_DATA* orig_at_data,
2816 : : int nforbidden, /* number of edges forbidden for traversal */
2817 : : int* forbidden_orig, /* atom nums of forbidden edges */
2818 : : /* [edge1at1,edge1at2, edge2at1, edge2at2, ... ] */
2819 : : int* n_fragbonds,
2820 : : int** fragbonds,
2821 : : int* n_fragatoms,
2822 : : int* fragatoms,
2823 : : int* err,
2824 : : char* pStrErr)
2825 : : {
2826 : : int i;
2827 : 0 : int max_atoms = orig_at_data->num_inp_atoms;
2828 : 0 : int* atnums = NULL;
2829 : 0 : subgraf* sg = NULL;
2830 : 0 : subgraf_pathfinder* spf = NULL;
2831 : :
2832 : 0 : *err = 0;
2833 : 0 : atnums = (int*)inchi_calloc(max_atoms, sizeof(int));
2834 [ # # ]: 0 : if ( !atnums )
2835 : : {
2836 [ # # ]: 0 : TREAT_ERR(*err, 9045, "Not enough memory");
2837 : 0 : goto exit_function;
2838 : : }
2839 [ # # ]: 0 : for ( i = 0; i < max_atoms; i++ )
2840 : : {
2841 : 0 : atnums[i] = orig_at_data->at[i].orig_at_number; /* i+1 normally*/
2842 : : }
2843 : :
2844 : 0 : sg = subgraf_new(orig_at_data, max_atoms, atnums);
2845 [ # # ]: 0 : if ( !sg )
2846 : : {
2847 [ # # ]: 0 : TREAT_ERR(*err, 9045, "Not enough memory");
2848 : 0 : goto exit_function;
2849 : : }
2850 : 0 : spf = subgraf_pathfinder_new(sg, orig_at_data, 0, 0); /* start = end = 0th node */
2851 [ # # ]: 0 : if ( !spf )
2852 : : {
2853 [ # # ]: 0 : TREAT_ERR(*err, 9045, "Not enough memory");
2854 : 0 : goto exit_function;
2855 : : }
2856 : :
2857 : 0 : spf->seen[0] = spf->start;
2858 : 0 : spf->nseen = 1;
2859 : 0 : *n_fragbonds = 0;
2860 : 0 : *n_fragatoms = 0;
2861 : :
2862 : 0 : subgraf_pathfinder_run(spf,
2863 : : nforbidden, forbidden_orig, /* this corrects cinnectivity of subgraf... */
2864 : : n_fragbonds, fragbonds,
2865 : : n_fragatoms, fragatoms);
2866 : :
2867 : :
2868 : 0 : exit_function:
2869 : 0 : subgraf_free(sg);
2870 : 0 : subgraf_pathfinder_free(spf);
2871 [ # # ]: 0 : if ( atnums )
2872 : : {
2873 [ # # ]: 0 : inchi_free(atnums);
2874 : : }
2875 : 0 : return;
2876 : : }
2877 : :
2878 : :
2879 : : /****************************************************************************
2880 : : For all CRUs detect the bonds potentially involved in frame shift
2881 : : ****************************************************************************/
2882 : 0 : void OAD_Polymer_FindBackbones(ORIG_ATOM_DATA* at_data,
2883 : : COMP_ATOM_DATA* composite_norm_data,
2884 : : int* err,
2885 : : char* pStrErr)
2886 : : {
2887 : : int i;
2888 : :
2889 : 0 : *err = 0;
2890 [ # # ]: 0 : for ( i = 0; i < at_data->polymer->n; i++ )
2891 : : {
2892 [ # # ]: 0 : if ( !at_data->polymer->units[i]->cyclizable )
2893 : : {
2894 : 0 : continue;
2895 : : }
2896 : :
2897 : 0 : OAD_CollectBackboneBonds(at_data,
2898 : 0 : at_data->polymer->units[i]->na,
2899 : 0 : at_data->polymer->units[i]->alist,
2900 : 0 : at_data->polymer->units[i]->end_atom1,
2901 : 0 : at_data->polymer->units[i]->end_atom2,
2902 : 0 : &(at_data->polymer->units[i]->nbkbonds),
2903 : 0 : at_data->polymer->units[i]->bkbonds,
2904 : : err, pStrErr);
2905 [ # # ]: 0 : if ( *err )
2906 : : {
2907 : 0 : at_data->polymer->units[i]->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
2908 : 0 : continue;
2909 : : }
2910 [ # # ]: 0 : if ( at_data->polymer->units[i]->nbkbonds < 1 )
2911 : : {
2912 : 0 : continue;
2913 : : }
2914 [ # # ]: 0 : if ( at_data->polymer->units[i]->nbkbonds == 1 )
2915 : : {
2916 : : /* Special case: we got only one bond between end_atom1 and */
2917 : : /* end_atom2 (this may be the result of metal disconnection) */
2918 : 0 : continue;
2919 : : }
2920 : :
2921 : 0 : OAD_PolymerUnit_DelistIntraRingBackboneBonds(at_data->polymer->units[i], at_data, err, pStrErr);
2922 [ # # ]: 0 : if ( *err )
2923 : : {
2924 : 0 : continue;
2925 : : }
2926 : :
2927 : 0 : OAD_PolymerUnit_DelistHighOrderBackboneBonds(at_data->polymer->units[i],
2928 : : at_data, composite_norm_data,
2929 : : err, pStrErr);
2930 [ # # ]: 0 : if ( *err )
2931 : : {
2932 : 0 : continue;
2933 : : }
2934 [ # # ]: 0 : if ( at_data->polymer->units[i]->nbkbonds == 0 )
2935 : : {
2936 : : /* We already cyclized frame-shiftable unit and preprocessed it (in 'prep_inp_data'). */
2937 : : /* Despite that, now we discovered that there are no bonds eligible for frame shift */
2938 : : /* (as all potentially eligible in-unit bonds are either in-ring or alternate ones). */
2939 : : /* We can not simply restore original connections as the structure may have been already heavily touched. */
2940 : : /* The most viable action is to hold a single frame-shift bond (between original caps of CRU ends). */
2941 : : /* It is for sure will be converted to original bonds to star atoms on possible inchi2struct. */
2942 : 0 : at_data->polymer->units[i]->cyclizable = 1;
2943 : 0 : at_data->polymer->units[i]->nbkbonds = 1;
2944 : 0 : at_data->polymer->units[i]->bkbonds[0][0] = at_data->polymer->units[i]->end_atom1;
2945 : 0 : at_data->polymer->units[i]->bkbonds[0][1] = at_data->polymer->units[i]->end_atom2;
2946 : : }
2947 : : }
2948 : :
2949 : 0 : return;
2950 : : }
2951 : :
2952 : :
2953 : : /****************************************************************************
2954 : : Collect all backbone atoms - of main chain(s), side chains being ignored
2955 : : ****************************************************************************/
2956 : 0 : void OAD_CollectBackboneAtoms(ORIG_ATOM_DATA* at_data,
2957 : : int na,
2958 : : int* alist,
2959 : : int end_atom1,
2960 : : int end_atom2,
2961 : : int* nbkatoms,
2962 : : int* bkatoms,
2963 : : int* err,
2964 : : char* pStrErr)
2965 : : {
2966 : 0 : int start = 0, end = 0;
2967 : 0 : subgraf* sg = NULL;
2968 : 0 : subgraf_pathfinder* spf = NULL;
2969 : 0 : int nbkbonds = 0;
2970 : 0 : int** bkbonds = NULL; /* list of [breakable] backbone bonds [ (a1,a2), (a3,a4), ... ] */
2971 : : int maxbkbonds;
2972 : :
2973 : 0 : *nbkatoms = 0;
2974 : 0 : maxbkbonds = at_data->num_inp_bonds + 2;
2975 : 0 : *err = imat_new(maxbkbonds, 2, &(bkbonds));
2976 : : /* djb-rwth: addressing coverity ID #499570 -- TREAT_ERR properly used in all cases */
2977 [ # # ]: 0 : if (*err)
2978 : : {
2979 [ # # ]: 0 : TREAT_ERR(*err, 9034, "Not enough memory (polymers)");
2980 : 0 : goto exit_function;
2981 : : }
2982 : 0 : nbkbonds = 0;
2983 : 0 : sg = subgraf_new(at_data, na, alist);
2984 [ # # ]: 0 : if ( !sg )
2985 : : {
2986 [ # # ]: 0 : TREAT_ERR(*err, 9037, "Not enough memory (polymers)");
2987 : 0 : goto exit_function;
2988 : : }
2989 : :
2990 : 0 : start = sg->orig2node[end_atom1]; end = sg->orig2node[end_atom2];
2991 [ # # ]: 0 : if ( start > end )
2992 : : {
2993 : 0 : int tmp = end;
2994 : 0 : end = start;
2995 : 0 : start = tmp;
2996 : : }
2997 : 0 : spf = subgraf_pathfinder_new(sg, at_data, start, end);
2998 [ # # ]: 0 : if ( !spf )
2999 : : {
3000 [ # # ]: 0 : TREAT_ERR(*err, 9039, "Not enough memory (polymers)");
3001 : 0 : goto exit_function;
3002 : : }
3003 : :
3004 : 0 : spf->seen[0] = spf->start; spf->nseen = 1;
3005 : 0 : nbkbonds = 0;
3006 : 0 : *nbkatoms = 0;
3007 : :
3008 : 0 : subgraf_pathfinder_run(spf, 0, NULL, &nbkbonds, bkbonds, nbkatoms, bkatoms);
3009 : :
3010 : 0 : subgraf_free(sg);
3011 : 0 : subgraf_pathfinder_free(spf);
3012 : 0 : *err = 0;
3013 : :
3014 : :
3015 : 0 : exit_function:
3016 [ # # ]: 0 : if ( bkbonds )
3017 : : {
3018 : 0 : imat_free(maxbkbonds, bkbonds);
3019 : 0 : bkbonds = NULL;
3020 : : }
3021 : :
3022 : 0 : return;
3023 : : }
3024 : :
3025 : :
3026 : : /****************************************************************************
3027 : : Collect all atoms reachable from start_atom
3028 : : ****************************************************************************/
3029 : 0 : int OAD_CollectReachableAtoms(ORIG_ATOM_DATA* orig_at_data,
3030 : : int start_atom,
3031 : : int nforbidden_bonds,
3032 : : int* forbidden_bond_atoms,
3033 : : int* n_reachable,
3034 : : int* reachable,
3035 : : int* err,
3036 : : char* pStrErr)
3037 : : {
3038 : 0 : int iatom, natnums, max_atoms, j, ret = _IS_OKAY;
3039 : 0 : int max_n_reachable = *n_reachable;
3040 : 0 : int* atnums = NULL;
3041 : 0 : subgraf* sg = NULL;
3042 : 0 : subgraf_pathfinder* spf = NULL;
3043 : :
3044 : : /* djb-rwth: removing redundant code */
3045 : 0 : max_atoms = orig_at_data->num_inp_atoms;
3046 : 0 : iatom = start_atom - 1;
3047 : 0 : *n_reachable = 0;
3048 : :
3049 : 0 : atnums = (int*)inchi_calloc(max_atoms, sizeof(int));
3050 [ # # ]: 0 : if ( !atnums )
3051 : : {
3052 : 0 : ret = _IS_ERROR;
3053 : 0 : goto exit_function;
3054 : : }
3055 [ # # ]: 0 : for ( j = 0; j < max_atoms; j++ )
3056 : : {
3057 : 0 : atnums[j] = orig_at_data->at[j].orig_at_number;
3058 : : }
3059 : 0 : sg = subgraf_new(orig_at_data, max_atoms, atnums);
3060 [ # # ]: 0 : if ( !sg )
3061 : : {
3062 : 0 : ret = _IS_ERROR;
3063 : 0 : goto exit_function;
3064 : : }
3065 : 0 : spf = subgraf_pathfinder_new(sg, orig_at_data, iatom, iatom);
3066 [ # # ]: 0 : if ( !spf )
3067 : : {
3068 : 0 : ret = _IS_ERROR;
3069 : 0 : goto exit_function;
3070 : : }
3071 : :
3072 : : /* move from orig# to node# */
3073 : 0 : spf->start = iatom;
3074 [ # # ]: 0 : for ( j = 0; j < nforbidden_bonds; j++ )
3075 : : {
3076 : 0 : forbidden_bond_atoms[2 * j] = sg->orig2node[forbidden_bond_atoms[2 * j]];
3077 : 0 : forbidden_bond_atoms[2 * j + 1] = sg->orig2node[forbidden_bond_atoms[2 * j + 1]];
3078 : : }
3079 : :
3080 : : /*memset(atnums, -1, max_atoms * sizeof(int));*/
3081 [ # # ]: 0 : for ( j = 0; j < max_atoms; j++ )
3082 : : {
3083 : 0 : atnums[j] = -1;
3084 : : }
3085 : :
3086 : 0 : spf->nseen = 0;
3087 : 0 : natnums = subgraf_pathfinder_collect_all(spf, nforbidden_bonds, forbidden_bond_atoms, atnums);
3088 : :
3089 [ # # ]: 0 : if ( natnums )
3090 : : {
3091 [ # # ]: 0 : if ( natnums > max_n_reachable )
3092 : : {
3093 : 0 : ret = _IS_ERROR;
3094 : 0 : goto exit_function;
3095 : : }
3096 : :
3097 [ # # # # ]: 0 : for (j = 0; j < natnums && j < max_atoms; j++) /* djb-rwth: fixing buffer overruns */
3098 : : {
3099 : 0 : reachable[(*n_reachable)++ ] = atnums[j];
3100 : : }
3101 : : }
3102 : :
3103 : 0 : exit_function:
3104 : 0 : subgraf_free(sg);
3105 : 0 : subgraf_pathfinder_free(spf);
3106 [ # # ]: 0 : if ( atnums )
3107 : : {
3108 [ # # ]: 0 : inchi_free(atnums);
3109 : : }
3110 : :
3111 : 0 : return ret;
3112 : : }
3113 : :
3114 : :
3115 : : /****************************************************************************
3116 : : Collect all backbone bonds - of main chain(s), side chains being ignored
3117 : : (for polymer CRU, these are the bonds potentially involved in frame shift)
3118 : : ****************************************************************************/
3119 : 0 : void OAD_CollectBackboneBonds(ORIG_ATOM_DATA* at_data,
3120 : : int na,
3121 : : int* alist,
3122 : : int end_atom1,
3123 : : int end_atom2,
3124 : : int* nbkbonds,
3125 : : int** bkbonds,
3126 : : int* err,
3127 : : char* pStrErr)
3128 : : {
3129 : 0 : int start = 0, end = 0, dummy;
3130 : 0 : subgraf* sg = NULL;
3131 : 0 : subgraf_pathfinder* spf = NULL;
3132 : : /* Establish subgraph for na atoms of the alist */
3133 : 0 : *nbkbonds = 0;
3134 : 0 : sg = subgraf_new(at_data, na, alist);
3135 [ # # ]: 0 : if ( !sg )
3136 : : {
3137 [ # # ]: 0 : TREAT_ERR(*err, 9037, "Not enough memory (polymers)");
3138 : : /* unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE; */
3139 : 0 : return;
3140 : : }
3141 : 0 : start = sg->orig2node[end_atom1];
3142 : 0 : end = sg->orig2node[end_atom2];
3143 : : #if 0
3144 : : if ( start > end )
3145 : : {
3146 : : int tmp = end;
3147 : : end = start;
3148 : : start = tmp;
3149 : : }
3150 : : #endif
3151 : 0 : spf = subgraf_pathfinder_new(sg, at_data, start, end);
3152 [ # # ]: 0 : if ( !spf )
3153 : : {
3154 [ # # ]: 0 : TREAT_ERR( *err, 9039, "Not enough memory (polymers)" ); /* djb-rwth: addressing coverity ID #499539 -- TREAT_ERR properly used */
3155 : : /*unit->cyclizable = CLOSING_SRU_NOT_APPLICABLE;*/
3156 : 0 : return;
3157 : : }
3158 : 0 : spf->seen[0] = spf->start;
3159 : 0 : spf->nseen = 1;
3160 : 0 : *nbkbonds = 0;
3161 : 0 : subgraf_pathfinder_run(spf, 0, NULL,
3162 : : nbkbonds,
3163 : : bkbonds, /* we will collect backbone CRU bonds here */
3164 : : &dummy,
3165 : : NULL);
3166 : :
3167 : 0 : subgraf_free(sg);
3168 : 0 : subgraf_pathfinder_free(spf);
3169 : 0 : *err = 0;
3170 : :
3171 : 0 : return;
3172 : : }
3173 : :
3174 : :
3175 : : /****************************************************************************
3176 : : Remove intra-ring bonds from the list of frame-shiftable bonds
3177 : : ****************************************************************************/
3178 : 0 : void OAD_PolymerUnit_DelistIntraRingBackboneBonds(OAD_PolymerUnit* unit,
3179 : : ORIG_ATOM_DATA* at_data,
3180 : : int* err,
3181 : : char* pStrErr)
3182 : : {
3183 : 0 : int nrings = 0;
3184 : 0 : int* num_ring_sys = NULL;
3185 : :
3186 [ # # ]: 0 : if ( !unit )
3187 : : {
3188 : 0 : return;
3189 : : }
3190 [ # # ]: 0 : if ( unit->nbkbonds < 1 )
3191 : : {
3192 : 0 : return;
3193 : : }
3194 : :
3195 : : /* Establish ring systems assignments for all related atoms */
3196 : :
3197 : 0 : *err = 1;
3198 : 0 : num_ring_sys = (int*)inchi_calloc((long long)at_data->num_inp_atoms + 1, sizeof(int)); /* djb-rwth: cast operator added */
3199 [ # # ]: 0 : if ( !num_ring_sys )
3200 : : {
3201 : 0 : goto exit_function;
3202 : : }
3203 : :
3204 : 0 : *err = 0;
3205 : :
3206 : 0 : nrings = OAD_Polymer_FindRingSystems(at_data->polymer, at_data->at, at_data->num_inp_atoms, &at_data->num_inp_bonds,
3207 : 0 : num_ring_sys, NULL, unit->end_atom1 - 1); /* NB: start dfs within connected compt! */
3208 : :
3209 [ # # ]: 0 : if ( nrings == 0 )
3210 : : {
3211 : 0 : goto exit_function;
3212 : : }
3213 : : else
3214 : : {
3215 : 0 : int at1, at2, j = 0;
3216 : 0 : repeatj:
3217 : 0 : at1 = unit->bkbonds[j][0];
3218 : 0 : at2 = unit->bkbonds[j][1];
3219 [ # # # # ]: 0 : if ( (num_ring_sys[at1] == num_ring_sys[at2]) && (num_ring_sys[at1] != -1) )
3220 : : {
3221 : 0 : OAD_PolymerUnit_RemoveLinkFromCRUChain(at1, at2, &unit->nbkbonds, unit->bkbonds);
3222 : : }
3223 : : else
3224 : : {
3225 : 0 : ++j;
3226 : : }
3227 [ # # ]: 0 : if ( j < unit->nbkbonds )
3228 : : {
3229 : 0 : goto repeatj;
3230 : : }
3231 : : }
3232 : :
3233 : 0 : exit_function:
3234 [ # # ]: 0 : if ( num_ring_sys )
3235 : : {
3236 [ # # ]: 0 : inchi_free(num_ring_sys);
3237 : : }
3238 : :
3239 : 0 : return;
3240 : : }
3241 : :
3242 : :
3243 : : /****************************************************************************
3244 : : Find ring systems (exclude possible cyclizing bonds) in all polymer SRU's
3245 : : ****************************************************************************/
3246 : 0 : int OAD_Polymer_FindRingSystems(OAD_Polymer* pd,
3247 : : inp_ATOM* at,
3248 : : int nat,
3249 : : int* num_inp_bonds,
3250 : : int* num_ring_sys,
3251 : : int* size_ring_sys,
3252 : : int start)
3253 : : {
3254 : 0 : int i, j, nrings = 0, bond_type, bond_stereo;
3255 : :
3256 [ # # ]: 0 : if ( NULL == num_ring_sys )
3257 : : {
3258 : 0 : return 0;
3259 : : }
3260 : :
3261 : : /* Remove polymer SRU 'cyclizing' bonds if any */
3262 [ # # ]: 0 : for ( j = 0; j < pd->n; j++ )
3263 : : {
3264 [ # # ]: 0 : if ( pd->units[j]->cyclized )
3265 : : {
3266 : 0 : OrigAtData_RemoveBond(pd->units[j]->end_atom1 - 1,
3267 : 0 : pd->units[j]->end_atom2 - 1,
3268 : : at, &bond_type, &bond_stereo,
3269 : : num_inp_bonds);
3270 : : }
3271 : : }
3272 : :
3273 : 0 : MarkRingSystemsInp(at, nat, start); /*0 );*/
3274 : :
3275 [ # # ]: 0 : for ( i = 0; i <= nat; i++ )
3276 : : {
3277 : 0 : num_ring_sys[i] = -1;
3278 : : }
3279 [ # # ]: 0 : for ( i = 0; i < nat; i++ )
3280 : : {
3281 [ # # ]: 0 : if ( at[i].nNumAtInRingSystem > 2 )
3282 : : {
3283 : 0 : int atnum = at[i].orig_at_number;
3284 : 0 : num_ring_sys[atnum] = at[i].nRingSystem;
3285 [ # # ]: 0 : if ( NULL != size_ring_sys )
3286 : : {
3287 : 0 : size_ring_sys[atnum] = at[i].nNumAtInRingSystem;
3288 : : }
3289 : : }
3290 : : }
3291 : :
3292 : 0 : UnMarkRingSystemsInp(at, nat);
3293 : :
3294 [ # # ]: 0 : for ( i = 0; i < nat; i++ )
3295 : : {
3296 [ # # ]: 0 : if ( num_ring_sys[i] > -1 )
3297 : : {
3298 : 0 : nrings++;
3299 : : }
3300 : : }
3301 : :
3302 : : /* Restore polymer SRU 'cyclizing' bonds if applicable */
3303 [ # # ]: 0 : for ( j = 0; j < pd->n; j++ )
3304 : : {
3305 [ # # ]: 0 : if ( pd->units[j]->cyclized )
3306 : : {
3307 : 0 : OrigAtData_AddSingleStereolessBond(pd->units[j]->end_atom1 - 1,
3308 : 0 : pd->units[j]->end_atom2 - 1,
3309 : : at,
3310 : : num_inp_bonds);
3311 : : }
3312 : : }
3313 : :
3314 : 0 : return nrings;
3315 : : }
3316 : :
3317 : :
3318 : : /****************************************************************************
3319 : : Fill atomic properties (OrigAtData ) necessary
3320 : : to calc seniority in polymer SRUs
3321 : : ****************************************************************************/
3322 : 0 : void OAD_Polymer_SetAtProps(OAD_Polymer* pd,
3323 : : inp_ATOM* at,
3324 : : int nat,
3325 : : int* num_inp_bonds,
3326 : : OAD_AtProps* aprops,
3327 : : int* cano_nums)
3328 : : {
3329 : : /* Max rank for in-ring atom is 216 which is achieved for N (element number 7 in Periodic system & erank_rule2[] ),*/
3330 : : /* then goes O with rank 215 (element number 8), and so on... lowest rank is 1 for H . */
3331 : : /* */
3332 : : /* This follows to IUPAC rule 2 [Pure Appl. Chem., Vol. 74, No. 10, 2002, p. 1926] which states: */
3333 : : /* a. a ring or ring system containing nitrogen; */
3334 : : /* b. a ring or ring system containing the heteroatom occurring earliest in the order given in Rule 4; */
3335 : : /* ( which is O > S > Se > Te > N > P > As > Sb > Bi > Si > Ge > Sn > Pb > B > Hg ) */
3336 : : /* ... */
3337 : :
3338 : 0 : 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,
3339 : : 175,174,173,172,171,170,169,205,209,213,165,164,163,162,161,160,159,158,157,156,155,154,153,152,
3340 : : 151,204,208,212,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,
3341 : : 127,126,125,124,123,122,121,201,119,203,207,116,115,114,113,112,111,110,109,108,107,106,105,104,
3342 : : 103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81 };
3343 : :
3344 : :
3345 : :
3346 : : /* Max rank for chain atom is 215 which is achieved for O (element number 8 in Periodic system & erank_rule4[] ), */
3347 : : /* then goes N with rank 212 (element number 8), and so on... lowest rank is 1 for H . */
3348 : : /* */
3349 : : /* This follows to IUPAC rule 4 [Pure Appl. Chem., Vol. 74, No. 10, 2002, p. 1927] which states: */
3350 : : /* O > S > Se > Te > N > P > As > Sb > Bi > Si > Ge > Sn > Pb > B > Hg */
3351 : : /* Note: Other heteroatoms may be placed within this order as indicated by their positions in the */
3352 : : /* periodic table [5]. */
3353 : :
3354 : 0 : 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,
3355 : : 175,174,173,172,171,170,169,205,209,213,165,164,163,162,161,160,159,158,157,156,155,154,153,152,
3356 : : 151,204,208,212,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,
3357 : : 127,126,125,124,123,122,121,201,119,203,207,116,115,114,113,112,111,110,109,108,107,106,105,104,
3358 : : 103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81 };
3359 : :
3360 : :
3361 : 0 : int i, j, k, nrings = 0;
3362 : 0 : int a1, a2, dummy = 0, bond_type = -1, bond_stereo = -1;
3363 : 0 : int* num_ring_sys = NULL, * size_ring_sys = NULL;
3364 : : /* djb-rwth: fixing oss-fuzz issue #68112 */
3365 : 0 : int err2_len = sizeof(erank_rule2) / sizeof(erank_rule2[0]);
3366 : 0 : int err4_len = sizeof(erank_rule4) / sizeof(erank_rule4[0]);
3367 : :
3368 [ # # # # : 0 : if ( (NULL == aprops) || !at || !pd ) /* djb-rwth: fixing oss-fuzz issue #68329, #68286 */
# # ]
3369 : : {
3370 : 0 : return;
3371 : : }
3372 : :
3373 : : /* Establish element ranks for atoms */
3374 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
3375 : : {
3376 : 0 : int atnum = at[k].orig_at_number, index = k;
3377 : 0 : U_CHAR err4_ind = at[k].el_number;
3378 [ # # ]: 0 : if ( cano_nums )
3379 : : {
3380 : 0 : index = cano_nums[atnum];
3381 : : }
3382 [ # # # # ]: 0 : if ( index >= 0 && err4_ind < err4_len )
3383 : : {
3384 : 0 : aprops[index].erank = erank_rule4[err4_ind];
3385 : 0 : aprops[index].ring_erank = 0;
3386 : 0 : aprops[index].ring_size = 0;
3387 : 0 : aprops[index].ring_num = -1;
3388 : : }
3389 : : else
3390 : : {
3391 : : /* deleted H's go here */
3392 : : ;
3393 : : }
3394 : :
3395 : : }
3396 : :
3397 : : /* Establish ring systems assignments for atoms */
3398 : 0 : num_ring_sys = (int*)inchi_calloc((long long)nat + 1, sizeof(int)); /* djb-rwth: cast operator added */
3399 [ # # ]: 0 : if ( NULL == num_ring_sys )
3400 : : {
3401 : 0 : goto exit_function;
3402 : : }
3403 : 0 : size_ring_sys = (int*)inchi_calloc((long long)nat + 1, sizeof(int)); /* djb-rwth: cast operator added */
3404 [ # # ]: 0 : if ( NULL == size_ring_sys )
3405 : : {
3406 : 0 : goto exit_function;
3407 : : }
3408 : :
3409 : : /* Note that we get here on the way of InChI2Struct conversion. */
3410 : : /* Break temporarily any of (actually, the first) SRU 'cyclizing' bonds */
3411 [ # # ]: 0 : for ( j = 0; j < pd->n; j++ )
3412 : : {
3413 [ # # # # ]: 0 : if ( pd->units[j]->na > 2 && pd->units[j]->nbkbonds > 0 &&
3414 [ # # ]: 0 : pd->units[j]->cyclized == 0 &&
3415 [ # # ]: 0 : pd->units[j]->cyclizable == CLOSING_SRU_RING )
3416 : : {
3417 : 0 : a1 = pd->units[j]->bkbonds[0][0] - 1;
3418 : 0 : a2 = pd->units[j]->bkbonds[0][1] - 1;
3419 : 0 : OrigAtData_RemoveBond(a1, a2, at, &bond_type, &bond_stereo, &dummy);
3420 : : }
3421 : : }
3422 : :
3423 : 0 : nrings = OAD_Polymer_FindRingSystems(pd, at, nat, num_inp_bonds, num_ring_sys, size_ring_sys, 0);
3424 : :
3425 : : /* Immediately restore just broken bond(s) */
3426 [ # # ]: 0 : for ( j = 0; j < pd->n; j++ )
3427 : : {
3428 [ # # ]: 0 : if ( pd->units[j]->na > 2 &&
3429 [ # # ]: 0 : pd->units[j]->nbkbonds > 0 &&
3430 [ # # ]: 0 : pd->units[j]->cyclized == 0 &&
3431 [ # # ]: 0 : pd->units[j]->cyclizable == CLOSING_SRU_RING )
3432 : : {
3433 : 0 : a1 = pd->units[j]->bkbonds[0][0] - 1;
3434 : 0 : a2 = pd->units[j]->bkbonds[0][1] - 1;
3435 : : /* OrigAtData_AddSingleStereolessBond( a1, a2, at, &dummy ); */
3436 : 0 : OrigAtData_AddBond(a1, a2, at, bond_type, bond_stereo, &dummy);
3437 : : }
3438 : : }
3439 : :
3440 [ # # ]: 0 : if ( nrings )
3441 : : {
3442 : 0 : int max_ring_num = 0;
3443 : : /* SRU contains ring[s], proceed with them following (not totally) the IUPAC guidelines */
3444 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
3445 : : {
3446 : : /* Browse 0-based original atoms, go to 1-based cano nums domain if cano_nums mapping is suppied */
3447 : 0 : int atnum = at[k].orig_at_number, index = k;
3448 [ # # ]: 0 : if ( cano_nums )
3449 : : {
3450 : 0 : index = cano_nums[atnum] + 1;
3451 : : }
3452 [ # # ]: 0 : if ( num_ring_sys[atnum] >= 0 )
3453 : : {
3454 : 0 : aprops[index].ring_num = num_ring_sys[atnum]; /* temporarily */
3455 : :
3456 [ # # ]: 0 : if ( max_ring_num < aprops[index].ring_num )
3457 : 0 : max_ring_num = aprops[index].ring_num; /* NB: OAD_Polymer_FindRingSystems may return num_ring_sys[] */
3458 : : /* which is not a list of consecutive numbers */
3459 : :
3460 : 0 : aprops[index].ring_size = size_ring_sys[atnum]; /* Size of ring system which includes the atom k . */
3461 : : /* It is used as an additional score for in-ring */
3462 : : /* atoms' prioritizing (instead of criteria in */
3463 : : /* 2c-2h of IUPAC rule 2 which deal with ring sizes). */
3464 : : }
3465 : : }
3466 : :
3467 [ # # ]: 0 : for ( i = 0; i <= max_ring_num; i++ )
3468 : : {
3469 : 0 : int erank, max_erank = 0;
3470 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
3471 : : {
3472 : 0 : int atnum = at[k].orig_at_number, index = k;
3473 [ # # ]: 0 : if ( cano_nums )
3474 : : {
3475 : 0 : index = cano_nums[atnum] + 1;
3476 : : }
3477 [ # # ]: 0 : if ( aprops[index].ring_num == i )
3478 : : {
3479 : 0 : erank = erank_rule2[at[k].el_number];
3480 [ # # ]: 0 : if ( erank > max_erank )
3481 : 0 : max_erank = erank;
3482 : : }
3483 : : }
3484 [ # # ]: 0 : for ( k = 0; k < nat; k++ )
3485 : : {
3486 : 0 : int atnum = at[k].orig_at_number, index = k;
3487 [ # # ]: 0 : if ( cano_nums )
3488 : : {
3489 : 0 : index = cano_nums[atnum] + 1;
3490 : : }
3491 [ # # ]: 0 : if ( aprops[index].ring_num == i )
3492 : : {
3493 [ # # ]: 0 : if ( aprops[index].ring_size > 2 )
3494 : : {
3495 : 0 : aprops[index].ring_erank = max_erank;
3496 : : }
3497 : : }
3498 : : }
3499 : : }
3500 : : }
3501 : :
3502 : 0 : exit_function:
3503 [ # # ]: 0 : if ( num_ring_sys )
3504 : : {
3505 [ # # ]: 0 : inchi_free(num_ring_sys);
3506 : : }
3507 [ # # ]: 0 : if ( size_ring_sys )
3508 : : {
3509 [ # # ]: 0 : inchi_free(size_ring_sys);
3510 : : }
3511 : :
3512 : 0 : return;
3513 : : }
3514 : :
3515 : :
3516 : : /****************************************************************************
3517 : : Exclude higher order bonds from list of bkbonds
3518 : : ****************************************************************************/
3519 : 0 : void OAD_PolymerUnit_DelistHighOrderBackboneBonds(OAD_PolymerUnit* unit,
3520 : : ORIG_ATOM_DATA* orig_at_data,
3521 : : COMP_ATOM_DATA* composite_norm_data,
3522 : : int* err,
3523 : : char* pStrErr)
3524 : : {
3525 : 0 : int at1, at2, j = 0, k, check_taut = 0, remove; /* djb-rwth: removing redundant variables/code */
3526 : :
3527 : 0 : int* orig_num = NULL, * curr_num = NULL;
3528 : 0 : int bond_is_untouchable = 0, btype; /* DT: moved from below 2024-09-01 DT */
3529 : :
3530 [ # # ]: 0 : if ( unit->na < 2 )
3531 : : {
3532 : 0 : return;
3533 : : }
3534 [ # # ]: 0 : if ( unit->nb < 2 )
3535 : : {
3536 : 0 : return;
3537 : : }
3538 [ # # ]: 0 : if ( unit->nbkbonds < 1 )
3539 : : {
3540 : 0 : return;
3541 : : }
3542 : :
3543 : : /* Care on tautomeric bonds */
3544 [ # # ]: 0 : if ( composite_norm_data )
3545 : : {
3546 : 0 : check_taut = 1;
3547 : 0 : orig_num = (int*)inchi_calloc((long long)orig_at_data->num_inp_atoms + 2, sizeof(int)); /* djb-rwth: cast operator added */
3548 : 0 : curr_num = (int*)inchi_calloc((long long)orig_at_data->num_inp_atoms + 2, sizeof(int)); /* djb-rwth: cast operator added */
3549 [ # # # # ]: 0 : if ( orig_num && curr_num )
3550 : : {
3551 : 0 : check_taut = 1;
3552 : 0 : CompAtomData_GetNumMapping(composite_norm_data, orig_num, curr_num);
3553 : : }
3554 : : }
3555 : 0 : repeatj:
3556 : 0 : remove = 0;
3557 : 0 : at1 = unit->bkbonds[j][0];
3558 : 0 : at2 = unit->bkbonds[j][1];
3559 : : /* djb-rwth: removing redundant code */
3560 [ # # ]: 0 : for ( k = 0; k < orig_at_data->at[at1 - 1].valence; k++ )
3561 : : {
3562 [ # # ]: 0 : if ( orig_at_data->at[at1 - 1].neighbor[k] != at2 - 1 )
3563 : 0 : continue;
3564 : : /* djb-rwth: removing redundant code */
3565 : : }
3566 : : /*if ( border > 1 ) */
3567 : : /* djb-rwth: removing redundant code */
3568 : 0 : bond_is_untouchable = 0;
3569 [ # # # # : 0 : if ( check_taut && composite_norm_data && composite_norm_data->at && curr_num ) /* djb-rwth: fixing a NULL pointer dereference */
# # # # ]
3570 : : {
3571 [ # # ]: 0 : for ( k = 0; k < composite_norm_data->at[curr_num[at1]].valence; k++ )
3572 : : {
3573 [ # # ]: 0 : if ( composite_norm_data->at[curr_num[at1]].neighbor[k] != curr_num[at2] )
3574 : : {
3575 : 0 : continue;
3576 : : }
3577 : 0 : btype = composite_norm_data->at[curr_num[at1]].bond_type[k];
3578 : 0 : bond_is_untouchable = (btype == BOND_TAUTOM); /*|| btype == BOND_ALTERN );*/
3579 : 0 : break;
3580 : : }
3581 : : }
3582 [ # # ]: 0 : if ( bond_is_untouchable )
3583 : : {
3584 : 0 : remove = 1;
3585 : : }
3586 : :
3587 [ # # ]: 0 : if ( remove )
3588 : : {
3589 : 0 : OAD_PolymerUnit_RemoveLinkFromCRUChain(at1, at2, &unit->nbkbonds, unit->bkbonds);
3590 : : }
3591 : : else
3592 : : {
3593 : 0 : ++j;
3594 : : }
3595 : :
3596 [ # # ]: 0 : if ( j < unit->nbkbonds )
3597 : : {
3598 : 0 : goto repeatj;
3599 : : }
3600 : :
3601 [ # # ]: 0 : if ( orig_num )
3602 : : {
3603 [ # # ]: 0 : inchi_free(orig_num);
3604 : : }
3605 [ # # ]: 0 : if ( curr_num )
3606 : : {
3607 [ # # ]: 0 : inchi_free(curr_num);
3608 : : }
3609 : :
3610 : 0 : return;
3611 : : }
3612 : :
3613 : :
3614 : : /****************************************************************************
3615 : : Remove bond (at1, at2)
3616 : : ****************************************************************************/
3617 : 0 : void OAD_PolymerUnit_RemoveLinkFromCRUChain(int at1,
3618 : : int at2,
3619 : : int* nbonds,
3620 : : int** bonds)
3621 : : {
3622 : : int p, q;
3623 : : #if 0
3624 : : if ( at1 > at2 )
3625 : : {
3626 : : int tmp = at1;
3627 : : at1 = at2;
3628 : : at2 = tmp;
3629 : : }
3630 : : #endif
3631 [ # # ]: 0 : for ( p = 0; p < *nbonds; p++ )
3632 : : {
3633 [ # # # # ]: 0 : if ( bonds[p][0] == at1 && bonds[p][1] == at2 )
3634 : : {
3635 [ # # ]: 0 : for ( q = p + 1; q < *nbonds; q++ )
3636 : : {
3637 : 0 : bonds[q - 1][0] = bonds[q][0];
3638 : 0 : bonds[q - 1][1] = bonds[q][1];
3639 : : }
3640 : 0 : (*nbonds)--;
3641 : 0 : break;
3642 : : }
3643 : : }
3644 : :
3645 : 0 : return;
3646 : : }
3647 : :
3648 : :
3649 : : /****************************************************************************
3650 : : Debug print polymer data for a given SRU
3651 : : ****************************************************************************/
3652 : 0 : void OAD_PolymerUnit_DebugTrace(OAD_PolymerUnit* u)
3653 : : {
3654 : 0 : char* conn = "ABSENT", * typ = "ABSENT", * styp = "ABSENT";
3655 : :
3656 [ # # ]: 0 : if ( !u )
3657 : : {
3658 : 0 : return;
3659 : : }
3660 : :
3661 [ # # ]: 0 : if ( u->conn == 1 )
3662 : : {
3663 : 0 : conn = "HT"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3664 : : }
3665 [ # # ]: 0 : else if ( u->conn == 2 )
3666 : : {
3667 : 0 : conn = "HH"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3668 : : }
3669 [ # # ]: 0 : else if ( u->conn == 3 )
3670 : : {
3671 : 0 : conn = "EU"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3672 : : }
3673 : :
3674 [ # # ]: 0 : if ( u->type == 0 )
3675 : : {
3676 : 0 : typ = "NONE"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3677 : : }
3678 [ # # ]: 0 : else if ( u->type == 1 )
3679 : : {
3680 : 0 : typ = "SRU"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3681 : : }
3682 [ # # ]: 0 : else if ( u->type == 2 )
3683 : : {
3684 : 0 : typ = "MON"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3685 : : }
3686 [ # # ]: 0 : else if ( u->type == 3 )
3687 : : {
3688 : 0 : typ = "COP"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3689 : : }
3690 [ # # ]: 0 : else if ( u->type == 4 )
3691 : : {
3692 : 0 : typ = "MOD"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3693 : : }
3694 [ # # ]: 0 : else if ( u->type == 5 )
3695 : : {
3696 : 0 : typ = "MER"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3697 : : }
3698 : :
3699 [ # # ]: 0 : if ( u->subtype == 1 )
3700 : : {
3701 : 0 : styp = "ALT"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3702 : : }
3703 [ # # ]: 0 : else if ( u->subtype == 2 )
3704 : : {
3705 : 0 : styp = "RAN"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3706 : : }
3707 [ # # ]: 0 : else if ( u->subtype == 3 )
3708 : : {
3709 : 0 : styp = "BLK"; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
3710 : : }
3711 : :
3712 : : {
3713 : : int i, k;
3714 : : int na, nb;
3715 : :
3716 : : ITRACE_("\n\nPOLYMER UNIT @ %-p", u);
3717 : :
3718 : : ITRACE_("\n\tid=%-d label=%-d type=%-s subtype=%-s conn=%-s subscr='%-s'\n",
3719 : : u->id, u->label, typ, styp, conn, u->smt);
3720 : :
3721 : : ITRACE_("\tBracket1 coords: %-f, %-f, %-f, %-f\n", u->xbr1[0], u->xbr1[1], u->xbr1[2], u->xbr1[3]);
3722 : : ITRACE_("\tBracket2 coords: %-f, %-f, %-f, %-f\n", u->xbr2[0], u->xbr2[1], u->xbr2[2], u->xbr2[3]);
3723 : :
3724 : 0 : na = u->na;
3725 : : ITRACE_("\t%-d atoms { ", na);
3726 [ # # ]: 0 : for ( k = 0; k < na - 1; k++ )
3727 : : {
3728 : : ITRACE_(" %-d, ", u->alist[k]);
3729 : : }
3730 : : ITRACE_(" %-d }\n", u->alist[na - 1]);
3731 : :
3732 : 0 : nb = u->nb;
3733 : : ITRACE_("\t%-d bonds crossing unit borders { ", nb);
3734 : :
3735 [ # # ]: 0 : for ( k = 0; k < nb; k++ )
3736 : : {
3737 : : ITRACE_(" %-d-%-d ", u->blist[2 * k], u->blist[2 * k + 1]);
3738 : : }
3739 : : ITRACE_("}\n");
3740 : :
3741 : : ITRACE_("\tCRU caps and end atoms { ");
3742 : :
3743 : : ITRACE_("*%-d-[-%-d(end1) ... ", u->cap1, u->end_atom1);
3744 : : ITRACE_("%-d(end2)-]-*%-d", u->end_atom2, u->cap2);
3745 : : ITRACE_(" }\n");
3746 : :
3747 : : ITRACE_("\tBackbone bonds (may include 'artificially cyclizing' one) : %-d bonds ", u->nbkbonds);
3748 [ # # ]: 0 : if ( u->nbkbonds )
3749 : : {
3750 : : ITRACE_(" { ");
3751 [ # # ]: 0 : for ( i = 0; i < u->nbkbonds; i++ )
3752 : : {
3753 : : ITRACE_("(%-d, %-d) ", u->bkbonds[i][0], u->bkbonds[i][1]);
3754 : : }
3755 : : ITRACE_("}\n");
3756 : : }
3757 : : }
3758 : :
3759 : :
3760 : 0 : return;
3761 : : }
3762 : :
3763 : :
3764 : : /****************************************************************************
3765 : : Debug print the whole polymer data
3766 : : ****************************************************************************/
3767 : 0 : void OAD_Polymer_DebugTrace(OAD_Polymer* p)
3768 : : {
3769 : : int i;
3770 : :
3771 [ # # ]: 0 : if ( !p )
3772 : : {
3773 : 0 : return;
3774 : : }
3775 : :
3776 : : ITRACE_("\n\n* POLYMER INFO @ %-p (%-d group(s))", p, p->n);
3777 : : ITRACE_("\n\n* %-d star atoms: ", p->n_pzz);
3778 [ # # ]: 0 : for ( i = 0; i < p->n_pzz; i++ )
3779 : : {
3780 : : ITRACE_(" %-d", p->pzz[i]);
3781 : : }
3782 : :
3783 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
3784 : : {
3785 : : ITRACE_("\n* Polymer unit %-d", i);
3786 : 0 : OAD_PolymerUnit_DebugTrace(p->units[i]);
3787 : : }
3788 : : ITRACE_("\n* Really-do-PS = %-d", p->really_do_frame_shift);
3789 : : ITRACE_("\n* Frame_shift_scheme = %-d", p->frame_shift_scheme);
3790 : : ITRACE_("\n* Edit-repeats = %-d", p->edit_repeats);
3791 : : ITRACE_("\n* End POLYMER INFO\n");
3792 : 0 : return;
3793 : :
3794 : : }
3795 : :
3796 : :
3797 : : /****************************************************************************/
3798 : 0 : int OAD_Polymer_GetRepresentation(OAD_Polymer* p)
3799 : : {
3800 : 0 : int i, n_source_based_units = 0, n_structure_based_units = 0;
3801 : :
3802 [ # # ]: 0 : if ( !p )
3803 : : {
3804 : 0 : return NO_POLYMER;
3805 : : }
3806 : :
3807 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
3808 : : {
3809 [ # # # # ]: 0 : if ( p->units[i]->nb == 2 || p->units[i]->nbkbonds > 0 ||
3810 [ # # # # ]: 0 : ((p->units[i]->cap1 > 0) && (p->units[i]->cap2 > 0)) )
3811 : : {
3812 : 0 : p->units[i]->representation = POLYMER_REPRESENTATION_STRUCTURE_BASED;
3813 : 0 : n_structure_based_units++;
3814 : : }
3815 [ # # ]: 0 : else if ( p->units[i]->nb == 0 )
3816 : : {
3817 : 0 : p->units[i]->representation = POLYMER_REPRESENTATION_SOURCE_BASED;
3818 : 0 : n_source_based_units++;
3819 : : }
3820 : : }
3821 [ # # ]: 0 : if ( p->n == n_source_based_units )
3822 : : {
3823 : 0 : return POLYMER_REPRESENTATION_SOURCE_BASED;
3824 : : }
3825 [ # # ]: 0 : else if ( p->n == n_structure_based_units )
3826 : : {
3827 : 0 : return POLYMER_REPRESENTATION_STRUCTURE_BASED;
3828 : : }
3829 [ # # # # ]: 0 : else if ( n_source_based_units &&
3830 : 0 : n_structure_based_units &&
3831 [ # # ]: 0 : (n_source_based_units + n_structure_based_units) == p->n )
3832 : : {
3833 : : /* TODO: check if SRU/MON are embedded in a single COP (is this check really necessary? ??) */
3834 : 0 : return POLYMER_REPRESENTATION_MIXED;
3835 : : }
3836 : : #if 0
3837 : : else if ( p->n == (n_source_based_units + n_structure_based_units) )
3838 : : {
3839 : : /*
3840 : : Structure based presentation may include no-crossing bond units
3841 : : which only serve as embedding for ( >1 ) structure-based SRU's.
3842 : : The code below accounts for this.
3843 : : */
3844 : : if ( n_source_based_units < n_structure_based_units )
3845 : : {
3846 : : int j, atom, atom_is_shared_with_struct_based_unit = 0;
3847 : : for ( i = 0; i < p->n; i++ )
3848 : : {
3849 : : int k;
3850 : : if ( p->units[i]->representation != POLYMER_REPRESENTATION_SOURCE_BASED )
3851 : : continue;
3852 : : for ( k = 0; k < p->units[i]->na; k++ )
3853 : : {
3854 : : atom = p->units[i]->alist[k];
3855 : : if ( is_in_the_ilist(p->pzz, atom, p->n_pzz) )
3856 : : continue;
3857 : : atom_is_shared_with_struct_based_unit = 0;
3858 : : for ( j = 0; j < p->n; j++ )
3859 : : {
3860 : : if ( p->units[j]->representation != POLYMER_REPRESENTATION_STRUCTURE_BASED )
3861 : : continue;
3862 : : if ( is_in_the_ilist(p->units[j]->alist, atom, p->units[j]->na) )
3863 : : {
3864 : : atom_is_shared_with_struct_based_unit = 1;
3865 : : break;
3866 : : }
3867 : : }
3868 : : if ( !atom_is_shared_with_struct_based_unit )
3869 : : break;
3870 : : }
3871 : : if ( !atom_is_shared_with_struct_based_unit )
3872 : : break;
3873 : : }
3874 : : if ( atom_is_shared_with_struct_based_unit )
3875 : : return POLYMER_REPRESENTATION_STRUCTURE_BASED;
3876 : : }
3877 : : return POLYMER_REPRESENTATION_MIXED;
3878 : : }
3879 : : #endif
3880 : :
3881 : 0 : return POLYMER_REPRESENTATION_UNRECOGNIZED;
3882 : : }
3883 : :
3884 : :
3885 : : /****************************************************************************
3886 : : Open pre-cyclized CRUs appropriately (i.e., make frame shift)
3887 : : ****************************************************************************/
3888 : 0 : void OAD_Polymer_SmartReopenCyclizedUnits(OAD_Polymer* p,
3889 : : inp_ATOM* at,
3890 : : int nat,
3891 : : int* num_inp_bonds)
3892 : : {
3893 : : int i;
3894 : : /* djb-rwth: fixing oss-fuzz issue #68329 */
3895 : 0 : OAD_AtProps* aprops = (OAD_AtProps*)inchi_calloc((long long)nat + 1, sizeof(OAD_AtProps)); /* djb-rwth: cast operator added */
3896 : : /* nat + 1: add extra element for possibe 1-based indexing */
3897 : :
3898 [ # # ]: 0 : if ( !p )
3899 : : {
3900 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3901 : 0 : return;
3902 : : }
3903 [ # # ]: 0 : if ( p->n < 1 )
3904 : : {
3905 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3906 : 0 : return;
3907 : : }
3908 [ # # ]: 0 : if ( !p->really_do_frame_shift )
3909 : : {
3910 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3911 : 0 : return;
3912 : : }
3913 : : /* djb-rwth: fixing oss-fuzz issue #68329 */
3914 [ # # ]: 0 : if ( nat <= 0 )
3915 : : {
3916 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3917 : 0 : return;
3918 : : }
3919 : :
3920 : : /*ITRACE_( "\n\n*********************************************************************\n* ENTERING OAD_Polymer_SmartReopenCyclizedUnits()" );
3921 : : OAD_Polymer_DebugTrace( p );*/
3922 : :
3923 : : /* Set atom properties for sorting */
3924 [ # # # # ]: 0 : if ( !aprops || !at ) /* djb-rwth: fixing oss-fuzz issue #68329, #68286 */
3925 : : {
3926 [ # # ]: 0 : inchi_free(aprops); /* djb-rwth: avoiding memory leak */
3927 : 0 : return;
3928 : : }
3929 : 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*/
3930 [ # # ]: 0 : for ( i = 0; i < p->n; i++ )
3931 : : {
3932 [ # # ]: 0 : if ( p->units[i] ) /* djb-rwth: fixing oss-fuzz issue #68329 */
3933 : : {
3934 : 0 : OAD_PolymerUnit* u = p->units[i];
3935 [ # # ]: 0 : if ( p->frame_shift_scheme == FSS_NONE )
3936 : : {
3937 : 0 : continue;
3938 : : }
3939 : 0 : if ( /* !u->cyclizable || u->cyclized || */
3940 [ # # ]: 0 : u->nbkbonds < 1 ||
3941 [ # # # # ]: 0 : u->cap1 < 1 || u->cap2 < 1 ||
3942 [ # # # # ]: 0 : u->cap1 > nat || u->cap2 > nat )
3943 : : {
3944 : 0 : continue;
3945 : : }
3946 [ # # ]: 0 : if ( OAD_PolymerUnit_SetReopeningDetails(u, at) )
3947 : : {
3948 : : int senior_bond;
3949 : 0 : OAD_PolymerUnit_SortBackboneBondsAndSetSeniors(u, at, aprops, &senior_bond);
3950 : : }
3951 : 0 : OAD_PolymerUnit_ReopenCyclized(u, at, aprops, nat, num_inp_bonds);
3952 : : }
3953 : : }
3954 : :
3955 : 0 : p->really_do_frame_shift = 0;
3956 [ # # ]: 0 : inchi_free(aprops);
3957 : :
3958 : 0 : return;
3959 : : }
3960 : :
3961 : :
3962 : : /****************************************************************************/
3963 : 0 : void OAD_PolymerUnit_ReopenCyclized(OAD_PolymerUnit* u,
3964 : : inp_ATOM* at,
3965 : : OAD_AtProps* aprops,
3966 : : int nat,
3967 : : int* num_inp_bonds)
3968 : : {
3969 : : int bond_type, bond_stereo;
3970 : :
3971 [ # # ]: 0 : if ( u->cyclizable == CLOSING_SRU_RING )
3972 : : {
3973 : : /* Decyclize artificially introduced bond */
3974 : 0 : OrigAtData_RemoveBond(u->end_atom1 - 1, u->end_atom2 - 1,
3975 : : at, &bond_type, &bond_stereo, num_inp_bonds);
3976 : : }
3977 [ # # ]: 0 : else if ( u->cyclizable == CLOSING_SRU_HIGHER_ORDER_BOND )
3978 : : {
3979 : 0 : OrigAtData_DecreaseBondOrder(u->end_atom1 - 1, u->end_atom2 - 1, at);
3980 : : }
3981 [ # # ]: 0 : else if ( u->cyclizable == CLOSING_SRU_DIRADICAL )
3982 : : {
3983 [ # # ]: 0 : if ( at[u->end_atom1 - 1].radical == RADICAL_TRIPLET )
3984 : : {
3985 : 0 : at[u->end_atom1 - 1].radical = 0;
3986 : : }
3987 : : }
3988 : :
3989 : : /* Add explicitly connections to star atoms */
3990 : 0 : OrigAtData_AddSingleStereolessBond(u->cap1 - 1, u->end_atom1 - 1,
3991 : : at, num_inp_bonds);
3992 : 0 : OrigAtData_AddSingleStereolessBond(u->cap2 - 1, u->end_atom2 - 1,
3993 : : at, num_inp_bonds);
3994 : :
3995 : : /* Create crossing bonds */
3996 : 0 : u->nb = 2;
3997 : 0 : u->nbkbonds = 0;
3998 [ # # ]: 0 : if ( !u->blist )
3999 : : {
4000 : 0 : u->blist = (int*)inchi_calloc(2 * (long long)u->nb, sizeof(int)); /* djb-rwth: cast operator added */
4001 : : }
4002 [ # # ]: 0 : if ( !u->blist )
4003 : : {
4004 : 0 : return;
4005 : : }
4006 : :
4007 : 0 : u->blist[0] = u->cap1;
4008 : 0 : u->blist[1] = u->end_atom1;
4009 : 0 : u->blist[2] = u->cap2;
4010 : 0 : u->blist[3] = u->end_atom2;
4011 : :
4012 : 0 : return;
4013 : : }
4014 : :
4015 : :
4016 : : /****************************************************************************/
4017 : 0 : int OAD_PolymerUnit_SetReopeningDetails(OAD_PolymerUnit* u, inp_ATOM* at)
4018 : : {
4019 : : int k;
4020 : :
4021 : : /* Check reopening type */
4022 : :
4023 : : /* Caps are separated by one atom - that's not error but do nothing */
4024 [ # # ]: 0 : if ( u->nbkbonds == 0 )
4025 : : {
4026 : : ;
4027 : : }
4028 [ # # ]: 0 : else if ( u->nbkbonds == 1 )
4029 : : {
4030 : 0 : u->end_atom1 = u->bkbonds[0][0];
4031 : 0 : u->end_atom2 = u->bkbonds[0][1];
4032 : :
4033 [ # # ]: 0 : if ( u->end_atom1 == u->end_atom2 )
4034 : : {
4035 : : #ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL
4036 : 0 : u->cyclizable = CLOSING_SRU_DIRADICAL;
4037 : : #else
4038 : : u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;
4039 : : #endif
4040 : : }
4041 : : else
4042 : : {
4043 : : /* If caps are separated by two atoms - that's not error but do nothing */
4044 [ # # ]: 0 : for ( k = 0; k < at[u->end_atom1 - 1].valence; k++ )
4045 : : {
4046 [ # # ]: 0 : if ( at[u->end_atom1 - 1].neighbor[k] == u->end_atom2 - 1 )
4047 : : {
4048 [ # # ]: 0 : if ( at[u->end_atom1 - 1].bond_type[k] > 1 )
4049 : : #ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND
4050 : 0 : u->cyclizable = CLOSING_SRU_HIGHER_ORDER_BOND;
4051 : : #else
4052 : : /* u->cyclizable = CLOSING_SRU_NOT_APPLICABLE;*/
4053 : : #endif
4054 : 0 : break;
4055 : : }
4056 : : }
4057 : : }
4058 : : } /* */
4059 : :
4060 : 0 : return u->nbkbonds;
4061 : : }
4062 : :
4063 : :
4064 : :
4065 : : /****************************************************************************/
4066 : 0 : void OAD_PolymerUnit_SortBackboneBondsAndSetSeniors(OAD_PolymerUnit* u,
4067 : : inp_ATOM* at,
4068 : : OAD_AtProps* aprops,
4069 : : int* senior_bond)
4070 : : {
4071 : 0 : int j, * bnum = NULL;
4072 : :
4073 : 0 : *senior_bond = 0;
4074 : :
4075 : : /* Sort backbone (== frame shiftable) bonds if necessary */
4076 [ # # ]: 0 : if ( u->nbkbonds > 1 )
4077 : : {
4078 : 0 : bnum = (int*)inchi_calloc(u->nbkbonds, sizeof(int));
4079 [ # # ]: 0 : if ( bnum )
4080 : : {
4081 [ # # ]: 0 : for ( j = 0; j < u->nbkbonds; j++ )
4082 : : {
4083 : 0 : bnum[j] = j;
4084 : : }
4085 : 0 : OAD_PolymerUnit_SortBackboneBonds(u, aprops, bnum);
4086 : 0 : *senior_bond = bnum[0];
4087 [ # # ]: 0 : inchi_free(bnum);
4088 : : }
4089 : : }
4090 : :
4091 : : /* v. 1.05+ : place senior atom the first ("left") in the senior bond */
4092 [ # # ]: 0 : if ( OAD_Polymer_IsFirstAtomRankLower(u->bkbonds[*senior_bond][0], u->bkbonds[*senior_bond][1], aprops) == 1 )
4093 : : {
4094 : 0 : int tmp = u->bkbonds[*senior_bond][0];
4095 : 0 : u->bkbonds[*senior_bond][0] = u->bkbonds[*senior_bond][1];
4096 : 0 : u->bkbonds[*senior_bond][1] = tmp;
4097 : : }
4098 : :
4099 : 0 : u->end_atom1 = u->bkbonds[*senior_bond][0];
4100 : 0 : u->end_atom2 = u->bkbonds[*senior_bond][1];
4101 : :
4102 : 0 : return;
4103 : : }
4104 : :
4105 : :
4106 : : /****************************************************************************/
4107 : 0 : void OAD_PolymerUnit_SortBackboneBonds(OAD_PolymerUnit* u,
4108 : : OAD_AtProps* aprops,
4109 : : int* bnum)
4110 : : {
4111 : : int i, j, tmp;
4112 : 0 : int n = u->nbkbonds;
4113 [ # # ]: 0 : if ( NULL == bnum )
4114 : : {
4115 : 0 : return;
4116 : : }
4117 [ # # ]: 0 : for ( i = 1; i < n; i++ )
4118 : : {
4119 : 0 : tmp = bnum[i];
4120 : 0 : j = i - 1;
4121 [ # # # # ]: 0 : while ( j >= 0 && OAD_Polymer_CompareBackboneBondsSeniority(u->bkbonds[bnum[j]], u->bkbonds[tmp], aprops) > 0 )
4122 : : {
4123 : 0 : bnum[j + 1] = bnum[j];
4124 : 0 : j--;
4125 : : }
4126 : 0 : bnum[j + 1] = tmp;
4127 : : }
4128 : :
4129 : 0 : return;
4130 : : }
4131 : :
4132 : :
4133 : : /****************************************************************************
4134 : : For sorting SRU cyclizing bonds (PS=='frame-shift') in descending order
4135 : : In general: favor greater max-rank end
4136 : : if max ends are the same, favor lesser min-rank end
4137 : : ****************************************************************************/
4138 : 0 : int OAD_Polymer_CompareBackboneBondsSeniority(int* b1, int* b2, OAD_AtProps* aprops)
4139 : : {
4140 : 0 : int b1min, b1max, b2min, b2max, tmp, cmp = 0;
4141 : :
4142 : : /* Find min and max ext-ranked ends of the both bonds */
4143 : 0 : b1max = b1[0]; b1min = b1[1];
4144 : 0 : b2max = b2[0]; b2min = b2[1];
4145 [ # # ]: 0 : if ( OAD_Polymer_IsFirstAtomRankLower(b1min, b1max, aprops) == -1 )
4146 : : {
4147 : 0 : tmp = b1max;
4148 : 0 : b1max = b1min;
4149 : 0 : b1min = tmp;
4150 : : }
4151 [ # # ]: 0 : if ( OAD_Polymer_IsFirstAtomRankLower(b2min, b2max, aprops) == -1 )
4152 : : {
4153 : 0 : tmp = b2max;
4154 : 0 : b2max = b2min;
4155 : 0 : b2min = tmp;
4156 : : }
4157 : :
4158 : : /* Compare bonds' seniority */
4159 : :
4160 : : /* First, favor the bond which has greater ext-rank end
4161 : : NB: the result may be 0, that is, equal max ext. ranks */
4162 : :
4163 : 0 : cmp = OAD_Polymer_CompareRanksOfTwoAtoms(b1max, b2max, aprops);
4164 [ # # ]: 0 : if ( cmp == 1 )
4165 : : {
4166 : 0 : return 1; /* rank(b1max) < rank(b2max), so bond2 is senior */
4167 : : }
4168 [ # # ]: 0 : else if ( cmp == -1 )
4169 : : {
4170 : 0 : return -1; /* rank(b1max) > rank(b2max), so bond1 is senior */
4171 : : }
4172 : :
4173 : : /* Max ends are of the same rank, so favor the bond with lesser min-rank end
4174 : : NB: the result may NOT be 0, that is, the case is always resolved */
4175 : :
4176 : 0 : cmp = OAD_Polymer_CompareRanksOfTwoAtoms(b1min, b2min, aprops); /*OAD_Polymer_IsFirstAtomRankLower( b1min, b2min, aprops );*/
4177 : :
4178 [ # # ]: 0 : if ( cmp == 1 )
4179 : : {
4180 : 0 : return -1; /* rank(b1min) < rank(b2min), so bond1 is senior */
4181 : : }
4182 [ # # ]: 0 : else if ( cmp == -1 )
4183 : : {
4184 : 0 : return 1; /* rank(b1min) > rank(b2min), so bond2 is senior */
4185 : : }
4186 : :
4187 : : /* Min ends are of the same rank. Here is the time to compare directly
4188 : : which canonical number is larger of max-ends ... */
4189 : :
4190 [ # # ]: 0 : if ( b1max < b2max )
4191 : : {
4192 : 0 : return 1;
4193 : : }
4194 [ # # ]: 0 : if ( b1max > b2max )
4195 : : {
4196 : 0 : return -1;
4197 : : }
4198 : :
4199 : : /* ... they are the same, so compare which canonical number is larger for min-ends ... */
4200 : :
4201 [ # # ]: 0 : if ( b1min < b2min )
4202 : : {
4203 : 0 : return -1; /* b1min < b2min, so bond1 is senior */
4204 : : }
4205 [ # # ]: 0 : if ( b1min > b2min )
4206 : : {
4207 : 0 : return 1; /* b1min > b2min, so bond2 is senior */
4208 : : }
4209 : :
4210 : 0 : return 0; /* we should not reach there */
4211 : : }
4212 : :
4213 : :
4214 : : /****************************************************************************
4215 : : Compare seniority of two atoms in polymer SRU
4216 : : loosely following IUPAC guidelines.
4217 : : NB: no last resort check here, so 0 (=='same seniority') may be returned
4218 : : ****************************************************************************/
4219 : 0 : int OAD_Polymer_CompareRanksOfTwoAtoms(int atom1, int atom2, OAD_AtProps* aprops)
4220 : : {
4221 : 0 : const int HETEROCYC = 3, HETEROAT = 2, CARBOCYC = 1, CARBOAT = 0;
4222 : : /* NB: Carbon's rank is always 2, next to the lowest */
4223 : :
4224 : 0 : int a1 = atom1 - 1;
4225 : 0 : int a2 = atom2 - 1;
4226 : 0 : int a1typ = CARBOAT;
4227 : 0 : int a2typ = CARBOAT;
4228 : :
4229 : : /* djb-rwth: fixing oss-fuzz issue #69501, #68277 */
4230 [ # # # # ]: 0 : if ( (a1 < 0) || (a2 < 0) )
4231 : : {
4232 : 0 : return 0;
4233 : : }
4234 : :
4235 [ # # ]: 0 : if ( aprops[a1].ring_size > 2 )
4236 : : {
4237 [ # # ]: 0 : if ( aprops[a1].ring_erank <= 2 )
4238 : : {
4239 : 0 : a1typ = CARBOCYC;
4240 : : }
4241 : : else
4242 : : {
4243 : 0 : a1typ = HETEROCYC;
4244 : : }
4245 : : }
4246 : : else
4247 : : {
4248 [ # # ]: 0 : if ( aprops[a1].erank == 2 )
4249 : : {
4250 : 0 : a1typ = CARBOAT;
4251 : : }
4252 : : else
4253 : : {
4254 : 0 : a1typ = HETEROAT;
4255 : : }
4256 : : }
4257 : :
4258 [ # # ]: 0 : if ( aprops[a2].ring_size > 2 )
4259 : : {
4260 [ # # ]: 0 : if ( aprops[a2].ring_erank <= 2 )
4261 : : {
4262 : 0 : a2typ = CARBOCYC;
4263 : : }
4264 : : else
4265 : : {
4266 : 0 : a2typ = HETEROCYC;
4267 : : }
4268 : : }
4269 : : else
4270 : : {
4271 [ # # ]: 0 : if ( aprops[a2].erank == 2 )
4272 : : {
4273 : 0 : a2typ = CARBOAT;
4274 : : }
4275 : : else
4276 : : {
4277 : 0 : a2typ = HETEROAT;
4278 : : }
4279 : : }
4280 : :
4281 : : /* Compare */
4282 : :
4283 : : /*
4284 : : Follow IUPAC Rule 1
4285 : : 'The basic order of seniority of subunits is:
4286 : : heterocyclic rings and ring systems > heteroatom chains >
4287 : : > carbocyclic rings and ring systems > acyclic carbon chains'
4288 : : */
4289 : :
4290 [ # # # # ]: 0 : if ( a1typ == HETEROCYC && a2typ == HETEROCYC ) /* a1 and a2 are HETEROCYC */
4291 : : {
4292 : : /* Try resolving by senior-heteroatom ring */
4293 [ # # ]: 0 : if ( aprops[a1].ring_erank < aprops[a2].ring_erank )
4294 : : {
4295 : 0 : return 1;
4296 : : }
4297 [ # # ]: 0 : if ( aprops[a1].ring_erank > aprops[a2].ring_erank )
4298 : : {
4299 : 0 : return -1;
4300 : : }
4301 : : /* Same senior-heteroatom rings, try resolving by total ring size */
4302 [ # # ]: 0 : if ( aprops[a1].ring_size < aprops[a2].ring_size )
4303 : : {
4304 : 0 : return 1;
4305 : : }
4306 [ # # ]: 0 : if ( aprops[a1].ring_size > aprops[a2].ring_size )
4307 : : {
4308 : 0 : return -1;
4309 : : }
4310 : : /* Could not resolve... */
4311 : 0 : return 0;
4312 : : }
4313 [ # # ]: 0 : else if ( a1typ == HETEROCYC )
4314 : : {
4315 : 0 : return -1; /* a1 is HETEROCYC, a2 is any other (==junior) */
4316 : : }
4317 [ # # ]: 0 : else if ( a2typ == HETEROCYC )
4318 : : {
4319 : 0 : return 1; /* a2 is HETEROCYC, a1 is any other (==junior) */
4320 : : }
4321 : :
4322 : : /* HETEROCYC left out */
4323 : :
4324 [ # # # # ]: 0 : if ( a1typ == HETEROAT && a2typ == HETEROAT ) /* a1 and a2 are HETEROAT */
4325 : : {
4326 [ # # ]: 0 : if ( aprops[a1].erank < aprops[a2].erank )
4327 : : {
4328 : 0 : return 1;
4329 : : }
4330 [ # # ]: 0 : if ( aprops[a1].erank > aprops[a2].erank )
4331 : : {
4332 : 0 : return -1;
4333 : : }
4334 : : /* Could not resolve... */
4335 : 0 : return 0;
4336 : : }
4337 [ # # ]: 0 : else if ( a1typ == HETEROAT )
4338 : : {
4339 : 0 : return -1; /* a1 is HETEROAT, a2 is any other (==junior) */
4340 : : }
4341 [ # # ]: 0 : else if ( a2typ == HETEROAT )
4342 : : {
4343 : 0 : return 1; /* a2 is HETEROAT, a1 is any other (==junior) */
4344 : : }
4345 : :
4346 : : /* HETEROAT left out */
4347 [ # # # # ]: 0 : if ( a1typ == CARBOCYC && a2typ == CARBOCYC ) /* a1 and a2 are CARBOCYC */
4348 : : {
4349 : : /* Same senior-atom (C) ring, try resolving by total ring size */
4350 [ # # ]: 0 : if ( aprops[a1].ring_size < aprops[a2].ring_size )
4351 : : {
4352 : 0 : return 1;
4353 : : }
4354 [ # # ]: 0 : if ( aprops[a1].ring_size > aprops[a2].ring_size )
4355 : : {
4356 : 0 : return -1;
4357 : : }
4358 : : /* Could not resolve... */
4359 : 0 : return 0;
4360 : : }
4361 [ # # ]: 0 : else if ( a1typ == CARBOCYC )
4362 : : {
4363 : 0 : return -1;
4364 : : }
4365 [ # # ]: 0 : else if ( a2typ == CARBOCYC )
4366 : : {
4367 : 0 : return 1;
4368 : : }
4369 : :
4370 : 0 : return 0; /* 0 means unresolved. It is legal here */
4371 : : }
4372 : :
4373 : :
4374 : : /****************************************************************************
4375 : : Compare seniority of two atoms in polymer SRU
4376 : : loosely following IUPAC guidelines.
4377 : : Always return non-zero result, that is, resolve the case.
4378 : : ****************************************************************************/
4379 : 0 : int OAD_Polymer_IsFirstAtomRankLower(int atom1, int atom2, OAD_AtProps* aprops)
4380 : : {
4381 : : /* Compare ext-ranks */
4382 : 0 : int result = OAD_Polymer_CompareRanksOfTwoAtoms(atom1, atom2, aprops);
4383 : :
4384 [ # # ]: 0 : if ( result )
4385 : : {
4386 : 0 : return result;
4387 : : }
4388 : :
4389 : : /* Could not resolve who is junior by extended-ranks... */
4390 : : /* As a last resort, simply check which canonical number is lesser */
4391 [ # # ]: 0 : if ( atom1 < atom2 )
4392 : : {
4393 : 0 : return 1;
4394 : : }
4395 [ # # ]: 0 : if ( atom1 > atom2 )
4396 : : {
4397 : 0 : return -1;
4398 : : }
4399 : :
4400 : : /* should not reach there */
4401 : 0 : return 0;
4402 : : }
4403 : :
4404 : :
4405 : : /****************************************************************************/
4406 : 54 : void OAD_ValidateAndSortOutPseudoElementAtoms(ORIG_ATOM_DATA* orig_at_data,
4407 : : int treat_polymers,
4408 : : int bNPZz,
4409 : : int* err,
4410 : : char* pStrErr)
4411 : : {
4412 : 54 : int i, k, n_pseudo = 0;
4413 : 54 : int nsgroups = 0, nzz = 0;
4414 : 54 : int nat = orig_at_data->num_inp_atoms;
4415 : 54 : OAD_Polymer* pd = orig_at_data->polymer;
4416 : 54 : OAD_PolymerUnit* u = NULL;
4417 : :
4418 [ + + - + ]: 54 : int pseudos_allowed = (bNPZz == 1) || (treat_polymers != POLYMERS_NO);
4419 : :
4420 [ + + ]: 673 : for ( k = 0; k < nat; k++ )
4421 : : {
4422 : 619 : int is_zz = 0, is_star = 0, is_zy = 0;
4423 : :
4424 : :
4425 : : /* Though "Zy" is present in our internal Periodic Table,
4426 : : _input_ "Zy" atoms are prohibited.
4427 : : */
4428 [ - + ]: 619 : if ( !strcmp(orig_at_data->at[k].elname, "Zy") )
4429 : : {
4430 : 0 : is_zy = 1;
4431 : : #if 0
4432 : : Disabled 2020 - 04 - 07
4433 : : TREAT_ERR(*err, (70 + 5), "Invalid element(s):");
4434 : : TREAT_ERR(*err, (70 + 5), orig_at_data->at[k].elname);
4435 : : continue;
4436 : : #endif
4437 : : }
4438 : 619 : is_star = !strcmp(orig_at_data->at[k].elname, "*");
4439 [ + - ]: 619 : if ( !is_star )
4440 : : {
4441 : 619 : is_zz = !strcmp(orig_at_data->at[k].elname, "Zz");
4442 : : }
4443 : :
4444 [ + - + - : 619 : if ( is_star || is_zz || is_zy )
- + ]
4445 : : {
4446 : 0 : n_pseudo++;
4447 [ # # ]: 0 : if ( 0 == pseudos_allowed )
4448 : : {
4449 : : /* That's an error */
4450 [ # # ]: 0 : TREAT_ERR(*err, (70 + 5), "Invalid element(s):");
4451 [ # # ]: 0 : TREAT_ERR(*err, (70 + 5), orig_at_data->at[k].elname);
4452 : 0 : continue;
4453 : : }
4454 : :
4455 : : /* Now check if valid pseudoelement atom */
4456 : :
4457 : : /* Should be strictly univalent and single-bonded */
4458 : : /* Should not have isotopic enrichment */
4459 [ # # ]: 0 : if ( orig_at_data->at[k].valence > 1 ||
4460 [ # # ]: 0 : orig_at_data->at[k].chem_bonds_valence > 1 /* || orig_at_data->at[k].iso_atw_diff != 0 */
4461 : : )
4462 : : {
4463 [ # # ]: 0 : TREAT_ERR(*err, (70 + 7), "Invalid pseudo element(s) bonding");
4464 : : /*TREAT_ERR( *err, ( 70 + 7 ), orig_at_data->at[k].elname );*/
4465 : 0 : continue;
4466 : : }
4467 : :
4468 : :
4469 : : /* Now convert both "*" and "Zz" temporarily to "Zy" */
4470 : 0 : mystrncpy(orig_at_data->at[k].elname, "Zy", sizeof("Zy"));
4471 : : }
4472 : : }
4473 : :
4474 : 54 : orig_at_data->n_zy = 0;
4475 : 54 : nzz = 0;
4476 [ - + ]: 54 : if ( orig_at_data->valid_polymer )
4477 : : {
4478 : 0 : nsgroups = pd->n;
4479 : : /* If applicable, check each CRU and back-convert "Zy" to "Zz" (polymer-related pseudoelement atoms)
4480 : : if they are from valid paired CRU crossing bond out-of-bracket caps */
4481 [ # # ]: 0 : for ( i = 0; i < nsgroups; i++ )
4482 : : {
4483 : 0 : u = pd->units[i];
4484 [ # # ]: 0 : if ( u )
4485 : : {
4486 [ # # # # ]: 0 : if ( u->cap1_is_undef && u->cap2_is_undef )
4487 : : {
4488 : : /* valid pair: CRU is capped with two undefined-nature atoms, call them "Zz", finally */
4489 : 0 : strcpy(orig_at_data->at[u->cap1 - 1].elname, "Zz");
4490 : 0 : strcpy(orig_at_data->at[u->cap2 - 1].elname, "Zz");
4491 : 0 : nzz += 2;
4492 : : }
4493 : : }
4494 : : }
4495 : 0 : orig_at_data->polymer->n_pzz = nzz;
4496 : : }
4497 : :
4498 : 54 : orig_at_data->n_zy = n_pseudo - nzz;
4499 [ - + ]: 54 : if ( orig_at_data->n_zy )
4500 : : {
4501 : : /* Have non-polymer-related pseudoelement atoms */
4502 [ # # ]: 0 : if ( 0 == bNPZz )
4503 : : {
4504 [ # # ]: 0 : TREAT_ERR(*err, (70 + 4), "Polymer-unrelated pseudoatoms are not allowed");
4505 : : }
4506 : : }
4507 : :
4508 [ - + ]: 54 : if ( *err )
4509 : : {
4510 : 0 : orig_at_data->valid_polymer = 0;
4511 : : }
4512 : :
4513 : 54 : }
4514 : :
4515 : :
4516 : : /****************************************************************************/
4517 : 0 : int Inp_Atom_GetBondType(inp_ATOM* at, int iatom1, int iatom2)
4518 : : {
4519 : : int i;
4520 : :
4521 [ # # ]: 0 : for ( i = 0; i < at[iatom1].valence; i++ )
4522 : : {
4523 [ # # ]: 0 : if ( at[iatom1].neighbor[i] == iatom2 )
4524 : : {
4525 : 0 : return at[iatom1].bond_type[i];
4526 : : }
4527 : : }
4528 : :
4529 : 0 : return -1;
4530 : : }
|