Branch data Line data Source code
1 : : /*
2 : : * International Chemical Identifier (InChI)
3 : : * Version 1
4 : : * Software version 1.07
5 : : * April 30, 2024
6 : : *
7 : : * MIT License
8 : : *
9 : : * Copyright (c) 2024 IUPAC and InChI Trust
10 : : *
11 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
12 : : * of this software and associated documentation files (the "Software"), to deal
13 : : * in the Software without restriction, including without limitation the rights
14 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 : : * copies of the Software, and to permit persons to whom the Software is
16 : : * furnished to do so, subject to the following conditions:
17 : : *
18 : : * The above copyright notice and this permission notice shall be included in all
19 : : * copies or substantial portions of the Software.
20 : : *
21 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 : : * SOFTWARE.
28 : : *
29 : : * The InChI library and programs are free software developed under the
30 : : * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
31 : : * Originally developed at NIST.
32 : : * Modifications and additions by IUPAC and the InChI Trust.
33 : : * Some portions of code were developed/changed by external contributors
34 : : * (either contractor or volunteer) which are listed in the file
35 : : * 'External-contributors' included in this distribution.
36 : : *
37 : : * info@inchi-trust.org
38 : : *
39 : : */
40 : :
41 : : #include <stdio.h>
42 : : #include <stdlib.h>
43 : : #include <string.h>
44 : : #include <ctype.h>
45 : : #include <stdarg.h>
46 : : #include <errno.h>
47 : : #include <limits.h>
48 : : #include <float.h>
49 : : #include <ctype.h>
50 : : #include <locale.h>
51 : :
52 : : #include "mode.h"
53 : :
54 : : #if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 )
55 : : #include <malloc.h>
56 : : #include <io.h>
57 : : #endif
58 : :
59 : :
60 : : #ifdef _WIN32
61 : : #include <crtdbg.h>
62 : : #endif
63 : :
64 : : #include "ichimain.h"
65 : : #include "inchi_api.h"
66 : : #include "bcf_s.h"
67 : : #include "ichi_io.h"
68 : : #include "inpdef.h"
69 : : #include "permutation_util.h"
70 : :
71 : :
72 : 0 : int rrand(int m)
73 : : {
74 : : return
75 : 0 : (int)((double)m * (rand() / (RAND_MAX + 1.0)));
76 : : }
77 : :
78 : 0 : void shuffle(void* obj, size_t nmemb, size_t size)
79 : : {
80 : 0 : void* temp = inchi_malloc(size);
81 : 0 : size_t n = nmemb;
82 [ # # ]: 0 : while (n > 1)
83 : : {
84 : 0 : size_t k = rrand((int)n--);
85 [ # # ]: 0 : if (temp) /* djb-rwth: fixing a NULL pointer dereference */
86 : : {
87 : 0 : memcpy(temp, BYTE(obj) + n * size, size);
88 : 0 : memcpy(BYTE(obj) + n * size, BYTE(obj) + k * size, size);
89 : 0 : memcpy(BYTE(obj) + k * size, temp, size);
90 : : }
91 : : }
92 : : #ifdef _WIN32
93 : : _free_dbg(temp, _NORMAL_BLOCK); /* djb-rwth: _free_dbg for _malloc_dbg must be used if Windows SDK is used */
94 : : #else
95 : 0 : free(temp); /* djb-rwth: otherwise just free */
96 : : #endif
97 : 0 : }
98 : :
99 : :
100 : : /* Use after OrigAtData_Duplicate (permuted <-- saved) */
101 : 0 : void OrigAtData_Permute(ORIG_ATOM_DATA* permuted, ORIG_ATOM_DATA* saved, int* numbers)
102 : : {
103 : : int i, j, k;
104 : 0 : int nat = saved->num_inp_atoms;
105 : 0 : size_t atsize = sizeof(saved->at[0]);
106 [ # # ]: 0 : for (i = 0; i < nat; i++)
107 : : {
108 : 0 : j = numbers[i];
109 : 0 : memcpy(permuted->at + j, saved->at + i, atsize);
110 [ # # ]: 0 : for (k = 0; k < permuted->at[j].valence; k++)
111 : : {
112 : 0 : permuted->at[j].neighbor[k] = numbers[permuted->at[j].neighbor[k]];
113 : : }
114 : 0 : permuted->at[j].orig_at_number = 1 + numbers[permuted->at[j].orig_at_number - 1];
115 : : }
116 [ # # # # ]: 0 : if (saved->polymer && permuted->polymer)
117 : : {
118 [ # # ]: 0 : if (saved->polymer->pzz)
119 : : {
120 [ # # ]: 0 : for (k = 0; k < saved->polymer->n_pzz; k++)
121 : : {
122 : 0 : permuted->polymer->pzz[k] = numbers[permuted->polymer->pzz[k]];
123 : : }
124 : : }
125 [ # # ]: 0 : if (saved->polymer->units)
126 : : {
127 [ # # ]: 0 : for (k = 0; k < saved->polymer->n; k++)
128 : : {
129 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->cap1 - 1];
130 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->end_atom1 - 1];
131 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->cap2 - 1];
132 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->end_atom2 - 1];
133 [ # # ]: 0 : if (permuted->polymer->units[k]->alist)
134 : : {
135 [ # # ]: 0 : for (j = 0; j < permuted->polymer->units[k]->na; j++)
136 : : {
137 : 0 : permuted->polymer->units[k]->alist[j] = 1 + numbers[permuted->polymer->units[k]->alist[j] - 1];
138 : : }
139 [ # # ]: 0 : for (j = 0; j < permuted->polymer->units[k]->nb; j++)
140 : : {
141 : 0 : permuted->polymer->units[k]->blist[2 * j] = 1 + numbers[permuted->polymer->units[k]->blist[2 * j] - 1];
142 : 0 : permuted->polymer->units[k]->blist[2 * j + 1] = 1 + numbers[permuted->polymer->units[k]->blist[2 * j + 1] - 1];
143 : : }
144 : : }
145 : : }
146 : : }
147 : : }
148 [ # # # # ]: 0 : if (saved->v3000 && permuted->v3000)
149 : : {
150 [ # # # # ]: 0 : if (saved->v3000->atom_index_orig && permuted->v3000->atom_index_orig)
151 : : {
152 [ # # ]: 0 : for (k = 0; k < nat; k++)
153 : : {
154 : 0 : permuted->v3000->atom_index_orig[k] = numbers[permuted->v3000->atom_index_orig[k]];
155 : : }
156 : : }
157 [ # # # # ]: 0 : if (saved->v3000->atom_index_fin && permuted->v3000->atom_index_fin)
158 : : {
159 [ # # ]: 0 : for (k = 0; k < nat; k++)
160 : : {
161 : 0 : permuted->v3000->atom_index_fin[k] = numbers[permuted->v3000->atom_index_fin[k]];
162 : : }
163 : : }
164 [ # # # # : 0 : if (saved->v3000->n_haptic_bonds && saved->v3000->lists_haptic_bonds && permuted->v3000->n_haptic_bonds && permuted->v3000->lists_haptic_bonds)
# # # # ]
165 : : {
166 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_haptic_bonds; j++)
167 : : {
168 : 0 : permuted->v3000->lists_haptic_bonds[j][1] = numbers[permuted->v3000->lists_haptic_bonds[j][1]];
169 [ # # ]: 0 : for (k = 3; k < saved->v3000->lists_haptic_bonds[j][2]; k++)
170 : : {
171 : 0 : permuted->v3000->lists_haptic_bonds[j][k] = numbers[permuted->v3000->lists_haptic_bonds[j][k]];
172 : : }
173 : : }
174 : : }
175 [ # # # # : 0 : if (saved->v3000->n_steabs && saved->v3000->lists_steabs && permuted->v3000->n_steabs && permuted->v3000->lists_steabs)
# # # # ]
176 : : {
177 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_steabs; j++)
178 : : {
179 [ # # ]: 0 : for (k = 2; k < saved->v3000->lists_steabs[j][1] + 2; k++)
180 : : {
181 : 0 : permuted->v3000->lists_steabs[j][k] = numbers[permuted->v3000->lists_steabs[j][k]];
182 : : }
183 : : }
184 : : }
185 [ # # # # : 0 : if (saved->v3000->n_sterel && saved->v3000->lists_sterel && permuted->v3000->n_sterel && permuted->v3000->lists_sterel)
# # # # ]
186 : : {
187 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_sterel; j++)
188 : : {
189 [ # # ]: 0 : for (k = 2; k < saved->v3000->lists_sterel[j][1] + 2; k++)
190 : : {
191 : 0 : permuted->v3000->lists_sterel[j][k] = numbers[permuted->v3000->lists_sterel[j][k]];
192 : : }
193 : : }
194 : : }
195 [ # # # # : 0 : if (saved->v3000->n_sterac && saved->v3000->lists_sterac && permuted->v3000->n_sterac && permuted->v3000->lists_sterac)
# # # # ]
196 : : {
197 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_sterac; j++)
198 : : {
199 [ # # ]: 0 : for (k = 2; k < saved->v3000->lists_sterac[j][1] + 2; k++)
200 : : {
201 : 0 : permuted->v3000->lists_sterac[j][k] = numbers[permuted->v3000->lists_sterac[j][k]];
202 : : }
203 : : }
204 : : }
205 : : }
206 : :
207 : 0 : return;
208 : : }
209 : :
210 : 0 : EXPIMP_TEMPLATE INCHI_API int INCHI_DECL PermuteMolfileText(
211 : : const char *moltext,
212 : : char *permuted_moltext,
213 : : size_t permuted_moltext_len)
214 : : {
215 : 0 : int status = -1;
216 [ # # # # : 0 : if (!moltext || !permuted_moltext || permuted_moltext_len == 0) return status;
# # ]
217 : :
218 : 0 : int *permutation_mapping = NULL;
219 : : ORIG_ATOM_DATA atom_data;
220 : : ORIG_ATOM_DATA permuted_atom_data;
221 : 0 : memset(&atom_data, 0, sizeof(atom_data));
222 : 0 : memset(&permuted_atom_data, 0, sizeof(permuted_atom_data));
223 : :
224 : : INCHI_IOSTREAM input_stream;
225 : 0 : inchi_ios_init(&input_stream, INCHI_IOS_TYPE_STRING, NULL);
226 : :
227 : : INCHI_IOSTREAM output_stream;
228 : 0 : inchi_ios_init(&output_stream, INCHI_IOS_TYPE_STRING, NULL);
229 : :
230 [ # # ]: 0 : if (inchi_ios_print_nodisplay(&input_stream, "%s", moltext) <= 0)
231 : 0 : goto exit;
232 : :
233 : 0 : INCHI_MODE input_atom_flags = 0;
234 : 0 : int struct_read_error = 0;
235 : 0 : int num_atoms = CreateOrigInpDataFromMolfile(
236 : : &input_stream,
237 : : &atom_data,
238 : : 0, 1, 1, 0, 0, NULL, NULL, NULL, NULL,
239 : : &input_atom_flags,
240 : : &struct_read_error,
241 : : NULL, 0
242 : : );
243 : :
244 [ # # ]: 0 : if (num_atoms <= 0)
245 : 0 : goto exit;
246 : :
247 [ # # ]: 0 : if (struct_read_error != 0)
248 : 0 : goto exit;
249 : :
250 [ # # ]: 0 : if (OrigAtData_Duplicate(&permuted_atom_data, &atom_data) != 0)
251 : 0 : goto exit;
252 : :
253 : 0 : permutation_mapping = inchi_malloc(num_atoms * sizeof(int));
254 [ # # ]: 0 : if (!permutation_mapping)
255 : 0 : goto exit;
256 : :
257 : : /* Enforce different permutation for molecules with more than one atom. */
258 : : int is_identity;
259 : : do {
260 [ # # ]: 0 : for (int i = 0; i < num_atoms; ++i)
261 : 0 : permutation_mapping[i] = i;
262 : 0 : shuffle(permutation_mapping, num_atoms, sizeof(int));
263 : 0 : is_identity = 1;
264 [ # # ]: 0 : for (int i = 0; i < num_atoms; ++i) {
265 [ # # ]: 0 : if (permutation_mapping[i] != i) {
266 : 0 : is_identity = 0;
267 : 0 : break;
268 : : }
269 : : }
270 [ # # # # ]: 0 : } while (num_atoms > 1 && is_identity);
271 : :
272 : 0 : OrigAtData_Permute(&permuted_atom_data, &atom_data, permutation_mapping);
273 : :
274 [ # # ]: 0 : if (OrigAtData_WriteToSDfile(
275 : : &permuted_atom_data,
276 : : &output_stream,
277 : : NULL, NULL, 0, 0, NULL, NULL) != 0)
278 : 0 : goto exit;
279 : :
280 : 0 : size_t out_len = output_stream.s.nUsedLength;
281 [ # # ]: 0 : if (out_len + 1 > permuted_moltext_len)
282 : 0 : goto exit;
283 : :
284 : 0 : memcpy(permuted_moltext, output_stream.s.pStr, out_len);
285 : 0 : permuted_moltext[out_len] = '\0';
286 : 0 : status = 0;
287 : :
288 : 0 : exit:
289 : 0 : inchi_ios_close(&input_stream);
290 : 0 : inchi_ios_close(&output_stream);
291 [ # # ]: 0 : inchi_free(permutation_mapping);
292 : 0 : FreeOrigAtData(&permuted_atom_data);
293 : 0 : FreeOrigAtData(&atom_data);
294 : :
295 : 0 : return status;
296 : : }
|