1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.utilities;
39
40 import static org.apache.commons.lang3.StringUtils.repeat;
41 import static org.apache.commons.math3.util.FastMath.max;
42
43
44
45
46
47
48
49
50
51
52 public class Hybrid36 {
53
54 private static final String digitsBase10 = "0123456789";
55 private static final String digitsUpper = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
56 private static final String digitsLower = "0123456789abcdefghijklmnopqrstuvwxyz";
57 private static final String valueOutOfRange = "value out of range.";
58 private static final String invalidNumberLiteral = "invalid number literal.";
59 private static final String unsupportedWidth = "unsupported width.";
60 private static final int[] digitsValuesUpper = new int[128];
61 private static final int[] digitsValuesLower = new int[128];
62 private static boolean firstCall = true;
63
64
65
66
67 private Hybrid36() {
68
69 }
70
71
72
73
74
75
76
77
78
79 public static int decode(int width, String s) {
80 String outOfRange = "Internal error Hybrid-36.decode: integer value out of range.";
81 if (firstCall) {
82 firstCall = false;
83 for (int i = 0; i < 128; i++) {
84 digitsValuesUpper[i] = -1;
85 }
86 for (int i = 0; i < 128; i++) {
87 digitsValuesLower[i] = -1;
88 }
89 for (int i = 0; i < 36; i++) {
90 int di = digitsUpper.charAt(i);
91 if (di > 127) {
92 throw new Error(outOfRange);
93 }
94 digitsValuesUpper[di] = i;
95 }
96 for (int i = 0; i < 36; i++) {
97 int di = digitsLower.charAt(i);
98 if (di > 127) {
99 throw new Error(outOfRange);
100 }
101 digitsValuesLower[di] = i;
102 }
103 }
104 if (s.length() == width) {
105 int di = s.charAt(0);
106 if (di <= 127) {
107 if (digitsValuesUpper[di] >= 10) {
108 int result = decodePure(digitsValuesUpper, 36, s);
109 if (width == 4) {
110
111 result -= 456560;
112 } else if (width == 5) {
113
114 result -= 16696160;
115 } else {
116 throw new Error(unsupportedWidth);
117 }
118 return result;
119 } else if (digitsValuesLower[di] >= 10) {
120 int result = decodePure(digitsValuesLower, 36, s);
121
122 if (width == 4) {
123
124 result += 756496;
125 } else if (width == 5) {
126
127 result += 26973856;
128 } else {
129 throw new Error(unsupportedWidth);
130 }
131 return result;
132 } else {
133 int result = decodePure(digitsValuesUpper, 10, s);
134 if (!(width == 4 || width == 5)) {
135 throw new Error(unsupportedWidth);
136 }
137 return result;
138 }
139 }
140 }
141 throw new Error(invalidNumberLiteral);
142 }
143
144
145
146
147
148
149
150
151
152 public static String encode(int width, int value) {
153 int i = value;
154 if (width == 4) {
155 if (i >= -999) {
156 if (i < 10000) {
157 return encodePure(digitsBase10, 4, i);
158 }
159 i -= 10000;
160
161 if (i < 1213056) {
162
163 i += 466560;
164 return encodePure(digitsUpper, 0, i);
165 }
166 i -= 1213056;
167 if (i < 1213056) {
168 i += 466560;
169 return encodePure(digitsLower, 0, i);
170 }
171 }
172 } else if (width == 5) {
173 if (i >= -9999) {
174 if (i < 100000) {
175 return encodePure(digitsBase10, 5, i);
176 }
177 i -= 100000;
178
179 if (i < 43670016) {
180
181 i += 16796160;
182 return encodePure(digitsUpper, 0, i);
183 }
184 i -= 43670016;
185 if (i < 43670016) {
186 i += 16796160;
187 return encodePure(digitsLower, 0, i);
188 }
189 }
190 } else {
191 throw new Error(unsupportedWidth);
192 }
193 throw new Error(valueOutOfRange);
194 }
195
196 private static int decodePure(int[] digitsValues, int digitsSize, String s) {
197 boolean haveMinus = false;
198 boolean haveNonBlank = false;
199 int value = 0;
200 for (int i = 0; i < s.length(); i++) {
201 char si = s.charAt(i);
202 if (si > 127) {
203 throw new Error(invalidNumberLiteral);
204 }
205 if (si == ' ') {
206 if (!haveNonBlank) {
207 continue;
208 }
209 value *= digitsSize;
210 } else if (si == '-') {
211 if (haveNonBlank) {
212 throw new Error(invalidNumberLiteral);
213 }
214 haveNonBlank = true;
215 haveMinus = true;
216 } else {
217 haveNonBlank = true;
218 int dv = digitsValues[si];
219 if (dv < 0 || dv >= digitsSize) {
220 throw new Error(invalidNumberLiteral);
221 }
222 value *= digitsSize;
223 value += dv;
224 }
225 }
226 if (haveMinus) {
227 value = -value;
228 }
229 return value;
230 }
231
232 private static String encodePure(String digits, int width, int value) {
233 boolean neg = false;
234 if (value < 0) {
235 neg = true;
236 value = -value;
237 }
238 StringBuilder buf = new StringBuilder();
239 while (true) {
240 int rest = value / digits.length();
241 buf.append(digits.charAt(value - rest * digits.length()));
242 if (rest == 0) {
243 break;
244 }
245 value = rest;
246 }
247 if (neg) {
248 buf.append('-');
249 }
250 StringBuilder result = new StringBuilder();
251 result.append(repeat(" ", max(0, width - buf.length())));
252 for (int i = buf.length() - 1; i >= 0; i--) {
253 result.append(buf.charAt(i));
254 }
255 return result.toString();
256 }
257 }