Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_num_parser.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "kis_num_parser.h"
8
9#include <qnumeric.h> // for qIsNaN
10#include <qmath.h>
11#include <QVector>
12#include <QRegularExpression>
13#include <QStringList>
14#include <QVariant>
15#include <QLocale>
16
17#include <iostream>
18
19using namespace std;
20
21const QVector<char> opLevel1 = {'+', '-'};
22const QVector<char> opLevel2 = {'*', '/'};
23
24const QStringList supportedFuncs = {"", "cos", "sin", "tan", "acos", "asin", "atan", "exp", "ln", "log10", "abs"};
25
26const QRegularExpression funcExpr("^(-)?([a-zA-Z]*[0-9]*)?\\((.+)\\)$");
27const QRegularExpression numberExpr("^(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)$");
28
29const QRegularExpression funcExprInteger("^(-)?\\((.+)\\)$");
30const QRegularExpression integerExpr("^(-)?([0-9]+)$");
31
32//double functions
33double treatFuncs(QString const& expr, bool & noProblem);
34double treatLevel1(QString const& expr, bool & noProblem);
35double treatLevel2(QString const& expr, bool & noProblem);
36double treatLevel3(QString const& expr, bool & noProblem);
37
38//int functions
39double treatLevel1Int(QString const& expr, bool & noProblem);
40double treatLevel2Int(QString const& expr, bool & noProblem);
41double treatFuncsInt(QString const& expr, bool & noProblem);
42
44
50double parseSimpleMathExpr(const QString &expr, bool *noProblem)
51{
52
53 bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided).
54
55 //then go down each 3 levels of operation priority.
56 if (noProblem != nullptr) {
57 return treatLevel1(expr, *noProblem);
58 }
59
60 return treatLevel1(expr, ok);
61
62}
63
69int parseIntegerMathExpr(QString const& expr, bool* noProblem)
70{
71
72 bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided).
73
74 if (noProblem != nullptr) {
75 return qRound(treatLevel1Int(expr, *noProblem));
76 }
77
78 return qRound(treatLevel1Int(expr, ok));
79
80}
81
82} //namespace KisNumericParser.
83
84
85//intermediate functions
86
94inline QString extractSubExprLevel1(QString & expr, char & nextOp, bool & noProblem){
95
96 QString ret;
97
98 int subCount = 0;
99
100 bool lastMetIsNumber = false;
101
102 for(int i = 0; i < expr.size(); i++){
103
104 if (expr.at(i) == '(') {
105 subCount++;
106 }
107
108 if (expr.at(i) == ')') {
109 subCount--;
110 }
111
112 if (subCount < 0) {
113 noProblem = false;
114 return ret;
115 }
116
117 if(i == expr.size()-1 && subCount == 0){
118 ret = expr;
119 expr.clear();
120 break;
121 }
122
123 if( (expr.at(i) == '+' || expr.at(i) == '-') &&
124 subCount == 0) {
125
126 if (expr.at(i) == '-' &&
127 i < expr.size()-1) {
128
129 bool cond = !expr.at(i+1).isSpace();
130
131 if (cond && !lastMetIsNumber) {
132 continue;
133 }
134
135 }
136
137 ret = expr.mid(0, i).trimmed();
138 nextOp = expr.at(i).toLatin1();
139 expr = expr.mid(i+1);
140 break;
141
142 }
143
144 if (expr.at(i).isDigit()) {
145 lastMetIsNumber = true;
146 } else if (expr.at(i) != '.' &&
147 !expr.at(i).isSpace()) {
148 lastMetIsNumber = false;
149 }
150
151 }
152
153 noProblem = true;
154 return ret;
155}
156
157
165inline QString extractSubExprLevel2(QString & expr, char & nextOp, bool & noProblem){
166
167 QString ret;
168
169 int subCount = 0;
170
171 for(int i = 0; i < expr.size(); i++){
172
173 if (expr.at(i) == '(') {
174 subCount++;
175 }
176
177 if (expr.at(i) == ')') {
178 subCount--;
179 }
180
181 if (subCount < 0) {
182 noProblem = false;
183 return ret;
184 }
185
186 if(i == expr.size()-1 && subCount == 0){
187 ret = expr;
188 expr.clear();
189 break;
190 }
191
192 if( (expr.at(i) == '*' || expr.at(i) == '/') &&
193 subCount == 0) {
194
195 ret = expr.mid(0, i).trimmed();
196 nextOp = expr.at(i).toLatin1();
197 expr = expr.mid(i+1);
198 break;
199
200 }
201
202 }
203
204 noProblem = true;
205 return ret;
206}
207
214double treatLevel1(const QString &expr, bool & noProblem)
215{
216
217 noProblem = true;
218
219 QString exprDestructible = expr;
220
221 char nextOp = '+';
222 double result = 0.0;
223
224 while (!exprDestructible.isEmpty()) {
225
226 double sign = (nextOp == '-') ? -1 : 1;
227 QString part = extractSubExprLevel1(exprDestructible, nextOp, noProblem);
228
229 if (!noProblem) {
230 return 0.0;
231 }
232
233 if (sign > 0) {
234 result += treatLevel2(part, noProblem);
235 } else {
236 result -= treatLevel2(part, noProblem);
237 }
238
239 if(!noProblem){
240 return 0.0;
241 }
242 }
243
244 return result;
245
246}
247
256double treatLevel2(QString const& expr, bool & noProblem)
257{
258
259 noProblem = true;
260
261 QString exprDestructible = expr;
262
263 char nextOp = '*';
264
265 QString part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
266
267 double result = treatLevel3(part, noProblem);
268
269 while (!exprDestructible.isEmpty()) {
270
271 if (!noProblem) {
272 return 0.0;
273 }
274
275 bool needToMultiply = (nextOp == '*');
276 part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
277
278 if (!noProblem) {
279 return 0.0;
280 }
281
282 if (needToMultiply) {
283 result *= treatLevel3(part, noProblem);
284 } else {
285 result /= treatLevel3(part, noProblem);
286 }
287 }
288
289 return result;
290}
291
300double treatLevel3(const QString &expr, bool & noProblem)
301{
302
303 noProblem = true;
304
305 int indexPower = -1;
306 int indexCount = 0;
307 int subLevels = 0;
308
309 for (int i = 0; i < expr.size(); i++) {
310 if (expr.at(i) == '(') {
311 subLevels++;
312 } else if(expr.at(i) == ')') {
313 subLevels--;
314 if (subLevels < 0) {
315 noProblem = false;
316 return 0.0;
317 }
318 } else if (expr.at(i) == '^') {
319 if (subLevels == 0) {
320 indexPower = i;
321 indexCount++;
322 }
323 }
324 }
325
326 if (indexCount > 1 || indexPower + 1 >= expr.size()) {
327 noProblem = false;
328 return 0.0;
329 }
330
331 if (indexPower > -1) {
332
333 QStringList subExprs;
334 subExprs << expr.mid(0,indexPower);
335 subExprs << expr.mid(indexPower+1);
336
337 bool noProb1 = true;
338 bool noProb2 = true;
339
340 double base = treatFuncs(subExprs[0], noProb1);
341 double power = treatFuncs(subExprs[1], noProb2);
342
343 return qPow(base, power);
344
345 } else {
346 return treatFuncs(expr, noProblem);
347 }
348
349 noProblem = false;
350 return 0.0;
351
352}
353
362double treatFuncs(QString const& expr, bool & noProblem)
363{
364
365 noProblem = true;
366
367 QRegularExpression funcExp = funcExpr; //copy the expression in the current execution stack, to avoid errors for example when multiple thread call this function.
368 QRegularExpression numExp = numberExpr;
369 QRegularExpressionMatch match;
370
371 if (expr.trimmed().contains(funcExp, &match)) {
372
373 int sign = match.captured(1).isEmpty() ? 1 : -1;
374 QString func = match.captured(2).toLower();
375 QString subExpr = match.captured(3);
376
377 double val = treatLevel1(subExpr, noProblem);
378
379 if (!noProblem) {
380 return 0.0;
381 }
382
383 if (func.isEmpty()) {
384 return sign*val;
385 }
386
387 if (!supportedFuncs.contains(func)) {
388 noProblem = false;
389 return 0.0;
390 }
391
392 //trigonometry is done in degree
393 if (func == "cos") {
394 val = qCos(val/180*qAcos(-1));
395 } else if (func == "sin") {
396 val = qSin(val/180*qAcos(-1));
397 } else if (func == "tan") {
398 val = qTan(val/180*qAcos(-1));
399 } else if(func == "acos") {
400 val = qAcos(val)*180/qAcos(-1);
401 } else if (func == "asin") {
402 val = qAsin(val)*180/qAcos(-1);
403 } else if (func == "atan") {
404 val = qAtan(val)*180/qAcos(-1);
405 } else if (func == "exp") {
406 val = qExp(val);
407 } else if (func == "ln") {
408 val = qLn(val);
409 } else if (func == "log10") {
410 val = qLn(val)/qLn(10.0);
411 } else if (func == "abs") {
412 val = qAbs(val);
413 }
414
415 return sign*val;
416 } else if(expr.trimmed().contains(numExp)) {
417 return expr.toDouble(&noProblem);
418 }
419
420 double val = QLocale().toDouble(expr, &noProblem);
421
422 if(noProblem) {
423 return val;
424 }
425
426 noProblem = false;
427 return 0.0;
428
429}
430
431//int functions
438double treatLevel1Int(QString const& expr, bool & noProblem)
439{
440
441 noProblem = true;
442
443 QString exprDestructible = expr;
444
445 char nextOp = '+';
446 double result = 0.0;
447
448 while (!exprDestructible.isEmpty()) {
449
450 double sign = (nextOp == '-') ? -1 : 1;
451 QString part = extractSubExprLevel1(exprDestructible, nextOp, noProblem);
452
453 if( !noProblem) {
454 return 0.0;
455 }
456
457 if (sign > 0) {
458 result += treatLevel2Int(part, noProblem);
459 } else {
460 result -= treatLevel2Int(part, noProblem);
461 }
462
463 if(!noProblem){
464 return 0.0;
465 }
466 }
467
468 return result;
469
470}
471
480double treatLevel2Int(const QString &expr, bool &noProblem)
481{
482
483 noProblem = true;
484
485 QString exprDestructible = expr;
486
487 char nextOp = '*';
488
489 QString part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
490
491 double result = treatFuncsInt(part, noProblem);
492
493 while (!exprDestructible.isEmpty()) {
494
495 if (!noProblem) {
496 return 0.0;
497 }
498
499 bool needToMultiply = (nextOp == '*');
500 part = extractSubExprLevel2(exprDestructible, nextOp, noProblem);
501
502 if (!noProblem) {
503 return 0.0;
504 }
505
506 if (needToMultiply) {
507 result *= treatFuncsInt(part, noProblem);
508 } else {
509
510 double val = treatFuncsInt(part, noProblem);
511
512 if(std::isinf(result/val) || qIsNaN(result/val)){
513 noProblem = false;
514 return 0.0;
515 }
516
517 result /= val;
518 }
519 }
520
521 return result;
522
523}
524
533double treatFuncsInt(QString const& expr, bool & noProblem)
534{
535
536 noProblem = true;
537
538 QRegularExpression funcExpInteger = funcExprInteger;
539 QRegularExpression numberExp = numberExpr;
540 QRegularExpressionMatch match;
541
542 if (expr.trimmed().contains(funcExpInteger, &match)) {
543
544 int sign = match.captured(1).isEmpty() ? 1 : -1;
545 QString subExpr = match.captured(2);
546
547 double val = treatLevel1Int(subExpr, noProblem);
548
549 if (!noProblem) {
550 return 0;
551 }
552
553 return sign*val;
554
555 } else if(expr.trimmed().contains(numberExp)) {
556 double value = QVariant(expr).toDouble(&noProblem);
557 return value;
558 }
559
560 noProblem = false;
561 return 0;
562
563}
float value(const T *src, size_t ch)
quint64 part(quint64 n1, quint64 n2, int p)
const QVector< char > opLevel2
const QVector< char > opLevel1
QString extractSubExprLevel2(QString &expr, char &nextOp, bool &noProblem)
extractSubExprLevel2 extract from an expression the part of an expression that need to be treated rec...
double treatFuncs(QString const &expr, bool &noProblem)
treatFuncs treat the last level of recursion: parenthesis and functions.
const QStringList supportedFuncs
QString extractSubExprLevel1(QString &expr, char &nextOp, bool &noProblem)
extractSubExprLevel1 extract from an expression the part of an expression that need to be treated rec...
double treatFuncsInt(QString const &expr, bool &noProblem)
treatFuncs treat the last level of recursion: parenthesis
const QRegularExpression funcExprInteger("^(-)?\\‍((.+)\\‍)$")
double treatLevel1(QString const &expr, bool &noProblem)
treatLevel1 treat an expression at the first level of recursion.
const QRegularExpression integerExpr("^(-)?([0-9]+)$")
const QRegularExpression numberExpr("^(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)$")
double treatLevel3(QString const &expr, bool &noProblem)
treatLevel3 treat a subexpression at the third level of recursion.
double treatLevel1Int(QString const &expr, bool &noProblem)
treatLevel1 treat an expression at the first level of recursion.
const QRegularExpression funcExpr("^(-)?([a-zA-Z]*[0-9]*)?\\‍((.+)\\‍)$")
double treatLevel2Int(QString const &expr, bool &noProblem)
treatLevel2 treat a subexpression at the second level of recursion.
double treatLevel2(QString const &expr, bool &noProblem)
treatLevel2 treat a subexpression at the second level of recursion.
the namespace contains functions to transform math expression written as QString in numbers.
int parseIntegerMathExpr(QString const &expr, bool *noProblem)
parse an expression to an int.
double parseSimpleMathExpr(const QString &expr, bool *noProblem)
parse an expression to a double.