File indexing completed on 2024-05-12 16:02:27

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_num_parser.h"
0008 
0009 #include <qnumeric.h> // for qIsNaN
0010 #include <qmath.h>
0011 #include <QVector>
0012 #include <QRegExp>
0013 #include <QStringList>
0014 #include <QVariant>
0015 #include <QLocale>
0016 
0017 #include <iostream>
0018 
0019 using namespace std;
0020 
0021 const QVector<char> opLevel1 = {'+', '-'};
0022 const QVector<char> opLevel2 = {'*', '/'};
0023 
0024 const QStringList supportedFuncs = {"", "cos", "sin", "tan", "acos", "asin", "atan", "exp", "ln", "log10", "abs"};
0025 
0026 const QRegExp funcExpr("(-)?([a-zA-Z]*[0-9]*)?\\((.+)\\)");
0027 const QRegExp numberExpr("(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)");
0028 
0029 const QRegExp funcExprInteger("(-)?\\((.+)\\)");
0030 const QRegExp integerExpr("(-)?([0-9]+)");
0031 
0032 //double functions
0033 double treatFuncs(QString const& expr, bool & noProblem);
0034 double treatLevel1(QString const& expr, bool & noProblem);
0035 double treatLevel2(QString const& expr, bool & noProblem);
0036 double treatLevel3(QString const& expr, bool & noProblem);
0037 
0038 //int functions
0039 double treatLevel1Int(QString const& expr, bool & noProblem);
0040 double treatLevel2Int(QString const& expr, bool & noProblem);
0041 double treatFuncsInt(QString const& expr, bool & noProblem);
0042 
0043 namespace KisNumericParser {
0044 
0045 /*!
0046  * \param expr the expression to parse
0047  * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise.
0048  * \return the numerical value the expression eval to (or 0 in case of error).
0049  */
0050 double parseSimpleMathExpr(const QString &expr, bool *noProblem)
0051 {
0052 
0053     bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided).
0054 
0055     //then go down each 3 levels of operation priority.
0056     if (noProblem != nullptr) {
0057         return treatLevel1(expr, *noProblem);
0058     }
0059 
0060     return treatLevel1(expr, ok);
0061 
0062 }
0063 
0064 /*!
0065  * \param expr the expression to parse
0066  * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise.
0067  * \return the numerical value the expression eval to (or 0 in case of error).
0068  */
0069 int parseIntegerMathExpr(QString const& expr, bool* noProblem)
0070 {
0071 
0072     bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided).
0073 
0074     if (noProblem != nullptr) {
0075         return qRound(treatLevel1Int(expr, *noProblem));
0076     }
0077 
0078     return qRound(treatLevel1Int(expr, ok));
0079 
0080 }
0081 
0082 } //namespace KisNumericParser.
0083 
0084 
0085 //intermediate functions
0086 
0087 /*!
0088  * \brief extractSubExprLevel1 extract from an expression the part of an expression that need to be treated recursively before computing level 1 operations (+, -).
0089  * \param expr The expression to treat, the part returned will be removed.
0090  * \param nextOp This reference, in case of success, will hold the first level operation identified as separator ('+' or '-')
0091  * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise.
0092  * \return The first part of the expression that doesn't contain first level operations not nested within parenthesis.
0093  */
0094 inline QString extractSubExprLevel1(QString & expr, char & nextOp, bool & noProblem){
0095 
0096     QString ret;
0097 
0098     int subCount = 0;
0099 
0100     bool lastMetIsNumber = false;
0101 
0102     for(int i = 0; i < expr.size(); i++){
0103 
0104     if (expr.at(i) == '(') {
0105         subCount++;
0106     }
0107 
0108     if (expr.at(i) == ')') {
0109         subCount--;
0110     }
0111 
0112     if (subCount < 0) {
0113         noProblem = false;
0114         return ret;
0115     }
0116 
0117     if(i == expr.size()-1 && subCount == 0){
0118         ret = expr;
0119         expr.clear();
0120         break;
0121     }
0122 
0123     if( (expr.at(i) == '+' || expr.at(i) == '-') &&
0124             subCount == 0) {
0125 
0126         if (expr.at(i) == '-' &&
0127                 i < expr.size()-1) {
0128 
0129             bool cond = !expr.at(i+1).isSpace();
0130 
0131             if (cond && !lastMetIsNumber) {
0132                 continue;
0133             }
0134 
0135         }
0136 
0137         ret = expr.mid(0, i).trimmed();
0138         nextOp = expr.at(i).toLatin1();
0139         expr = expr.mid(i+1);
0140         break;
0141 
0142     }
0143 
0144     if (expr.at(i).isDigit()) {
0145         lastMetIsNumber = true;
0146     } else if (expr.at(i) != '.' &&
0147               !expr.at(i).isSpace()) {
0148         lastMetIsNumber = false;
0149     }
0150 
0151     }
0152 
0153     noProblem = true;
0154     return ret;
0155 }
0156 
0157 
0158 /*!
0159  * \brief extractSubExprLevel2 extract from an expression the part of an expression that need to be treated recursively before computing level 2 operations (*, /).
0160  * \param expr The expression to treat, the part returned will be removed.
0161  * \param nextOp This reference, in case of success, will hold the first level operation identified as separator ('*' or '/')
0162  * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise.
0163  * \return The first part of the expression that doesn't contain second level operations not nested within parenthesis.
0164  */
0165 inline QString extractSubExprLevel2(QString & expr, char & nextOp, bool & noProblem){
0166 
0167     QString ret;
0168 
0169     int subCount = 0;
0170 
0171     for(int i = 0; i < expr.size(); i++){
0172 
0173     if (expr.at(i) == '(') {
0174         subCount++;
0175     }
0176 
0177     if (expr.at(i) == ')') {
0178         subCount--;
0179     }
0180 
0181     if (subCount < 0) {
0182         noProblem = false;
0183         return ret;
0184     }
0185 
0186     if(i == expr.size()-1 && subCount == 0){
0187         ret = expr;
0188         expr.clear();
0189         break;
0190     }
0191 
0192     if( (expr.at(i) == '*' || expr.at(i) == '/') &&
0193             subCount == 0) {
0194 
0195         ret = expr.mid(0, i).trimmed();
0196         nextOp = expr.at(i).toLatin1();
0197         expr = expr.mid(i+1);
0198         break;
0199 
0200     }
0201 
0202     }
0203 
0204     noProblem = true;
0205     return ret;
0206 }
0207 
0208 /*!
0209  * \brief treatLevel1 treat an expression at the first level of recursion.
0210  * \param expr The expression to treat.
0211  * \param noProblem A reference to a bool set to true if no problem happened, false otherwise.
0212  * \return The value of the parsed expression or subexpression or 0 in case of error.
0213  */
0214 double treatLevel1(const QString &expr, bool & noProblem)
0215 {
0216 
0217     noProblem = true;
0218 
0219     QString exprDestructable = expr;
0220 
0221     char nextOp = '+';
0222     double result = 0.0;
0223 
0224     while (!exprDestructable.isEmpty()) {
0225 
0226         double sign = (nextOp == '-') ? -1 : 1;
0227         QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem);
0228 
0229         if (!noProblem) {
0230         return 0.0;
0231         }
0232 
0233         if (sign > 0) {
0234         result += treatLevel2(part, noProblem);
0235         } else {
0236         result -= treatLevel2(part, noProblem);
0237         }
0238 
0239         if(!noProblem){
0240         return 0.0;
0241         }
0242     }
0243 
0244     return result;
0245 
0246 }
0247 
0248 /*!
0249  * \brief treatLevel2 treat a subexpression at the second level of recursion.
0250  * \param expr The subexpression to treat.
0251  * \param noProblem A reference to a bool set to true if no problem happened, false otherwise.
0252  * \return The value of the parsed subexpression or 0 in case of error.
0253  *
0254  * The expression should not contain first level operations not nested in parenthesis.
0255  */
0256 double treatLevel2(QString const& expr, bool & noProblem)
0257 {
0258 
0259     noProblem = true;
0260 
0261     QString exprDestructable = expr;
0262 
0263     char nextOp = '*';
0264 
0265     QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem);
0266 
0267     double result = treatLevel3(part, noProblem);
0268 
0269     while (!exprDestructable.isEmpty()) {
0270 
0271         if (!noProblem) {
0272         return 0.0;
0273         }
0274 
0275         bool needToMultiply = (nextOp == '*');
0276         part = extractSubExprLevel2(exprDestructable, nextOp, noProblem);
0277 
0278         if (!noProblem) {
0279         return 0.0;
0280         }
0281 
0282         if (needToMultiply) {
0283         result *= treatLevel3(part, noProblem);
0284         } else {
0285         result /= treatLevel3(part, noProblem);
0286         }
0287     }
0288 
0289     return result;
0290 }
0291 
0292 /*!
0293  * \brief treatLevel3 treat a subexpression at the third level of recursion.
0294  * \param expr The subexpression to treat.
0295  * \param noProblem A reference to a bool set to true if no problem happened, false otherwise.
0296  * \return The value of the parsed subexpression or 0 in case of error.
0297  *
0298  * The expression should not contain first or second level operations not nested in parenthesis.
0299  */
0300 double treatLevel3(const QString &expr, bool & noProblem)
0301 {
0302 
0303     noProblem = true;
0304 
0305     int indexPower = -1;
0306     int indexCount = 0;
0307     int subLevels = 0;
0308 
0309     for (int i = 0; i < expr.size(); i++) {
0310         if (expr.at(i) == '(') {
0311             subLevels++;
0312         } else if(expr.at(i) == ')') {
0313             subLevels--;
0314             if (subLevels < 0) {
0315                 noProblem = false;
0316                 return 0.0;
0317             }
0318         } else if (expr.at(i) == '^') {
0319             if (subLevels == 0) {
0320                 indexPower = i;
0321                 indexCount++;
0322             }
0323         }
0324     }
0325 
0326     if (indexCount > 1 || indexPower + 1 >= expr.size()) {
0327         noProblem = false;
0328         return 0.0;
0329     }
0330 
0331     if (indexPower > -1) {
0332 
0333         QStringList subExprs;
0334         subExprs << expr.mid(0,indexPower);
0335         subExprs << expr.mid(indexPower+1);
0336 
0337         bool noProb1 = true;
0338         bool noProb2 = true;
0339 
0340         double base = treatFuncs(subExprs[0], noProb1);
0341         double power = treatFuncs(subExprs[1], noProb2);
0342 
0343         return qPow(base, power);
0344 
0345     } else {
0346         return treatFuncs(expr, noProblem);
0347     }
0348 
0349     noProblem = false;
0350     return 0.0;
0351 
0352 }
0353 
0354 /*!
0355  * \brief treatFuncs treat the last level of recursion: parenthesis and functions.
0356  * \param expr The expression to parse.
0357  * \param noProblem A reference to a bool set to true if no problem happened, false otherwise.
0358  * \return The value of the parsed subexpression or 0 in case of error.
0359  *
0360  * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function.
0361  */
0362 double treatFuncs(QString const& expr, bool & noProblem)
0363 {
0364 
0365     noProblem = true;
0366 
0367     QRegExp funcExp = funcExpr; //copy the expression in the current execution stack, to avoid errors for example when multiple thread call this function.
0368     QRegExp numExp = numberExpr;
0369 
0370     if (funcExp.exactMatch(expr.trimmed())) {
0371 
0372         int sign = funcExp.capturedTexts()[1].isEmpty() ? 1 : -1;
0373         QString func = funcExp.capturedTexts()[2].toLower();
0374         QString subExpr = funcExp.capturedTexts()[3];
0375 
0376         double val = treatLevel1(subExpr, noProblem);
0377 
0378         if (!noProblem) {
0379             return 0.0;
0380         }
0381 
0382         if (func.isEmpty()) {
0383             return sign*val;
0384         }
0385 
0386         if (!supportedFuncs.contains(func)) {
0387             noProblem = false;
0388             return 0.0;
0389         }
0390 
0391         //trigonometry is done in degree
0392         if (func == "cos") {
0393             val = qCos(val/180*qAcos(-1));
0394         } else if (func == "sin") {
0395             val = qSin(val/180*qAcos(-1));
0396         } else if (func == "tan") {
0397             val = qTan(val/180*qAcos(-1));
0398         } else if(func == "acos") {
0399             val = qAcos(val)*180/qAcos(-1);
0400         } else if (func == "asin") {
0401             val = qAsin(val)*180/qAcos(-1);
0402         } else if (func == "atan") {
0403             val = qAtan(val)*180/qAcos(-1);
0404         } else if (func == "exp") {
0405             val = qExp(val);
0406         } else if (func == "ln") {
0407             val = qLn(val);
0408         } else if (func == "log10") {
0409             val = qLn(val)/qLn(10.0);
0410         } else if (func == "abs") {
0411             val = qAbs(val);
0412         }
0413 
0414         return sign*val;
0415     } else if(numExp.exactMatch(expr.trimmed())) {
0416         return expr.toDouble(&noProblem);
0417     }
0418 
0419     double val = QLocale().toDouble(expr, &noProblem);
0420 
0421     if(noProblem) {
0422         return val;
0423     }
0424 
0425     noProblem = false;
0426     return 0.0;
0427 
0428 }
0429 
0430 //int functions
0431 /*!
0432  * \brief treatLevel1 treat an expression at the first level of recursion.
0433  * \param expr The expression to treat.
0434  * \param noProblem A reference to a bool set to true if no problem happened, false otherwise.
0435  * \return The value of the parsed expression or subexpression or 0 in case of error.
0436  */
0437 double treatLevel1Int(QString const& expr, bool & noProblem)
0438 {
0439 
0440     noProblem = true;
0441 
0442     QString exprDestructable = expr;
0443 
0444     char nextOp = '+';
0445     double result = 0.0;
0446 
0447     while (!exprDestructable.isEmpty()) {
0448 
0449     double sign = (nextOp == '-') ? -1 : 1;
0450     QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem);
0451 
0452     if( !noProblem) {
0453         return 0.0;
0454     }
0455 
0456     if (sign > 0) {
0457         result += treatLevel2Int(part, noProblem);
0458     } else {
0459         result -= treatLevel2Int(part, noProblem);
0460     }
0461 
0462     if(!noProblem){
0463         return 0.0;
0464     }
0465     }
0466 
0467     return result;
0468 
0469 }
0470 
0471 /*!
0472  * \brief treatLevel2 treat a subexpression at the second level of recursion.
0473  * \param expr The subexpression to treat.
0474  * \param noProblem A reference to a bool set to true if no problem happened, false otherwise.
0475  * \return The value of the parsed subexpression or 0 in case of error.
0476  *
0477  * The expression should not contain first level operations not nested in parenthesis.
0478  */
0479 double treatLevel2Int(const QString &expr, bool &noProblem)
0480 {
0481 
0482     noProblem = true;
0483 
0484     QString exprDestructable = expr;
0485 
0486     char nextOp = '*';
0487 
0488     QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem);
0489 
0490     double result = treatFuncsInt(part, noProblem);
0491 
0492     while (!exprDestructable.isEmpty()) {
0493 
0494     if (!noProblem) {
0495         return 0.0;
0496     }
0497 
0498     bool needToMultiply = (nextOp == '*');
0499     part = extractSubExprLevel2(exprDestructable, nextOp, noProblem);
0500 
0501     if (!noProblem) {
0502         return 0.0;
0503     }
0504 
0505     if (needToMultiply) {
0506         result *= treatFuncsInt(part, noProblem);
0507     } else {
0508 
0509         double val = treatFuncsInt(part, noProblem);
0510 
0511         if(std::isinf(result/val) || qIsNaN(result/val)){
0512         noProblem = false;
0513         return 0.0;
0514         }
0515 
0516         result /= val;
0517     }
0518     }
0519 
0520     return result;
0521 
0522 }
0523 
0524 /*!
0525  * \brief treatFuncs treat the last level of recursion: parenthesis
0526  * \param expr The expression to parse.
0527  * \param noProblem A reference to a bool set to true if no problem happened, false otherwise.
0528  * \return The value of the parsed subexpression or 0 in case of error.
0529  *
0530  * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function.
0531  */
0532 double treatFuncsInt(QString const& expr, bool & noProblem)
0533 {
0534 
0535     noProblem = true;
0536 
0537     QRegExp funcExpInteger = funcExprInteger;
0538     QRegExp integerExp = integerExpr;
0539     QRegExp numberExp = numberExpr;
0540 
0541     if (funcExpInteger.exactMatch(expr.trimmed())) {
0542 
0543         int sign = funcExpInteger.capturedTexts()[1].isEmpty() ? 1 : -1;
0544         QString subExpr = funcExpInteger.capturedTexts()[2];
0545 
0546         double val = treatLevel1Int(subExpr, noProblem);
0547 
0548         if (!noProblem) {
0549             return 0;
0550         }
0551 
0552         return sign*val;
0553 
0554     } else if(numberExp.exactMatch(expr.trimmed())) {
0555         double value = QVariant(expr).toDouble(&noProblem);
0556         return value;
0557     }
0558 
0559     noProblem = false;
0560     return 0;
0561 
0562 }