Branch data Line data Source code
1 : : /*
2 : : * SPDX-License-Identifier: MIT
3 : : * Copyright (c) 2024 IUPAC and InChI Trust
4 : : */
5 : :
6 : : #include <stdio.h>
7 : : #include <stdlib.h>
8 : : #include <string.h>
9 : : #include <ctype.h>
10 : : #include <stdarg.h>
11 : : #include <errno.h>
12 : : #include <limits.h>
13 : : #include <float.h>
14 : : #include <ctype.h>
15 : : #include <locale.h>
16 : :
17 : : #include "mode.h"
18 : :
19 : : #if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 )
20 : : #include <malloc.h>
21 : : #include <io.h>
22 : : #endif
23 : :
24 : :
25 : : #ifdef _WIN32
26 : : #include <crtdbg.h>
27 : : #endif
28 : :
29 : : #include "ichimain.h"
30 : : #include "inchi_api.h"
31 : : #include "bcf_s.h"
32 : : #include "ichi_io.h"
33 : : #include "inpdef.h"
34 : : #include "permutation_util.h"
35 : :
36 : :
37 : 0 : int rrand(int m)
38 : : {
39 : : return
40 : 0 : (int)((double)m * (rand() / (RAND_MAX + 1.0)));
41 : : }
42 : :
43 : 0 : void shuffle(void* obj, size_t nmemb, size_t size)
44 : : {
45 : 0 : void* temp = inchi_malloc(size);
46 : 0 : size_t n = nmemb;
47 [ # # ]: 0 : while (n > 1)
48 : : {
49 : 0 : size_t k = rrand((int)n--);
50 [ # # ]: 0 : if (temp) /* djb-rwth: fixing a NULL pointer dereference */
51 : : {
52 : 0 : memcpy(temp, BYTE(obj) + n * size, size);
53 : 0 : memcpy(BYTE(obj) + n * size, BYTE(obj) + k * size, size);
54 : 0 : memcpy(BYTE(obj) + k * size, temp, size);
55 : : }
56 : : }
57 : : #ifdef _WIN32
58 : : _free_dbg(temp, _NORMAL_BLOCK); /* djb-rwth: _free_dbg for _malloc_dbg must be used if Windows SDK is used */
59 : : #else
60 : 0 : free(temp); /* djb-rwth: otherwise just free */
61 : : #endif
62 : 0 : }
63 : :
64 : :
65 : : /* Use after OrigAtData_Duplicate (permuted <-- saved) */
66 : 0 : void OrigAtData_Permute(ORIG_ATOM_DATA* permuted, ORIG_ATOM_DATA* saved, int* numbers)
67 : : {
68 : : int i, j, k;
69 : 0 : int nat = saved->num_inp_atoms;
70 : 0 : size_t atsize = sizeof(saved->at[0]);
71 [ # # ]: 0 : for (i = 0; i < nat; i++)
72 : : {
73 : 0 : j = numbers[i];
74 : 0 : memcpy(permuted->at + j, saved->at + i, atsize);
75 [ # # ]: 0 : for (k = 0; k < permuted->at[j].valence; k++)
76 : : {
77 : 0 : permuted->at[j].neighbor[k] = numbers[permuted->at[j].neighbor[k]];
78 : : }
79 : 0 : permuted->at[j].orig_at_number = 1 + numbers[permuted->at[j].orig_at_number - 1];
80 : : }
81 [ # # # # ]: 0 : if (saved->polymer && permuted->polymer)
82 : : {
83 [ # # ]: 0 : if (saved->polymer->pzz)
84 : : {
85 [ # # ]: 0 : for (k = 0; k < saved->polymer->n_pzz; k++)
86 : : {
87 : 0 : permuted->polymer->pzz[k] = numbers[permuted->polymer->pzz[k]];
88 : : }
89 : : }
90 [ # # ]: 0 : if (saved->polymer->units)
91 : : {
92 [ # # ]: 0 : for (k = 0; k < saved->polymer->n; k++)
93 : : {
94 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->cap1 - 1];
95 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->end_atom1 - 1];
96 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->cap2 - 1];
97 : 0 : permuted->polymer->units[k]->cap1 = 1 + numbers[permuted->polymer->units[k]->end_atom2 - 1];
98 [ # # ]: 0 : if (permuted->polymer->units[k]->alist)
99 : : {
100 [ # # ]: 0 : for (j = 0; j < permuted->polymer->units[k]->na; j++)
101 : : {
102 : 0 : permuted->polymer->units[k]->alist[j] = 1 + numbers[permuted->polymer->units[k]->alist[j] - 1];
103 : : }
104 [ # # ]: 0 : for (j = 0; j < permuted->polymer->units[k]->nb; j++)
105 : : {
106 : 0 : permuted->polymer->units[k]->blist[2 * j] = 1 + numbers[permuted->polymer->units[k]->blist[2 * j] - 1];
107 : 0 : permuted->polymer->units[k]->blist[2 * j + 1] = 1 + numbers[permuted->polymer->units[k]->blist[2 * j + 1] - 1];
108 : : }
109 : : }
110 : : }
111 : : }
112 : : }
113 [ # # # # ]: 0 : if (saved->v3000 && permuted->v3000)
114 : : {
115 [ # # # # ]: 0 : if (saved->v3000->atom_index_orig && permuted->v3000->atom_index_orig)
116 : : {
117 [ # # ]: 0 : for (k = 0; k < nat; k++)
118 : : {
119 : 0 : permuted->v3000->atom_index_orig[k] = numbers[permuted->v3000->atom_index_orig[k]];
120 : : }
121 : : }
122 [ # # # # ]: 0 : if (saved->v3000->atom_index_fin && permuted->v3000->atom_index_fin)
123 : : {
124 [ # # ]: 0 : for (k = 0; k < nat; k++)
125 : : {
126 : 0 : permuted->v3000->atom_index_fin[k] = numbers[permuted->v3000->atom_index_fin[k]];
127 : : }
128 : : }
129 [ # # # # : 0 : if (saved->v3000->n_haptic_bonds && saved->v3000->lists_haptic_bonds && permuted->v3000->n_haptic_bonds && permuted->v3000->lists_haptic_bonds)
# # # # ]
130 : : {
131 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_haptic_bonds; j++)
132 : : {
133 : 0 : permuted->v3000->lists_haptic_bonds[j][1] = numbers[permuted->v3000->lists_haptic_bonds[j][1]];
134 [ # # ]: 0 : for (k = 3; k < saved->v3000->lists_haptic_bonds[j][2]; k++)
135 : : {
136 : 0 : permuted->v3000->lists_haptic_bonds[j][k] = numbers[permuted->v3000->lists_haptic_bonds[j][k]];
137 : : }
138 : : }
139 : : }
140 [ # # # # : 0 : if (saved->v3000->n_steabs && saved->v3000->lists_steabs && permuted->v3000->n_steabs && permuted->v3000->lists_steabs)
# # # # ]
141 : : {
142 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_steabs; j++)
143 : : {
144 [ # # ]: 0 : for (k = 2; k < saved->v3000->lists_steabs[j][1] + 2; k++)
145 : : {
146 : 0 : permuted->v3000->lists_steabs[j][k] = numbers[permuted->v3000->lists_steabs[j][k]];
147 : : }
148 : : }
149 : : }
150 [ # # # # : 0 : if (saved->v3000->n_sterel && saved->v3000->lists_sterel && permuted->v3000->n_sterel && permuted->v3000->lists_sterel)
# # # # ]
151 : : {
152 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_sterel; j++)
153 : : {
154 [ # # ]: 0 : for (k = 2; k < saved->v3000->lists_sterel[j][1] + 2; k++)
155 : : {
156 : 0 : permuted->v3000->lists_sterel[j][k] = numbers[permuted->v3000->lists_sterel[j][k]];
157 : : }
158 : : }
159 : : }
160 [ # # # # : 0 : if (saved->v3000->n_sterac && saved->v3000->lists_sterac && permuted->v3000->n_sterac && permuted->v3000->lists_sterac)
# # # # ]
161 : : {
162 [ # # ]: 0 : for (j = 0; j < saved->v3000->n_sterac; j++)
163 : : {
164 [ # # ]: 0 : for (k = 2; k < saved->v3000->lists_sterac[j][1] + 2; k++)
165 : : {
166 : 0 : permuted->v3000->lists_sterac[j][k] = numbers[permuted->v3000->lists_sterac[j][k]];
167 : : }
168 : : }
169 : : }
170 : : }
171 : :
172 : 0 : return;
173 : : }
174 : :
175 : 0 : EXPIMP_TEMPLATE INCHI_API int INCHI_DECL PermuteMolfileText(
176 : : const char *moltext,
177 : : char *permuted_moltext,
178 : : size_t permuted_moltext_len)
179 : : {
180 : 0 : int status = -1;
181 [ # # # # : 0 : if (!moltext || !permuted_moltext || permuted_moltext_len == 0) return status;
# # ]
182 : :
183 : 0 : int *permutation_mapping = NULL;
184 : : ORIG_ATOM_DATA atom_data;
185 : : ORIG_ATOM_DATA permuted_atom_data;
186 : 0 : memset(&atom_data, 0, sizeof(atom_data));
187 : 0 : memset(&permuted_atom_data, 0, sizeof(permuted_atom_data));
188 : :
189 : : INCHI_IOSTREAM input_stream;
190 : 0 : inchi_ios_init(&input_stream, INCHI_IOS_TYPE_STRING, NULL);
191 : :
192 : : INCHI_IOSTREAM output_stream;
193 : 0 : inchi_ios_init(&output_stream, INCHI_IOS_TYPE_STRING, NULL);
194 : :
195 [ # # ]: 0 : if (inchi_ios_print_nodisplay(&input_stream, "%s", moltext) <= 0)
196 : 0 : goto exit;
197 : :
198 : 0 : INCHI_MODE input_atom_flags = 0;
199 : 0 : int struct_read_error = 0;
200 : 0 : int num_atoms = CreateOrigInpDataFromMolfile(
201 : : &input_stream,
202 : : &atom_data,
203 : : 0, 1, 1, 0, 0, NULL, NULL, NULL, NULL,
204 : : &input_atom_flags,
205 : : &struct_read_error,
206 : : NULL, 0
207 : : );
208 : :
209 [ # # ]: 0 : if (num_atoms <= 0)
210 : 0 : goto exit;
211 : :
212 [ # # ]: 0 : if (struct_read_error != 0)
213 : 0 : goto exit;
214 : :
215 [ # # ]: 0 : if (OrigAtData_Duplicate(&permuted_atom_data, &atom_data) != 0)
216 : 0 : goto exit;
217 : :
218 : 0 : permutation_mapping = inchi_malloc(num_atoms * sizeof(int));
219 [ # # ]: 0 : if (!permutation_mapping)
220 : 0 : goto exit;
221 : :
222 : : /* Enforce different permutation for molecules with more than one atom. */
223 : : int is_identity;
224 : : do {
225 [ # # ]: 0 : for (int i = 0; i < num_atoms; ++i)
226 : 0 : permutation_mapping[i] = i;
227 : 0 : shuffle(permutation_mapping, num_atoms, sizeof(int));
228 : 0 : is_identity = 1;
229 [ # # ]: 0 : for (int i = 0; i < num_atoms; ++i) {
230 [ # # ]: 0 : if (permutation_mapping[i] != i) {
231 : 0 : is_identity = 0;
232 : 0 : break;
233 : : }
234 : : }
235 [ # # # # ]: 0 : } while (num_atoms > 1 && is_identity);
236 : :
237 : 0 : OrigAtData_Permute(&permuted_atom_data, &atom_data, permutation_mapping);
238 : :
239 [ # # ]: 0 : if (OrigAtData_WriteToSDfile(
240 : : &permuted_atom_data,
241 : : &output_stream,
242 : : NULL, NULL, 0, 0, NULL, NULL) != 0)
243 : 0 : goto exit;
244 : :
245 : 0 : size_t out_len = output_stream.s.nUsedLength;
246 [ # # ]: 0 : if (out_len + 1 > permuted_moltext_len)
247 : 0 : goto exit;
248 : :
249 : 0 : memcpy(permuted_moltext, output_stream.s.pStr, out_len);
250 : 0 : permuted_moltext[out_len] = '\0';
251 : 0 : status = 0;
252 : :
253 : 0 : exit:
254 : 0 : inchi_ios_close(&input_stream);
255 : 0 : inchi_ios_close(&output_stream);
256 [ # # ]: 0 : inchi_free(permutation_mapping);
257 : 0 : FreeOrigAtData(&permuted_atom_data);
258 : 0 : FreeOrigAtData(&atom_data);
259 : :
260 : 0 : return status;
261 : : }
|