1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Copyright (c) 2018-2021 The plumed team
3 (see the PEOPLE file at the root of the distribution for a list of names)
4
5 See http://www.plumed.org for more information.
6
7 This file is part of plumed, version 2.
8
9 plumed is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 plumed is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 #include "h36.h"
23 #include <vector>
24 #include "Exception.h"
25
26 namespace PLMD {
27
28 /// Tiny namespace for hybrid36 format.
29 /// This namespace includes freely available tools for h36 format.
30 namespace h36 {
31
32
33 /*! C port of the hy36encode() and hy36decode() functions in the
34 hybrid_36.py Python prototype/reference implementation.
35 See the Python script for more information.
36
37 This file has no external dependencies, NOT even standard C headers.
38 Optionally, use hybrid_36_c.h, or simply copy the declarations
39 into your code.
40
41 This file is unrestricted Open Source (cctbx.sf.net).
42 Please send corrections and enhancements to cctbx@cci.lbl.gov .
43
44 See also: http://cci.lbl.gov/hybrid_36/
45
46 Ralf W. Grosse-Kunstleve, Feb 2007.
47 */
48
49 /* The following #include may be commented out.
50 It is here only to enforce consistency of the declarations
51 and the definitions.
52 */
53 // #include <iotbx/pdb/hybrid_36_c.h>
54
55 /* All static functions below are implementation details
56 (and not accessible from other translation units).
57 */
58
59 static
60 const char*
digits_upper()61 digits_upper() { return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
62
63 static
64 const char*
digits_lower()65 digits_lower() { return "0123456789abcdefghijklmnopqrstuvwxyz"; }
66
67 static
68 const char*
value_out_of_range()69 value_out_of_range() { return "value out of range."; }
70
71 static
invalid_number_literal()72 const char* invalid_number_literal() { return "invalid number literal."; }
73
74 static
unsupported_width()75 const char* unsupported_width() { return "unsupported width."; }
76
77 static
78 void
fill_with_stars(unsigned width,char * result)79 fill_with_stars(unsigned width, char* result)
80 {
81 while (width) {
82 *result++ = '*';
83 width--;
84 }
85 *result = '\0';
86 }
87
88 static
89 void
encode_pure(const char * digits,unsigned digits_size,unsigned width,int value,char * result)90 encode_pure(
91 const char* digits,
92 unsigned digits_size,
93 unsigned width,
94 int value,
95 char* result)
96 {
97 char buf[16];
98 int rest;
99 unsigned i, j;
100 i = 0;
101 j = 0;
102 if (value < 0) {
103 j = 1;
104 value = -value;
105 }
106 while (1) {
107 rest = value / digits_size;
108 buf[i++] = digits[value - rest * digits_size];
109 if (rest == 0) break;
110 value = rest;
111 }
112 if (j) buf[i++] = '-';
113 for(j=i; j<width; j++) *result++ = ' ';
114 while (i != 0) *result++ = buf[--i];
115 *result = '\0';
116 }
117
118 static
119 const char*
decode_pure(const int * digits_values,unsigned digits_size,const char * s,unsigned s_size,int * result)120 decode_pure(
121 const int* digits_values,
122 unsigned digits_size,
123 const char* s,
124 unsigned s_size,
125 int* result)
126 {
127 int si, dv;
128 int have_minus = 0;
129 int have_non_blank = 0;
130 int value = 0;
131 unsigned i = 0;
132 for(; i<s_size; i++) {
133 si = s[i];
134 if (si < 0 || si > 127) {
135 *result = 0;
136 return invalid_number_literal();
137 }
138 if (si == ' ') {
139 if (!have_non_blank) continue;
140 value *= digits_size;
141 }
142 else if (si == '-') {
143 if (have_non_blank) {
144 *result = 0;
145 return invalid_number_literal();
146 }
147 have_non_blank = 1;
148 have_minus = 1;
149 continue;
150 }
151 else {
152 have_non_blank = 1;
153 dv = digits_values[si];
154 if (dv < 0 || dv >= digits_size) {
155 *result = 0;
156 return invalid_number_literal();
157 }
158 value *= digits_size;
159 value += dv;
160 }
161 }
162 if (have_minus) value = -value;
163 *result = value;
164 return 0;
165 }
166
167 /*! hybrid-36 encoder: converts integer value to string result
168
169 width: must be 4 (e.g. for residue sequence numbers)
170 or 5 (e.g. for atom serial numbers)
171
172 value: integer value to be converted
173
174 result: pointer to char array of size width+1 or greater
175 on return result is null-terminated
176
177 return value: pointer to error message, if any,
178 or 0 on success
179
180 Example usage (from C++):
181 char result[4+1];
182 const char* errmsg = hy36encode(4, 12345, result);
183 if (errmsg) throw std::runtime_error(errmsg);
184 */
185 const char*
hy36encode(unsigned width,int value,char * result)186 hy36encode(unsigned width, int value, char* result)
187 {
188 int i = value;
189 if (width == 4U) {
190 if (i >= -999) {
191 if (i < 10000) {
192 encode_pure(digits_upper(), 10U, 4U, i, result);
193 return 0;
194 }
195 i -= 10000;
196 if (i < 1213056 /* 26*36**3 */) {
197 i += 466560 /* 10*36**3 */;
198 encode_pure(digits_upper(), 36U, 0U, i, result);
199 return 0;
200 }
201 i -= 1213056;
202 if (i < 1213056) {
203 i += 466560;
204 encode_pure(digits_lower(), 36U, 0U, i, result);
205 return 0;
206 }
207 }
208 }
209 else if (width == 5U) {
210 if (i >= -9999) {
211 if (i < 100000) {
212 encode_pure(digits_upper(), 10U, 5U, i, result);
213 return 0;
214 }
215 i -= 100000;
216 if (i < 43670016 /* 26*36**4 */) {
217 i += 16796160 /* 10*36**4 */;
218 encode_pure(digits_upper(), 36U, 0U, i, result);
219 return 0;
220 }
221 i -= 43670016;
222 if (i < 43670016) {
223 i += 16796160;
224 encode_pure(digits_lower(), 36U, 0U, i, result);
225 return 0;
226 }
227 }
228 }
229 else {
230 fill_with_stars(width, result);
231 return unsupported_width();
232 }
233 fill_with_stars(width, result);
234 return value_out_of_range();
235 }
236
237 /*! hybrid-36 decoder: converts string s to integer result
238
239 width: must be 4 (e.g. for residue sequence numbers)
240 or 5 (e.g. for atom serial numbers)
241
242 s: string to be converted
243 does not have to be null-terminated
244
245 s_size: size of s
246 must be equal to width, or an error message is
247 returned otherwise
248
249 result: integer holding the conversion result
250
251 return value: pointer to error message, if any,
252 or 0 on success
253
254 Example usage (from C++):
255 int result;
256 const char* errmsg = hy36decode(width, "A1T5", 4, &result);
257 if (errmsg) throw std::runtime_error(errmsg);
258 */
259 const char*
hy36decode(unsigned width,const char * s,unsigned s_size,int * result)260 hy36decode(unsigned width, const char* s, unsigned s_size, int* result)
261 {
262 static const std::vector<int> digits_values_upper_vector([]() {
263 std::vector<int> ret(128U,-1);
264 for(unsigned i=0; i<36U; i++) {
265 int di = digits_upper()[i];
266 if (di < 0 || di > 127) {
267 plumed_error()<<"internal error hy36decode: integer value out of range";
268 }
269 ret[di] = i;
270 }
271 return ret;
272 }());
273 static const int* digits_values_upper=digits_values_upper_vector.data();
274 static const std::vector<int> digits_values_lower_vector([]() {
275 std::vector<int> ret(128U,-1);
276 for(unsigned i=0; i<36U; i++) {
277 int di = digits_lower()[i];
278 if (di < 0 || di > 127) {
279 plumed_error()<<"internal error hy36decode: integer value out of range";
280 }
281 ret[di] = i;
282 }
283 return ret;
284 }());
285 static const int* digits_values_lower=digits_values_lower_vector.data();
286 int di;
287 const char* errmsg;
288 if (s_size == width) {
289 di = s[0];
290 if (di >= 0 && di <= 127) {
291 if (digits_values_upper[di] >= 10) {
292 errmsg = decode_pure(digits_values_upper, 36U, s, s_size, result);
293 if (errmsg == 0) {
294 /* result - 10*36**(width-1) + 10**width */
295 if (width == 4U) (*result) -= 456560;
296 else if (width == 5U) (*result) -= 16696160;
297 else {
298 *result = 0;
299 return unsupported_width();
300 }
301 return 0;
302 }
303 }
304 else if (digits_values_lower[di] >= 10) {
305 errmsg = decode_pure(digits_values_lower, 36U, s, s_size, result);
306 if (errmsg == 0) {
307 /* result + 16*36**(width-1) + 10**width */
308 if (width == 4U) (*result) += 756496;
309 else if (width == 5U) (*result) += 26973856;
310 else {
311 *result = 0;
312 return unsupported_width();
313 }
314 return 0;
315 }
316 }
317 else {
318 errmsg = decode_pure(digits_values_upper, 10U, s, s_size, result);
319 if (errmsg) return errmsg;
320 if (!(width == 4U || width == 5U)) {
321 *result = 0;
322 return unsupported_width();
323 }
324 return 0;
325 }
326 }
327 }
328 *result = 0;
329 return invalid_number_literal();
330 }
331
332 }
333
334 }
335
336