1 /************************************************************************
2  **
3  **  @file   calculator.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   November 15, 2013
6  **
7  **  @brief
8  **  @copyright
9  **  This source code is part of the Valentina project, a pattern making
10  **  program, whose allow create and modeling patterns of clothing.
11  **  Copyright (C) 2013-2015 Valentina project
12  **  <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13  **
14  **  Valentina is free software: you can redistribute it and/or modify
15  **  it under the terms of the GNU General Public License as published by
16  **  the Free Software Foundation, either version 3 of the License, or
17  **  (at your option) any later version.
18  **
19  **  Valentina is distributed in the hope that it will be useful,
20  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  **  GNU General Public License for more details.
23  **
24  **  You should have received a copy of the GNU General Public License
25  **  along with Valentina.  If not, see <http://www.gnu.org/licenses/>.
26  **
27  *************************************************************************/
28 
29 #include "calculator.h"
30 
31 #include <QStaticStringData>
32 #include <QStringData>
33 #include <QStringDataPtr>
34 #include <QStringList>
35 #include <QSharedPointer>
36 #include <QtDebug>
37 
38 #include "../vmisc/def.h"
39 #include "../qmuparser/qmuparsererror.h"
40 #include "variables/vinternalvariable.h"
41 #include "../vmisc/vabstractapplication.h"
42 
43 //---------------------------------------------------------------------------------------------------------------------
44 /**
45  * @brief Calculator class wraper for QMuParser. Make easy initialization math parser.
46  *
47  * This constructor hide initialization variables, operators, character sets.
48  * Use this constuctor for evaluation formula. All formulas must be converted to internal look.
49  * Example:
50  *
51  * const QString formula = VAbstractApplication::VApp()->FormulaFromUser(edit->text());
52  * Calculator *cal = new Calculator(data, patternType);
53  * const qreal result = cal->EvalFormula(data->PlainVariables(), formula);
54  * delete cal;
55  *
56  */
Calculator()57 Calculator::Calculator()
58     : QmuFormulaBase(),
59       m_varsValues(),
60       m_vars(nullptr)
61 {
62     InitCharSets();
63 
64     // Parser doesn't know any variable on this stage. So, we just use variable factory that for each unknown variable
65     // set value to 0.
66     SetVarFactory(VarFactory, this);
67     SetSepForEval();
68 
69     DefineFun(QStringLiteral("warning"), Warning);
70 }
71 
72 //---------------------------------------------------------------------------------------------------------------------
73 /**
74  * @brief eval calculate formula.
75  *
76  * First we try eval expression without adding variables. If it fail, we take tokens from expression and add variables
77  * to parser and try again.
78  *
79  * @param formula string of formula.
80  * @return value of formula.
81  */
EvalFormula(const QHash<QString,QSharedPointer<VInternalVariable>> * vars,const QString & formula)82 qreal Calculator::EvalFormula(const QHash<QString, QSharedPointer<VInternalVariable>> *vars, const QString &formula)
83 {
84     // Converting with locale is much faster in case of single numerical value.
85     QLocale c(QLocale::C);
86     bool ok = false;
87     qreal result = c.toDouble(formula, &ok);
88     if (ok)
89     {
90         return result;
91     }
92 
93     SetSepForEval();//Reset separators options
94     m_vars = vars;
95     SetExpr(formula);
96 
97     m_pTokenReader->IgnoreUndefVar(true);
98     return Eval();
99 }
100 
101 //---------------------------------------------------------------------------------------------------------------------
VarFactory(const QString & a_szName,void * a_pUserData)102 qreal *Calculator::VarFactory(const QString &a_szName, void *a_pUserData)
103 {
104     Q_UNUSED(a_szName)
105     Calculator *calc = static_cast<Calculator *>(a_pUserData);
106 
107     if (calc->m_vars != nullptr && calc->m_vars->contains(a_szName))
108     {
109         QSharedPointer<qreal> val(new qreal(*calc->m_vars->value(a_szName)->GetValue()));
110         calc->m_varsValues.append(val);
111         return val.data();
112     }
113 
114     if (a_szName.startsWith('#'))
115     {
116         QSharedPointer<qreal> val(new qreal(std::numeric_limits<qreal>::quiet_NaN()));
117         calc->m_varsValues.append(val);
118         return val.data();
119     }
120 
121     throw qmu::QmuParserError (qmu::ecUNASSIGNABLE_TOKEN);
122 }
123 
124 //---------------------------------------------------------------------------------------------------------------------
Warning(const QString & warningMsg,qreal value)125 qreal Calculator::Warning(const QString &warningMsg, qreal value)
126 {
127     VAbstractApplication::VApp()->IsPedantic() ? throw qmu::QmuParserWarning(warningMsg) :
128                                                qWarning() << VAbstractApplication::warningMessageSignature + warningMsg;
129 
130     return value;
131 }
132