Changeset View
Changeset View
Standalone View
Standalone View
libs/widgetutils/kis_num_parser.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (c) 2016 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch> | ||||
3 | * | ||||
4 | * This program is free software; you can redistribute it and/or modify | ||||
5 | * it under the terms of the GNU General Public License as published by | ||||
6 | * the Free Software Foundation; either version 2 of the License, or | ||||
7 | * (at your option) any later version. | ||||
8 | * | ||||
9 | * This program is distributed in the hope that it will be useful, | ||||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
12 | * GNU General Public License for more details. | ||||
13 | * | ||||
14 | * You should have received a copy of the GNU General Public License | ||||
15 | * along with this program; if not, write to the Free Software | ||||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
17 | */ | ||||
18 | | ||||
19 | #include "kis_num_parser.h" | ||||
20 | | ||||
21 | //#include <QtMath> | ||||
22 | #include <qmath.h> | ||||
23 | #include <QVector> | ||||
24 | #include <QRegExp> | ||||
25 | #include <QStringList> | ||||
26 | #include <QVariant> | ||||
27 | | ||||
28 | #include <iostream> | ||||
29 | | ||||
30 | using namespace std; | ||||
31 | | ||||
32 | const QVector<char> opLevel1 = {'+', '-'}; | ||||
33 | const QVector<char> opLevel2 = {'*', '/'}; | ||||
34 | | ||||
35 | const QStringList supportedFuncs = {"", "cos", "sin", "tan", "acos", "asin", "atan", "exp", "ln", "log10", "abs"}; | ||||
36 | | ||||
37 | const QRegExp funcExpr("(-)?([a-zA-Z]*)?\\((.+)\\)"); | ||||
38 | const QRegExp numberExpr("(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)"); | ||||
39 | | ||||
40 | const QRegExp funcExprInteger("(-)?\\((.+)\\)"); | ||||
41 | const QRegExp integerExpr("(-)?([0-9]+)"); | ||||
42 | | ||||
43 | //double functions | ||||
44 | double treatFuncs(QString const& expr, bool & noProblem); | ||||
45 | double treatLevel1(QString const& expr, bool & noProblem); | ||||
46 | double treatLevel2(QString const& expr, bool & noProblem); | ||||
47 | double treatLevel3(QString const& expr, bool & noProblem); | ||||
48 | | ||||
49 | //int functions | ||||
50 | double treatLevel1Int(QString const& expr, bool & noProblem); | ||||
51 | double treatLevel2Int(QString const& expr, bool & noProblem); | ||||
52 | double treatFuncsInt(QString const& expr, bool & noProblem); | ||||
53 | | ||||
54 | namespace KisNumericParser { | ||||
55 | | ||||
56 | /*! | ||||
57 | * \param expr the expression to parse | ||||
58 | * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise. | ||||
59 | * \return the numerical value the expression eval to (or 0 in case of error). | ||||
60 | */ | ||||
61 | double parseSimpleMathExpr(const QString &expr, bool *noProblem) | ||||
62 | { | ||||
63 | | ||||
64 | bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided). | ||||
65 | | ||||
66 | //then go down each 3 levels of operation priority. | ||||
67 | if (noProblem != nullptr) { | ||||
68 | return treatLevel1(expr, *noProblem); | ||||
69 | } | ||||
70 | | ||||
71 | return treatLevel1(expr, ok); | ||||
72 | | ||||
73 | } | ||||
74 | | ||||
75 | /*! | ||||
76 | * \param expr the expression to parse | ||||
77 | * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise. | ||||
78 | * \return the numerical value the expression eval to (or 0 in case of error). | ||||
79 | */ | ||||
80 | int parseIntegerMathExpr(QString const& expr, bool* noProblem) | ||||
81 | { | ||||
82 | | ||||
83 | bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided). | ||||
84 | | ||||
85 | if (noProblem != nullptr) { | ||||
86 | return qRound(treatLevel1Int(expr, *noProblem)); | ||||
87 | } | ||||
88 | | ||||
89 | return qRound(treatLevel1Int(expr, ok)); | ||||
90 | | ||||
91 | } | ||||
92 | | ||||
93 | } //namespace KisNumericParser. | ||||
94 | | ||||
95 | | ||||
96 | //intermediate functions | ||||
97 | | ||||
98 | /*! | ||||
99 | * \brief extractSubExprLevel1 extract from an expression the part of an expression that need to be treated recursivly before computing level 1 operations (+, -). | ||||
100 | * \param expr The expression to treat, the part returned will be removed. | ||||
101 | * \param nextOp This reference, in case of sucess, will hold the first level operation identified as separator ('+' or '-') | ||||
102 | * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise. | ||||
103 | * \return The first part of the expression that doesn't contain first level operations not nested within parenthesis. | ||||
104 | */ | ||||
105 | inline QString extractSubExprLevel1(QString & expr, char & nextOp, bool & noProblem){ | ||||
106 | | ||||
107 | QString ret; | ||||
108 | | ||||
109 | int subCount = 0; | ||||
110 | | ||||
111 | bool lastMetIsNumber = false; | ||||
112 | | ||||
113 | for(int i = 0; i < expr.size(); i++){ | ||||
114 | | ||||
115 | if (expr.at(i) == '(') { | ||||
116 | subCount++; | ||||
117 | } | ||||
118 | | ||||
119 | if (expr.at(i) == ')') { | ||||
120 | subCount--; | ||||
121 | } | ||||
122 | | ||||
123 | if (subCount < 0) { | ||||
124 | noProblem = false; | ||||
125 | return ret; | ||||
126 | } | ||||
127 | | ||||
128 | if(i == expr.size()-1 && subCount == 0){ | ||||
129 | ret = expr; | ||||
130 | expr.clear(); | ||||
131 | break; | ||||
132 | } | ||||
133 | | ||||
134 | if( (expr.at(i) == '+' || expr.at(i) == '-') && | ||||
135 | subCount == 0) { | ||||
136 | | ||||
137 | if (expr.at(i) == '-' && | ||||
138 | i < expr.size()-1) { | ||||
139 | | ||||
140 | bool cond = !expr.at(i+1).isSpace(); | ||||
141 | | ||||
142 | if (cond && !lastMetIsNumber) { | ||||
143 | continue; | ||||
144 | } | ||||
145 | | ||||
146 | } | ||||
147 | | ||||
148 | ret = expr.mid(0, i).trimmed(); | ||||
149 | nextOp = expr.at(i).toLatin1(); | ||||
150 | expr = expr.mid(i+1); | ||||
151 | break; | ||||
152 | | ||||
153 | } | ||||
154 | | ||||
155 | if (expr.at(i).isDigit()) { | ||||
156 | lastMetIsNumber = true; | ||||
157 | } else if (expr.at(i) != '.' && | ||||
158 | !expr.at(i).isSpace()) { | ||||
159 | lastMetIsNumber = false; | ||||
160 | } | ||||
161 | | ||||
162 | } | ||||
163 | | ||||
164 | noProblem = true; | ||||
165 | return ret; | ||||
166 | } | ||||
167 | | ||||
168 | | ||||
169 | /*! | ||||
170 | * \brief extractSubExprLevel2 extract from an expression the part of an expression that need to be treated recursivly before computing level 2 operations (*, /). | ||||
171 | * \param expr The expression to treat, the part returned will be removed. | ||||
172 | * \param nextOp This reference, in case of sucess, will hold the first level operation identified as separator ('*' or '/') | ||||
173 | * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise. | ||||
174 | * \return The first part of the expression that doesn't contain second level operations not nested within parenthesis. | ||||
175 | */ | ||||
176 | inline QString extractSubExprLevel2(QString & expr, char & nextOp, bool & noProblem){ | ||||
177 | | ||||
178 | QString ret; | ||||
179 | | ||||
180 | int subCount = 0; | ||||
181 | | ||||
182 | for(int i = 0; i < expr.size(); i++){ | ||||
183 | | ||||
184 | if (expr.at(i) == '(') { | ||||
185 | subCount++; | ||||
186 | } | ||||
187 | | ||||
188 | if (expr.at(i) == ')') { | ||||
189 | subCount--; | ||||
190 | } | ||||
191 | | ||||
192 | if (subCount < 0) { | ||||
193 | noProblem = false; | ||||
194 | return ret; | ||||
195 | } | ||||
196 | | ||||
197 | if(i == expr.size()-1 && subCount == 0){ | ||||
198 | ret = expr; | ||||
199 | expr.clear(); | ||||
200 | break; | ||||
201 | } | ||||
202 | | ||||
203 | if( (expr.at(i) == '*' || expr.at(i) == '/') && | ||||
204 | subCount == 0) { | ||||
205 | | ||||
206 | ret = expr.mid(0, i).trimmed(); | ||||
207 | nextOp = expr.at(i).toLatin1(); | ||||
208 | expr = expr.mid(i+1); | ||||
209 | break; | ||||
210 | | ||||
211 | } | ||||
212 | | ||||
213 | } | ||||
214 | | ||||
215 | noProblem = true; | ||||
216 | return ret; | ||||
217 | } | ||||
218 | | ||||
219 | /*! | ||||
220 | * \brief treatLevel1 treat an expression at the first level of recursion. | ||||
221 | * \param expr The expression to treat. | ||||
222 | * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. | ||||
223 | * \return The value of the parsed expression or subexpression or 0 in case of error. | ||||
224 | */ | ||||
225 | double treatLevel1(const QString &expr, bool & noProblem) | ||||
226 | { | ||||
227 | | ||||
228 | noProblem = true; | ||||
229 | | ||||
230 | QString exprDestructable = expr; | ||||
231 | | ||||
232 | char nextOp = '+'; | ||||
233 | double result = 0.0; | ||||
234 | | ||||
235 | while (!exprDestructable.isEmpty()) { | ||||
236 | | ||||
237 | double sign = (nextOp == '-') ? -1 : 1; | ||||
238 | QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem); | ||||
239 | | ||||
240 | if (!noProblem) { | ||||
241 | return 0.0; | ||||
242 | } | ||||
243 | | ||||
244 | if (sign > 0) { | ||||
245 | result += treatLevel2(part, noProblem); | ||||
246 | } else { | ||||
247 | result -= treatLevel2(part, noProblem); | ||||
248 | } | ||||
249 | | ||||
250 | if(!noProblem){ | ||||
251 | return 0.0; | ||||
252 | } | ||||
253 | } | ||||
254 | | ||||
255 | return result; | ||||
256 | | ||||
257 | } | ||||
258 | | ||||
259 | /*! | ||||
260 | * \brief treatLevel2 treat a subexpression at the second level of recursion. | ||||
261 | * \param expr The subexpression to treat. | ||||
262 | * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. | ||||
263 | * \return The value of the parsed subexpression or 0 in case of error. | ||||
264 | * | ||||
265 | * The expression should not contain first level operations not nested in parenthesis. | ||||
266 | */ | ||||
267 | double treatLevel2(QString const& expr, bool & noProblem) | ||||
268 | { | ||||
269 | | ||||
270 | noProblem = true; | ||||
271 | | ||||
272 | QString exprDestructable = expr; | ||||
273 | | ||||
274 | char nextOp = '*'; | ||||
275 | | ||||
276 | QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); | ||||
277 | | ||||
278 | double result = treatLevel3(part, noProblem); | ||||
279 | | ||||
280 | while (!exprDestructable.isEmpty()) { | ||||
281 | | ||||
282 | if (!noProblem) { | ||||
283 | return 0.0; | ||||
284 | } | ||||
285 | | ||||
286 | bool needToMultiply = (nextOp == '*'); | ||||
287 | part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); | ||||
288 | | ||||
289 | if (!noProblem) { | ||||
290 | return 0.0; | ||||
291 | } | ||||
292 | | ||||
293 | if (needToMultiply) { | ||||
294 | result *= treatLevel3(part, noProblem); | ||||
295 | } else { | ||||
296 | result /= treatLevel3(part, noProblem); | ||||
297 | } | ||||
298 | } | ||||
299 | | ||||
300 | return result; | ||||
301 | } | ||||
302 | | ||||
303 | /*! | ||||
304 | * \brief treatLevel3 treat a subexpression at the third level of recursion. | ||||
305 | * \param expr The subexpression to treat. | ||||
306 | * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. | ||||
307 | * \return The value of the parsed subexpression or 0 in case of error. | ||||
308 | * | ||||
309 | * The expression should not contain first or second level operations not nested in parenthesis. | ||||
310 | */ | ||||
311 | double treatLevel3(const QString &expr, bool & noProblem) | ||||
312 | { | ||||
313 | | ||||
314 | noProblem = true; | ||||
315 | | ||||
316 | int indexPower = -1; | ||||
317 | int indexCount = 0; | ||||
318 | int subLevels = 0; | ||||
319 | | ||||
320 | for (int i = 0; i < expr.size(); i++) { | ||||
321 | if (expr.at(i) == '(') { | ||||
322 | subLevels++; | ||||
323 | } else if(expr.at(i) == ')') { | ||||
324 | subLevels--; | ||||
325 | if (subLevels < 0) { | ||||
326 | noProblem = false; | ||||
327 | return 0.0; | ||||
328 | } | ||||
329 | } else if (expr.at(i) == '^') { | ||||
330 | if (subLevels == 0) { | ||||
331 | indexPower = i; | ||||
332 | indexCount++; | ||||
333 | } | ||||
334 | } | ||||
335 | } | ||||
336 | | ||||
337 | if (indexCount > 1 || indexPower + 1 >= expr.size()) { | ||||
338 | noProblem = false; | ||||
339 | return 0.0; | ||||
340 | } | ||||
341 | | ||||
342 | if (indexPower > -1) { | ||||
343 | | ||||
344 | QStringList subExprs; | ||||
345 | subExprs << expr.mid(0,indexPower); | ||||
346 | subExprs << expr.mid(indexPower+1); | ||||
347 | | ||||
348 | bool noProb1 = true; | ||||
349 | bool noProb2 = true; | ||||
350 | | ||||
351 | double base = treatFuncs(subExprs[0], noProb1); | ||||
352 | double power = treatFuncs(subExprs[1], noProb2); | ||||
353 | | ||||
354 | return qPow(base, power); | ||||
355 | | ||||
356 | } else { | ||||
357 | return treatFuncs(expr, noProblem); | ||||
358 | } | ||||
359 | | ||||
360 | noProblem = false; | ||||
361 | return 0.0; | ||||
362 | | ||||
363 | } | ||||
364 | | ||||
365 | /*! | ||||
366 | * \brief treatFuncs treat the last level of recursion: parenthesis and functions. | ||||
367 | * \param expr The expression to parse. | ||||
368 | * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. | ||||
369 | * \return The value of the parsed subexpression or 0 in case of error. | ||||
370 | * | ||||
371 | * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function. | ||||
372 | */ | ||||
373 | double treatFuncs(QString const& expr, bool & noProblem) | ||||
374 | { | ||||
375 | | ||||
376 | noProblem = true; | ||||
377 | | ||||
378 | QRegExp funcExp = funcExpr; //copy the expression in the current execution stack, to avoid errors for example when multiple thread call this function. | ||||
379 | QRegExp numExp = numberExpr; | ||||
380 | | ||||
381 | if (funcExp.exactMatch(expr.trimmed())) { | ||||
382 | | ||||
383 | int sign = funcExp.capturedTexts()[1].isEmpty() ? 1 : -1; | ||||
384 | QString func = funcExp.capturedTexts()[2].toLower(); | ||||
385 | QString subExpr = funcExp.capturedTexts()[3]; | ||||
386 | | ||||
387 | double val = treatLevel1(subExpr, noProblem); | ||||
388 | | ||||
389 | if (!noProblem) { | ||||
390 | return 0.0; | ||||
391 | } | ||||
392 | | ||||
393 | if (func.isEmpty()) { | ||||
394 | return sign*val; | ||||
395 | } | ||||
396 | | ||||
397 | if (!supportedFuncs.contains(func)) { | ||||
398 | noProblem = false; | ||||
399 | return 0.0; | ||||
400 | } | ||||
401 | | ||||
402 | //trigonometry is done in degree | ||||
403 | if (func == "cos") { | ||||
404 | val = qCos(val/180*qAcos(-1)); | ||||
405 | } else if (func == "sin") { | ||||
406 | val = qSin(val/180*qAcos(-1)); | ||||
407 | } else if (func == "tan") { | ||||
408 | val = qTan(val/180*qAcos(-1)); | ||||
409 | } else if(func == "acos") { | ||||
410 | val = qAcos(val)*180/qAcos(-1); | ||||
411 | } else if (func == "asin") { | ||||
412 | val = qAsin(val)*180/qAcos(-1); | ||||
413 | } else if (func == "atan") { | ||||
414 | val = qAtan(val)*180/qAcos(-1); | ||||
415 | } else if (func == "exp") { | ||||
416 | val = qExp(val); | ||||
417 | } else if (func == "ln") { | ||||
418 | val = qLn(val); | ||||
419 | } else if (func == "log10") { | ||||
420 | val = qLn(val)/qLn(10.0); | ||||
421 | } else if (func == "abs") { | ||||
422 | val = qAbs(val); | ||||
423 | } | ||||
424 | | ||||
425 | return sign*val; | ||||
426 | } else if(numExp.exactMatch(expr.trimmed())) { | ||||
427 | return expr.toDouble(&noProblem); | ||||
428 | } | ||||
429 | | ||||
430 | noProblem = false; | ||||
431 | return 0.0; | ||||
432 | | ||||
433 | } | ||||
434 | | ||||
435 | //int functions | ||||
436 | /*! | ||||
437 | * \brief treatLevel1 treat an expression at the first level of recursion. | ||||
438 | * \param expr The expression to treat. | ||||
439 | * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. | ||||
440 | * \return The value of the parsed expression or subexpression or 0 in case of error. | ||||
441 | */ | ||||
442 | double treatLevel1Int(QString const& expr, bool & noProblem) | ||||
443 | { | ||||
444 | | ||||
445 | noProblem = true; | ||||
446 | | ||||
447 | QString exprDestructable = expr; | ||||
448 | | ||||
449 | char nextOp = '+'; | ||||
450 | double result = 0.0; | ||||
451 | | ||||
452 | while (!exprDestructable.isEmpty()) { | ||||
453 | | ||||
454 | double sign = (nextOp == '-') ? -1 : 1; | ||||
455 | QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem); | ||||
456 | | ||||
457 | if( !noProblem) { | ||||
458 | return 0.0; | ||||
459 | } | ||||
460 | | ||||
461 | if (sign > 0) { | ||||
462 | result += treatLevel2Int(part, noProblem); | ||||
463 | } else { | ||||
464 | result -= treatLevel2Int(part, noProblem); | ||||
465 | } | ||||
466 | | ||||
467 | if(!noProblem){ | ||||
468 | return 0.0; | ||||
469 | } | ||||
470 | } | ||||
471 | | ||||
472 | return result; | ||||
473 | | ||||
474 | } | ||||
475 | | ||||
476 | /*! | ||||
477 | * \brief treatLevel2 treat a subexpression at the second level of recursion. | ||||
478 | * \param expr The subexpression to treat. | ||||
479 | * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. | ||||
480 | * \return The value of the parsed subexpression or 0 in case of error. | ||||
481 | * | ||||
482 | * The expression should not contain first level operations not nested in parenthesis. | ||||
483 | */ | ||||
484 | double treatLevel2Int(const QString &expr, bool &noProblem) | ||||
485 | { | ||||
486 | | ||||
487 | noProblem = true; | ||||
488 | | ||||
489 | QString exprDestructable = expr; | ||||
490 | | ||||
491 | char nextOp = '*'; | ||||
492 | | ||||
493 | QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); | ||||
494 | | ||||
495 | double result = treatFuncsInt(part, noProblem); | ||||
496 | | ||||
497 | while (!exprDestructable.isEmpty()) { | ||||
498 | | ||||
499 | if (!noProblem) { | ||||
500 | return 0.0; | ||||
501 | } | ||||
502 | | ||||
503 | bool needToMultiply = (nextOp == '*'); | ||||
504 | part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); | ||||
505 | | ||||
506 | if (!noProblem) { | ||||
507 | return 0.0; | ||||
508 | } | ||||
509 | | ||||
510 | if (needToMultiply) { | ||||
511 | result *= treatFuncsInt(part, noProblem); | ||||
512 | } else { | ||||
513 | | ||||
514 | double val = treatFuncsInt(part, noProblem); | ||||
515 | | ||||
516 | if(std::isinf(result/val) || std::isnan(result/val)){ | ||||
517 | noProblem = false; | ||||
518 | return 0.0; | ||||
519 | } | ||||
520 | | ||||
521 | result /= val; | ||||
522 | } | ||||
523 | } | ||||
524 | | ||||
525 | return result; | ||||
526 | | ||||
527 | } | ||||
528 | | ||||
529 | /*! | ||||
530 | * \brief treatFuncs treat the last level of recursion: parenthesis | ||||
531 | * \param expr The expression to parse. | ||||
532 | * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. | ||||
533 | * \return The value of the parsed subexpression or 0 in case of error. | ||||
534 | * | ||||
535 | * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function. | ||||
536 | */ | ||||
537 | double treatFuncsInt(QString const& expr, bool & noProblem) | ||||
538 | { | ||||
539 | | ||||
540 | noProblem = true; | ||||
541 | | ||||
542 | QRegExp funcExpInteger = funcExprInteger; | ||||
543 | QRegExp integerExp = integerExpr; | ||||
544 | QRegExp numberExp = numberExpr; | ||||
545 | | ||||
546 | if (funcExpInteger.exactMatch(expr.trimmed())) { | ||||
547 | | ||||
548 | int sign = funcExpInteger.capturedTexts()[1].isEmpty() ? 1 : -1; | ||||
549 | QString subExpr = funcExpInteger.capturedTexts()[2]; | ||||
550 | | ||||
551 | double val = treatLevel1Int(subExpr, noProblem); | ||||
552 | | ||||
553 | if (!noProblem) { | ||||
554 | return 0; | ||||
555 | } | ||||
556 | | ||||
557 | return sign*val; | ||||
558 | | ||||
559 | } else if(numberExp.exactMatch(expr.trimmed())) { | ||||
560 | double value = QVariant(expr).toDouble(&noProblem); | ||||
561 | return value; | ||||
562 | } | ||||
563 | | ||||
564 | noProblem = false; | ||||
565 | return 0; | ||||
566 | | ||||
567 | } |