1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.data;
18
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23
24 import org.apache.commons.math3.util.FastMath;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class PolynomialParser {
48
49
50 public enum Unit {
51
52
53 RADIANS(1.0),
54
55
56 DEGREES(FastMath.toRadians(1.0)),
57
58
59 ARC_SECONDS(FastMath.toRadians(1.0 / 3600.0)),
60
61
62 MILLI_ARC_SECONDS(FastMath.toRadians(1.0 / 3600000.0)),
63
64
65 MICRO_ARC_SECONDS(FastMath.toRadians(1.0 / 3600000000.0)),
66
67
68 NO_UNITS(1.0);
69
70
71 private final double factor;
72
73
74
75
76 Unit(final double factor) {
77 this.factor = factor;
78 }
79
80
81
82
83
84 public double toSI(final double value) {
85 return value * factor;
86 }
87
88 }
89
90
91 private static final String[] MINUS = new String[] {
92 "-",
93 "\u2212"
94 };
95
96
97 private static final String[] PLUS = new String[] {
98 "+",
99 };
100
101
102 private static final String[] MULTIPLICATION = new String[] {
103 "*",
104 "\u00d7"
105 };
106
107
108 private static final String[] DEGREES = new String[] {
109 "\u00b0",
110 "\u25e6"
111 };
112
113
114 private static final String[] ARC_SECONDS = new String[] {
115 "\u2033",
116 "''",
117 "\""
118 };
119
120
121 private static final String[] SUPERSCRIPTS = new String[] {
122 "\u2070",
123 "\u00b9",
124 "\u00b2",
125 "\u00b3",
126 "\u2074",
127 "\u2075",
128 "\u2076",
129 "\u2077",
130 "\u2078",
131 "\u2079",
132 };
133
134
135 private static final String[] DIGITS = new String[] {
136 "0",
137 "1",
138 "2",
139 "3",
140 "4",
141 "5",
142 "6",
143 "7",
144 "8",
145 "9",
146 };
147
148
149 private final Pattern pattern;
150
151
152 private Matcher matcher;
153
154
155 private int next;
156
157
158 private double parsedCoefficient;
159
160
161 private int parsedPower;
162
163
164 private final Unit defaultUnit;
165
166
167
168
169
170 public PolynomialParser(final char freeVariable, final Unit defaultUnit) {
171
172 this.defaultUnit = defaultUnit;
173
174 final String space = "\\p{Space}*";
175 final String unit = either(quote(merge(DEGREES, ARC_SECONDS)));
176 final String sign = either(quote(merge(MINUS, PLUS)));
177 final String integer = "\\p{Digit}+";
178 final String exp = "[eE]" + zeroOrOne(sign, false) + integer;
179 final String fractional = "\\.\\p{Digit}*" + zeroOrOne(exp, false);
180 final String embeddedUnit = group(integer, true) +
181 group(unit, true) +
182 group(fractional, true);
183 final String appendedUnit = group(either(group(integer + zeroOrOne(fractional, false), false),
184 group(fractional, false)),
185 true) +
186 zeroOrOne(unit, true);
187 final String caretPower = "\\^" + any(quote(DIGITS));
188 final String superscripts = any(quote(SUPERSCRIPTS));
189 final String power = zeroOrOne(either(quote(MULTIPLICATION)), false) +
190 space + freeVariable +
191 either(caretPower, superscripts);
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 pattern = Pattern.compile(space + zeroOrOne(sign, true) + space +
213 either(group(embeddedUnit, false), group(appendedUnit, false)) +
214 space + zeroOrOne(power, true));
215
216 }
217
218
219
220
221
222
223 private String[] merge(final String[] markers1, final String[] markers2) {
224 final String[] merged = new String[markers1.length + markers2.length];
225 System.arraycopy(markers1, 0, merged, 0, markers1.length);
226 System.arraycopy(markers2, 0, merged, markers1.length, markers2.length);
227 return merged;
228 }
229
230
231
232
233
234 private String[] quote(final String ... markers) {
235 final String[] quoted = new String[markers.length];
236 for (int i = 0; i < markers.length; ++i) {
237 quoted[i] = "\\Q" + markers[i] + "\\E";
238 }
239 return quoted;
240 }
241
242
243
244
245
246
247 private String group(final CharSequence r, final boolean capturing) {
248 return (capturing ? "(" : "(?:") + r + ")";
249 }
250
251
252
253
254
255
256 private String either(final CharSequence ... markers) {
257 final StringBuilder builder = new StringBuilder();
258 for (final CharSequence marker : markers) {
259 if (builder.length() > 0) {
260 builder.append('|');
261 }
262 builder.append(marker);
263 }
264 return group(builder, false);
265 }
266
267
268
269
270
271
272 private String any(final CharSequence ... markers) {
273 return group(either(markers) + "*", true);
274 }
275
276
277
278
279
280
281 private String zeroOrOne(final CharSequence r, final boolean capturing) {
282 final String optional = group(r, false) + "?";
283 return capturing ? group(optional, true) : optional;
284 }
285
286
287
288
289
290
291
292
293 private int startMarker(final String s, final int offset, final String[] markers) {
294 for (int i = 0; i < markers.length; ++i) {
295 if (s.startsWith(markers[i], offset)) {
296 return i;
297 }
298 }
299 return -1;
300 }
301
302
303
304
305
306
307 public double[] parse(final String expression) {
308
309 final Map<Integer, Double> coefficients = new HashMap<Integer, Double>();
310 int maxDegree = -1;
311 matcher = pattern.matcher(expression);
312 next = 0;
313 while (parseMonomial(expression)) {
314 maxDegree = FastMath.max(maxDegree, parsedPower);
315 coefficients.put(parsedPower, parsedCoefficient);
316 }
317
318 if (maxDegree < 0) {
319 return null;
320 }
321
322 final double[] parsedPolynomial = new double[maxDegree + 1];
323 for (Map.Entry<Integer, Double> entry : coefficients.entrySet()) {
324 parsedPolynomial[entry.getKey()] = entry.getValue();
325 }
326
327 return parsedPolynomial;
328
329 }
330
331
332
333
334
335 private boolean parseMonomial(final String expression) {
336
337
338 final int signGroup = 1;
339 final int coeffIntGroup = 2;
340 final int embeddedUnitGroup = 3;
341 final int coeffFracGroup = 4;
342 final int coeffGroup = 5;
343 final int appendedUnitGroup = 6;
344 final int powerGroup = 7;
345 final int caretGroup = 8;
346 final int superScriptGroup = 9;
347
348
349 matcher.region(next, matcher.regionEnd());
350
351 if (matcher.lookingAt()) {
352
353
354 final double sign = startMarker(expression, matcher.start(signGroup), MINUS) >= 0 ? -1 : 1;
355 final String coeff;
356 final Unit unit;
357 if (matcher.start(embeddedUnitGroup) >= 0) {
358
359 coeff = matcher.group(coeffIntGroup) + matcher.group(coeffFracGroup);
360 if (startMarker(expression, matcher.start(embeddedUnitGroup), DEGREES) >= 0) {
361 unit = Unit.DEGREES;
362 } else {
363
364
365 unit = Unit.ARC_SECONDS;
366 }
367 } else {
368
369 coeff = matcher.group(coeffGroup);
370 if (startMarker(expression, matcher.start(appendedUnitGroup), DEGREES) >= 0) {
371 unit = Unit.DEGREES;
372 } else if (startMarker(expression, matcher.start(appendedUnitGroup), ARC_SECONDS) >= 0) {
373 unit = Unit.ARC_SECONDS;
374 } else {
375 unit = defaultUnit;
376 }
377 }
378 parsedCoefficient = sign * unit.toSI(Double.parseDouble(coeff));
379
380 if (matcher.start(powerGroup) < matcher.end(powerGroup)) {
381
382
383 if (matcher.start(caretGroup) < matcher.end(caretGroup)) {
384
385 parsedPower = 0;
386 for (int index = matcher.start(caretGroup); index < matcher.end(caretGroup); ++index) {
387 parsedPower = parsedPower * 10 + startMarker(expression, index, DIGITS);
388 }
389 } else if (matcher.start(superScriptGroup) < matcher.end(superScriptGroup)) {
390
391 parsedPower = 0;
392 for (int index = matcher.start(superScriptGroup); index < matcher.end(superScriptGroup); ++index) {
393 parsedPower = parsedPower * 10 + startMarker(expression, index, SUPERSCRIPTS);
394 }
395 } else {
396
397 parsedPower = 1;
398 }
399
400 } else {
401
402 parsedPower = 0;
403 }
404
405 next = matcher.end();
406 return true;
407
408 } else {
409
410 parsedCoefficient = Double.NaN;
411 parsedPower = -1;
412 return false;
413
414 }
415
416 }
417
418 }