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