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