source: cpp/frams/genetics/fL/fL_matheval.cpp @ 780

Last change on this file since 780 was 780, checked in by Maciej Komosinski, 6 years ago

Added sources for genetic encodings fB, fH, fL

File size: 16.2 KB
Line 
1#include <frams/util/extvalue.h>
2#include <frams/util/sstring.h>
3#include <stack>
4#include "fL_matheval.h"
5
6// Used available operators in MathEvaluation
7
8double madd(double left, double right)
9{
10        return left + right;
11}
12
13double msub(double left, double right)
14{
15        return left - right;
16}
17
18double mmul(double left, double right)
19{
20        return left * right;
21}
22
23double mand(double left, double right)
24{
25        return left && right;
26}
27
28double mor(double left, double right)
29{
30        return left || right;
31}
32
33double mgreater(double left, double right)
34{
35        return left > right;
36}
37
38double mless(double left, double right)
39{
40        return left < right;
41}
42
43double mequal(double left, double right)
44{
45        return left == right;
46}
47
48double mnotequal(double left, double right)
49{
50        return left != right;
51}
52
53double meqless(double left, double right)
54{
55        return left <= right;
56}
57
58double meqgreater(double left, double right)
59{
60        return left >= right;
61}
62
63// Methods for converting evaluation tokens to string
64
65std::string MathEvaluation::Number::toString()
66{
67        return std::string(SString::valueOf(value).c_str());
68}
69
70std::string MathEvaluation::Variable::toString()
71{
72        if (id == -1) return std::string(1, VARIABLEPREFIX) + "t";
73        return std::string(1, VARIABLEPREFIX) + std::to_string(id);
74}
75
76std::string MathEvaluation::Parenthesis::toString()
77{
78        return ptype == ParenthesisType::OPEN ? "(" : ")";
79}
80
81std::string MathEvaluation::Operator::toString()
82{
83        return opname;
84}
85
86void MathEvaluation::Number::operator=(const Number& src)
87{
88        value = src.value;
89}
90
91double MathEvaluation::Operator::execute(double left, double right)
92{
93        return operation(left, right);
94}
95
96void MathEvaluation::registerOperators()
97{
98        // list of available operators in MathEvaluation
99        operators["+"] = new Operator(madd, 2, Associativity::LEFT, "+");
100        operators["-"] = new Operator(msub, 2, Associativity::LEFT, "-");
101        operators["*"] = new Operator(mmul, 3, Associativity::LEFT, "*");
102        operators["&"] = new Operator(mand, 0, Associativity::LEFT, "&");
103        operators["|"] = new Operator(mor, 0, Associativity::LEFT, "|");
104        operators[">"] = new Operator(mgreater, 1, Associativity::LEFT, ">");
105        operators["<"] = new Operator(mless, 1, Associativity::LEFT, "<");
106        operators[">="] = new Operator(meqgreater, 1, Associativity::LEFT, ">=");
107        operators["<="] = new Operator(meqless, 1, Associativity::LEFT, "<=");
108        operators["="] = new Operator(mequal, 1, Associativity::RIGHT, "=");
109        operators["~"] = new Operator(mnotequal, 1, Associativity::RIGHT, "~");
110}
111
112void MathEvaluation::clearPostfix()
113{
114        if (!postfixlist.empty())
115        {
116                for (Token *el : postfixlist)
117                {
118                        // only numbers or parenthesis should be removed - operators and
119                        // variables are removed later during destruction of the
120                        // MathEvaluation object
121                        if (el->type == TokenType::NUMBER ||
122                                el->type == TokenType::PARENTHESIS)
123                                delete el;
124                }
125                postfixlist.clear();
126        }
127}
128
129MathEvaluation::MathEvaluation(int varcount) : varcount(varcount), vars(varcount, NULL)
130{
131        registerOperators();
132        for (int i = 0; i < varcount; i++)
133        {
134                vars[i] = new Variable(i);
135        }
136        t = new Variable(-1);
137        t->value = 0;
138}
139
140MathEvaluation::~MathEvaluation()
141{
142        clearPostfix();
143
144        std::unordered_map<std::string, Operator *>::iterator it;
145        for (it = operators.begin(); it != operators.end(); it++)
146        {
147                delete it->second;
148        }
149        operators.clear();
150
151        for (Variable *var : vars)
152        {
153                delete var;
154        }
155        vars.clear();
156        delete t;
157}
158
159int MathEvaluation::convertString(std::string expression)
160{
161        clearPostfix(); //clear previous objects
162        ExtValue val;
163
164        unsigned int it = 0;
165
166        std::list<Token*> operatorstack; // stores operators for later
167
168        bool canbeunary = true; // used to determine if -/+ operators should be unary
169        while (it < expression.size())
170        {
171                bool parsenumber = false;
172                int oplen = 2; // first method checks if 2-character operators match, then 1-character
173
174                // if following letters describe variable
175                if (expression[it] == VARIABLEPREFIX ||
176                        (canbeunary && strchr("+-", expression[it]) &&
177                        it + 1 < expression.size() &&
178                        expression[it + 1] == VARIABLEPREFIX))
179                {
180                        // determine if before variable there is an unary operator '-'
181                        // if yes, the variable will be negated
182                        double mult = expression[it] == '-' ? -1 : 1;
183                        it += expression[it] == VARIABLEPREFIX ? 1 : 2;
184                        // if there is no space after '$' character, return error
185                        if (it >= expression.size())
186                        {
187                                std::string message = "Evaluation error: Invalid variable '";
188                                message += expression.substr(it) + "'";
189                                logMessage("MathEvaluation", "convertString", LOG_ERROR, message.c_str());
190                                while (!operatorstack.empty())
191                                {
192                                        if (operatorstack.back()->type != TokenType::OPERATOR)
193                                        {
194                                                delete operatorstack.back();
195                                                operatorstack.pop_back();
196                                        }
197                                }
198                                return -1;
199                        }
200
201                        //determine id of variable
202                        const char *ptr = expression[it] == 't' ? expression.c_str() + it + 1 : val.parseNumber(expression.c_str() + it, ExtPType::TInt);
203                        int index = expression[it] == 't' ? -1 : val.getInt();
204                        bool invalidid = false;
205                        // if id is parsed properly
206                        if (ptr) // if current element is number, then add it to RPN
207                        {
208                                // if id is exceeding available amount of variables, then variable
209                                // is invalid
210                                if (index >= varcount)
211                                {
212                                        invalidid = true;
213                                }
214                                else
215                                {
216                                        Variable *v = index == -1 ? t : vars[index];
217                                        // if variable had unary '-', then is multiplied by -1
218                                        // multiplying value stored in variable would change value
219                                        // in every occurrence of variable
220                                        if (mult == -1)
221                                        {
222                                                postfixlist.push_back(new Number(-1));
223                                                postfixlist.push_back(v);
224                                                postfixlist.push_back(operators["*"]);
225                                        }
226                                        // push pointer to a variable in order to make evaluation
227                                        // reusable for other values of variables
228                                        else
229                                        {
230                                                postfixlist.push_back(v);
231                                        }
232                                        int offset = ptr - (expression.c_str() + it);
233                                        it += offset;
234                                        // after variable there cannot be unary operator
235                                        canbeunary = false;
236                                }
237                        }
238                        // if parsing of variable went wrong, then return error message
239                        if (!ptr || invalidid)
240                        {
241                                std::string message = "Evaluation error: Invalid variable '";
242                                message += expression.substr(it) + "'";
243                                logMessage("MathEvaluation", "convertString", LOG_ERROR, message.c_str());
244                                while (!operatorstack.empty())
245                                {
246                                        if (operatorstack.back()->type != TokenType::OPERATOR)
247                                        {
248                                                delete operatorstack.back();
249                                                operatorstack.pop_back();
250                                        }
251                                }
252                                return -1;
253                        }
254                }
255                // if current characters describe operators
256                else if ((it + 1 < expression.size() && operators.find(expression.substr(it, oplen)) != operators.end())
257                        || operators.find(expression.substr(it, --oplen)) != operators.end())
258                {
259                        // if operator is + or - and from context it is known that it is
260                        // unary version of one of those operators, then move control to
261                        // number parsing
262                        if (canbeunary && strchr("+-", expression[it]) &&
263                                it + 1 < expression.size() && isdigit(expression[it + 1]))
264                        {
265                                parsenumber = true;
266                        }
267                        else
268                        {
269                                // otherwise prepare pointer to a given operator
270                                Operator *newop = operators[expression.substr(it, oplen)];
271
272                                bool finished = operatorstack.empty();
273
274                                // before operator will be put onto the operator stack, all operators
275                                // with higher precedence than the current operator should be popped
276                                // from the stack and added to postfix list
277                                while (!finished)
278                                {
279
280                                        Token *curr = operatorstack.back();
281                                        // when token in the operator stack is opened parenthesis,
282                                        // then the process of taking operators from stack stops.
283                                        if (curr->type == TokenType::PARENTHESIS)
284                                        {
285                                                Parenthesis *par = (Parenthesis *)curr;
286                                                if (par->ptype == ParenthesisType::OPEN)
287                                                {
288                                                        finished = true;
289                                                }
290                                        }
291                                        else
292                                        {
293                                                // if current operator in stack is left-associated and
294                                                // its precedence is equal or greater than precedence of
295                                                // new operator than current operator is taken from stack
296                                                // and added to postfixlist. If current operator is
297                                                // right-associated, then its precedence must be strictly
298                                                // higher than new operator precedence
299                                                Operator *op = (Operator *)curr;
300                                                if (newop->assoc == Associativity::LEFT && newop->precedence <= op->precedence)
301                                                {
302                                                        postfixlist.push_back(op);
303                                                        operatorstack.pop_back();
304                                                }
305                                                else if (newop->assoc == Associativity::RIGHT && newop->precedence < op->precedence)
306                                                {
307                                                        postfixlist.push_back(op);
308                                                        operatorstack.pop_back();
309                                                }
310                                                else
311                                                {
312                                                        finished = true;
313                                                }
314                                        }
315                                        finished = finished || operatorstack.empty();
316                                }
317                                // after operator there cannot be unary operator - wrap it in
318                                // parenthesis
319                                canbeunary = false;
320                                // add new operator to stack
321                                operatorstack.push_back(newop);
322                                it += oplen;
323                        }
324                }
325                else if (expression[it] == '(' || expression[it] == ')')
326                {
327                        // if current character is open parenthesis, then add it to
328                        // the operator stack
329                        if (expression[it] == '(')
330                        {
331                                Parenthesis *par = new Parenthesis(ParenthesisType::OPEN);
332                                // after open parenthesis there can be unary operator
333                                canbeunary = true;
334                                operatorstack.push_back(par);
335                        }
336                        else
337                        {
338                                // if current character is closed parenthesis, then all operators
339                                // are popped from stack into postfix list until first open
340                                // parenthesis is found
341                                bool finpop = false;
342                                while (!finpop)
343                                {
344                                        // if there is no more operators in the stack, then parenthesis
345                                        // is mismatched
346                                        if (operatorstack.empty())
347                                        {
348                                                logMessage("MathEvaluation", "convertString", LOG_ERROR, "Evaluation error: Parenthesis mismatch");
349                                                return -1;
350                                        }
351                                        Token *curr = operatorstack.back();
352                                        if (curr->type == TokenType::PARENTHESIS)
353                                        {
354                                                // if corresponding parenthesis is found, then opening
355                                                // parenthesis is deleted and removed from stack - postfix
356                                                // notation does not have parenthesis
357                                                Parenthesis *par = (Parenthesis *)curr;
358                                                if (par->ptype == ParenthesisType::OPEN)
359                                                {
360                                                        delete par;
361                                                        operatorstack.pop_back();
362                                                        finpop = true;
363                                                }
364                                        }
365                                        else
366                                        {
367                                                postfixlist.push_back(curr);
368                                                operatorstack.pop_back();
369                                        }
370                                }
371                                // after closed parenthesis unary operators does not exist
372                                canbeunary = false;
373                        }
374                        it++;
375                }
376                else if (isspace(expression[it])) // all whitespaces are skipped
377                {
378                        it++;
379                }
380                else // if above conditions are not satisfied, then method assumes that
381                {    // characters describe number
382                        parsenumber = true;
383                }
384                if (parsenumber)
385                {
386                        // if parsing went successfully, then push number to postfix list
387                        const char *ptr = val.parseNumber(expression.c_str() + it, ExtPType::TDouble);
388                        if (ptr) // if current element is number, then add it to RPN
389                        {
390                                Number *num = new Number(val.getDouble());
391                                postfixlist.push_back(num);
392                                int offset = ptr - (expression.c_str() + it);
393                                it += offset;
394                                // There are no unary operators after number
395                                canbeunary = false;
396                        }
397                        else
398                        {
399                                // otherwise return error
400                                std::string message = "Evaluation error: Invalid letter '";
401                                message += expression[it];
402                                message += "'";
403                                logMessage("MathEvaluation", "convertString", LOG_ERROR, message.c_str());
404                                while (!operatorstack.empty())
405                                {
406                                        if (operatorstack.back()->type != TokenType::OPERATOR)
407                                        {
408                                                delete operatorstack.back();
409                                                operatorstack.pop_back();
410                                        }
411                                }
412                                return -1;
413                        }
414                }
415        }
416
417        // Pop remaining operators from stack and add them to the postfix list
418        while (!operatorstack.empty())
419        {
420                Token *curr = operatorstack.back();
421                if (curr->type == TokenType::OPERATOR)
422                {
423                        postfixlist.push_back(curr);
424                        operatorstack.pop_back();
425                }
426                else
427                {
428                        // if current token in the stack is open parenthesis, then mismatch
429                        // occured
430                        logMessage("MathEvaluation", "convertString", LOG_ERROR, "Evaluation error: Parenthesis mismatch");
431                        while (!operatorstack.empty())
432                        {
433                                if (operatorstack.back()->type != TokenType::OPERATOR)
434                                {
435                                        delete operatorstack.back();
436                                        operatorstack.pop_back();
437                                }
438                        }
439                        return -1;
440                }
441        }
442
443        return 0;
444}
445
446int MathEvaluation::evaluateRPN(double &res)
447{
448        // stack holds number used during operator execution
449        std::stack<Number *> numberstack;
450
451        for (std::list<Token *>::iterator it = postfixlist.begin(); it != postfixlist.end(); it++)
452        {
453                Token *tok = (*it);
454                // if token is number or variable - add it to number stack
455                if (tok->type == TokenType::NUMBER || tok->type == TokenType::VARIABLE)
456                {
457                        Number *num = new Number(((Number *)tok)->value);
458                        numberstack.push(num);
459                }
460                else
461                {
462                        // the token is an operator
463                        Operator *op = (Operator *)tok;
464                        // if there isn't at least 2 elements in number stack, then return error
465                        if (numberstack.size() < 2)
466                        {
467                                logMessage("MathEvaluation", "convertString", LOG_ERROR, "Evaluation error: The math expression is not complete");
468                                while (!numberstack.empty())
469                                {
470                                        if (numberstack.top()->type == TokenType::NUMBER) delete numberstack.top();
471                                        numberstack.pop();
472                                }
473                                return -1;
474                        }
475                        // otherwise, pop two top elements from number stack and use them
476                        // with current operator
477                        double right = numberstack.top()->value;
478                        if (numberstack.top()->type == TokenType::NUMBER) delete numberstack.top();
479                        numberstack.pop();
480                        double left = numberstack.top()->value;
481                        if (numberstack.top()->type == TokenType::NUMBER) delete numberstack.top();
482                        numberstack.pop();
483                        double result = op->execute(left, right);
484                        Number *newnum = new Number(result);
485                        // Push operation result to number stack
486                        numberstack.push(newnum);
487                }
488        }
489
490        // in the end of processing, only 1 element should be available in number stack.
491        // Otherwise expression was not complete and error will be returned
492        if (numberstack.size() != 1)
493        {
494                logMessage("MathEvaluation", "convertString", LOG_ERROR, "Evaluation error: The math expression is not complete");
495                while (!numberstack.empty())
496                {
497                        if (numberstack.top()->type == TokenType::NUMBER) delete numberstack.top();
498                        numberstack.pop();
499                }
500                return -1;
501        }
502
503        res = numberstack.top()->value;
504        if (numberstack.top()->type == TokenType::NUMBER) delete numberstack.top();
505        numberstack.pop();
506        return 0;
507}
508
509void MathEvaluation::modifyVariable(int i, double val)
510{
511        if (i == -1)
512        {
513                t->value = val;
514        }
515        else
516        {
517                if (!vars[i])
518                {
519                        vars[i] = new Variable(i);
520                }
521                vars[i]->value = val;
522        }
523}
524
525int MathEvaluation::evaluate(std::string expression, double &result)
526{
527        int res = convertString(expression);
528        if (res != 0) return res;
529        return evaluateRPN(result);
530}
531
532int MathEvaluation::RPNToInfix(std::string &result)
533{
534        // stack holds stringified chunk and its precedence
535        std::stack<std::pair<std::string, int>> chunks;
536        // foreach token in postfix list
537        for (Token *tok : postfixlist)
538        {
539                // if token is not an operator, push stringified variable or number
540                // on top of stack
541                if (tok->type != TokenType::OPERATOR)
542                {
543                        // number or variable has no precedence
544                        chunks.push({ tok->toString(), -1 });
545                }
546                else // if token is an operator
547                {
548                        Operator *op = (Operator *)tok;
549                        int prec = op->precedence;
550                        // if there are not at least two stringified chunks in stack, return error
551                        if (chunks.size() < 2)
552                        {
553                                logMessage("MathEvaluation", "RPNToInfix", LOG_ERROR,
554                                        "Could not convert RPN notation to infix notation");
555                                return -1;
556                        }
557                        // pop first chunk
558                        std::pair<std::string, int> right = chunks.top();
559                        chunks.pop();
560                        if (right.second != -1 && right.second < op->precedence)
561                        {
562                                // if chunk on the right of operator has lower precedence than
563                                // this operator, then wrap it with parenthesis
564                                right.first = std::string(1, '(') + right.first + std::string(1, ')');
565                        }
566                        else if (right.second >= op->precedence)
567                        {
568                                prec = right.second;
569                        }
570                        std::pair<std::string, int> left = chunks.top();
571                        chunks.pop();
572                        if (left.second != -1 && left.second < op->precedence)
573                        {
574                                // if chunk on the left of operator has lower precedence than
575                                // this operator, then wrap it with parenthesis
576                                left.first = std::string(1, '(') + left.first + std::string(1, ')');
577                        }
578                        std::string res = left.first + op->toString() + right.first;
579                        // add summed chunk
580                        chunks.push({ res, prec });
581                }
582        }
583        // if there is more than one chunk then return error
584        if (chunks.size() != 1)
585        {
586                logMessage("MathEvaluation", "RPNToInfix", LOG_ERROR,
587                        "Could not convert RPN notation to infix notation");
588                return -1;
589        }
590        result = chunks.top().first;
591        chunks.pop();
592        return 0;
593}
Note: See TracBrowser for help on using the repository browser.