/src/muparser/src/muParser.cpp
Line | Count | Source |
1 | | /* |
2 | | |
3 | | _____ __ _____________ _______ ______ ___________ |
4 | | / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ |
5 | | | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ |
6 | | |__|_| /____/| __(____ /__| /____ >\___ >__| |
7 | | \/ |__| \/ \/ \/ |
8 | | Copyright (C) 2004 - 2022 Ingo Berg |
9 | | |
10 | | Redistribution and use in source and binary forms, with or without modification, are permitted |
11 | | provided that the following conditions are met: |
12 | | |
13 | | * Redistributions of source code must retain the above copyright notice, this list of |
14 | | conditions and the following disclaimer. |
15 | | * Redistributions in binary form must reproduce the above copyright notice, this list of |
16 | | conditions and the following disclaimer in the documentation and/or other materials provided |
17 | | with the distribution. |
18 | | |
19 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR |
20 | | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
21 | | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
22 | | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
25 | | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
26 | | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | | */ |
28 | | |
29 | | #include "muParser.h" |
30 | | #include "muParserTemplateMagic.h" |
31 | | |
32 | | //--- Standard includes ------------------------------------------------------------------------ |
33 | | #include <cmath> |
34 | | #include <algorithm> |
35 | | #include <numeric> |
36 | | #include <cstdlib> |
37 | | |
38 | | |
39 | | using namespace std; |
40 | | |
41 | | /** \file |
42 | | \brief Implementation of the standard floating point parser. |
43 | | */ |
44 | | |
45 | | |
46 | | |
47 | | /** \brief Namespace for mathematical applications. */ |
48 | | namespace mu |
49 | | { |
50 | | //--------------------------------------------------------------------------- |
51 | | /** \brief Default value recognition callback. |
52 | | \param [in] a_szExpr Pointer to the expression |
53 | | \param [in, out] a_iPos Pointer to an index storing the current position within the expression |
54 | | \param [out] a_fVal Pointer where the value should be stored in case one is found. |
55 | | \return 1 if a value was found 0 otherwise. |
56 | | */ |
57 | | int Parser::IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) |
58 | 571k | { |
59 | | // There is an issue with libc++ where it creates an error if a double value is followed by a character which |
60 | | // is the case when using postfix operators. |
61 | | // |
62 | | // http://cplusplus.github.io/LWG/lwg-defects.html#2381 |
63 | | // |
64 | | // This happens only with libc++, not with libstdc++ (Gnu C++ standard library) |
65 | | // It seems that Macs are using libc++. This is causing #123. The fix below will fix #123 |
66 | | // but is will break localization support and cause #136. |
67 | | |
68 | | // I'm disabling this fix. For systems using libc++ you must put a space between floating point numbers and postfix operators. |
69 | | #if defined(__APPLE__) && defined(NEVERTRUE) |
70 | | try |
71 | | { |
72 | | std::size_t charsProcessed; |
73 | | value_type fVal = static_cast<value_type>(std::stod(string_type(a_szExpr), &charsProcessed)); |
74 | | if (charsProcessed == 0) |
75 | | return 0; |
76 | | |
77 | | *a_iPos += (int)charsProcessed; |
78 | | *a_fVal = fVal; |
79 | | return 1; |
80 | | } |
81 | | catch (...) |
82 | | { |
83 | | return 0; |
84 | | } |
85 | | #else |
86 | 571k | value_type fVal(0); |
87 | | |
88 | 571k | stringstream_type stream(a_szExpr); |
89 | 571k | stream.imbue(Parser::s_locale); |
90 | 571k | stream >> fVal; |
91 | 571k | stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading |
92 | | |
93 | 571k | if (iEnd == (stringstream_type::pos_type) - 1) |
94 | 7.34k | return 0; |
95 | | |
96 | 564k | *a_iPos += (int)iEnd; |
97 | 564k | *a_fVal = fVal; |
98 | 564k | return 1; |
99 | 571k | #endif |
100 | 571k | } |
101 | | |
102 | | |
103 | | //--------------------------------------------------------------------------- |
104 | | /** \brief Constructor. |
105 | | |
106 | | Call ParserBase class constructor and trigger Function, Operator and Constant initialization. |
107 | | */ |
108 | | Parser::Parser() |
109 | 3.51k | :ParserBase() |
110 | 3.51k | { |
111 | 3.51k | AddValIdent(IsVal); |
112 | | |
113 | 3.51k | InitCharSets(); |
114 | 3.51k | InitFun(); |
115 | 3.51k | InitConst(); |
116 | 3.51k | InitOprt(); |
117 | 3.51k | } |
118 | | |
119 | | //--------------------------------------------------------------------------- |
120 | | /** \brief Define the character sets. |
121 | | \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars |
122 | | |
123 | | This function is used for initializing the default character sets that define |
124 | | the characters to be useable in function and variable names and operators. |
125 | | */ |
126 | | void Parser::InitCharSets() |
127 | 3.51k | { |
128 | 3.51k | DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")); |
129 | 3.51k | DefineOprtChars(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}")); |
130 | 3.51k | DefineInfixOprtChars(_T("/+-*^?<>=#!$%&|~'_")); |
131 | 3.51k | } |
132 | | |
133 | | //--------------------------------------------------------------------------- |
134 | | /** \brief Initialize the default functions. */ |
135 | | void Parser::InitFun() |
136 | 3.51k | { |
137 | 3.51k | if (mu::TypeInfo<mu::value_type>::IsInteger()) |
138 | 0 | { |
139 | | // When setting MUP_BASETYPE to an integer type |
140 | | // Place functions for dealing with integer values here |
141 | | // ... |
142 | | // ... |
143 | | // ... |
144 | 0 | } |
145 | 3.51k | else |
146 | 3.51k | { |
147 | | // trigonometric functions |
148 | 3.51k | DefineFun(_T("sin"), MathImpl<value_type>::Sin); |
149 | 3.51k | DefineFun(_T("cos"), MathImpl<value_type>::Cos); |
150 | 3.51k | DefineFun(_T("tan"), MathImpl<value_type>::Tan); |
151 | 3.51k | DefineFun(_T("cot"), MathImpl<value_type>::Cot); |
152 | | // arcus functions |
153 | 3.51k | DefineFun(_T("asin"), MathImpl<value_type>::ASin); |
154 | 3.51k | DefineFun(_T("acos"), MathImpl<value_type>::ACos); |
155 | 3.51k | DefineFun(_T("atan"), MathImpl<value_type>::ATan); |
156 | 3.51k | DefineFun(_T("atan2"), MathImpl<value_type>::ATan2); |
157 | | // hyperbolic functions |
158 | 3.51k | DefineFun(_T("sinh"), MathImpl<value_type>::Sinh); |
159 | 3.51k | DefineFun(_T("cosh"), MathImpl<value_type>::Cosh); |
160 | 3.51k | DefineFun(_T("tanh"), MathImpl<value_type>::Tanh); |
161 | 3.51k | DefineFun(_T("coth"), MathImpl<value_type>::Coth); |
162 | | // arcus hyperbolic functions |
163 | 3.51k | DefineFun(_T("asinh"), MathImpl<value_type>::ASinh); |
164 | 3.51k | DefineFun(_T("acosh"), MathImpl<value_type>::ACosh); |
165 | 3.51k | DefineFun(_T("atanh"), MathImpl<value_type>::ATanh); |
166 | | // Logarithm functions |
167 | 3.51k | DefineFun(_T("log2"), MathImpl<value_type>::Log2); |
168 | 3.51k | DefineFun(_T("log10"), MathImpl<value_type>::Log10); |
169 | 3.51k | DefineFun(_T("log"), MathImpl<value_type>::Log); |
170 | 3.51k | DefineFun(_T("ln"), MathImpl<value_type>::Log); |
171 | | // misc |
172 | 3.51k | DefineFun(_T("exp"), MathImpl<value_type>::Exp); |
173 | 3.51k | DefineFun(_T("sqrt"), MathImpl<value_type>::Sqrt); |
174 | 3.51k | DefineFun(_T("sign"), MathImpl<value_type>::Sign); |
175 | 3.51k | DefineFun(_T("rint"), MathImpl<value_type>::Rint); |
176 | 3.51k | DefineFun(_T("abs"), MathImpl<value_type>::Abs); |
177 | | // Functions with variable number of arguments |
178 | 3.51k | DefineFun(_T("sum"), MathImpl<value_type>::Sum); |
179 | 3.51k | DefineFun(_T("avg"), MathImpl<value_type>::Avg); |
180 | 3.51k | DefineFun(_T("min"), MathImpl<value_type>::Min); |
181 | 3.51k | DefineFun(_T("max"), MathImpl<value_type>::Max); |
182 | | // Random number |
183 | 3.51k | DefineFun(_T("rnd"), MathImpl<value_type>::Rnd, false); |
184 | 3.51k | } |
185 | 3.51k | } |
186 | | |
187 | | //--------------------------------------------------------------------------- |
188 | | /** \brief Initialize constants. |
189 | | |
190 | | By default the parser recognizes two constants. Pi ("pi") and the Eulerian |
191 | | number ("_e"). |
192 | | */ |
193 | | void Parser::InitConst() |
194 | 3.51k | { |
195 | 3.51k | DefineConst(_T("_pi"), MathImpl<value_type>::CONST_PI); |
196 | 3.51k | DefineConst(_T("_e"), MathImpl<value_type>::CONST_E); |
197 | 3.51k | } |
198 | | |
199 | | //--------------------------------------------------------------------------- |
200 | | /** \brief Initialize operators. |
201 | | |
202 | | By default only the unary minus operator is added. |
203 | | */ |
204 | | void Parser::InitOprt() |
205 | 3.51k | { |
206 | 3.51k | DefineInfixOprt(_T("-"), MathImpl<value_type>::UnaryMinus); |
207 | 3.51k | DefineInfixOprt(_T("+"), MathImpl<value_type>::UnaryPlus); |
208 | 3.51k | } |
209 | | |
210 | | //--------------------------------------------------------------------------- |
211 | | void Parser::OnDetectVar(string_type* /*pExpr*/, int& /*nStart*/, int& /*nEnd*/) |
212 | 0 | { |
213 | | // this is just sample code to illustrate modifying variable names on the fly. |
214 | | // I'm not sure anyone really needs such a feature... |
215 | | /* |
216 | | |
217 | | |
218 | | string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd); |
219 | | string sRepl = std::string("_") + sVar + "_"; |
220 | | |
221 | | int nOrigVarEnd = nEnd; |
222 | | cout << "variable detected!\n"; |
223 | | cout << " Expr: " << *pExpr << "\n"; |
224 | | cout << " Start: " << nStart << "\n"; |
225 | | cout << " End: " << nEnd << "\n"; |
226 | | cout << " Var: \"" << sVar << "\"\n"; |
227 | | cout << " Repl: \"" << sRepl << "\"\n"; |
228 | | nEnd = nStart + sRepl.length(); |
229 | | cout << " End: " << nEnd << "\n"; |
230 | | pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl); |
231 | | cout << " New expr: " << *pExpr << "\n"; |
232 | | */ |
233 | 0 | } |
234 | | |
235 | | //--------------------------------------------------------------------------- |
236 | | /** \brief Numerically differentiate with regard to a variable. |
237 | | \param [in] a_Var Pointer to the differentiation variable. |
238 | | \param [in] a_fPos Position at which the differentiation should take place. |
239 | | \param [in] a_fEpsilon Epsilon used for the numerical differentiation. |
240 | | |
241 | | Numerical differentiation uses a 5 point operator yielding a 4th order |
242 | | formula. The default value for epsilon is 0.00074 which is |
243 | | numeric_limits<double>::epsilon() ^ (1/5). |
244 | | */ |
245 | | value_type Parser::Diff(value_type* a_Var, value_type a_fPos, value_type a_fEpsilon) const |
246 | 0 | { |
247 | 0 | value_type fRes(0); |
248 | 0 | value_type fBuf(*a_Var); |
249 | 0 | value_type f[4] = { 0,0,0,0 }; |
250 | 0 | value_type fEpsilon(a_fEpsilon); |
251 | | |
252 | | // Backwards compatible calculation of epsilon inc case the user doesn't provide |
253 | | // his own epsilon |
254 | 0 | if (fEpsilon == 0) |
255 | 0 | fEpsilon = (a_fPos == 0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos; |
256 | |
|
257 | 0 | *a_Var = a_fPos + 2 * fEpsilon; f[0] = Eval(); |
258 | 0 | *a_Var = a_fPos + 1 * fEpsilon; f[1] = Eval(); |
259 | 0 | *a_Var = a_fPos - 1 * fEpsilon; f[2] = Eval(); |
260 | 0 | *a_Var = a_fPos - 2 * fEpsilon; f[3] = Eval(); |
261 | 0 | *a_Var = fBuf; // restore variable |
262 | |
|
263 | 0 | fRes = (-f[0] + 8 * f[1] - 8 * f[2] + f[3]) / (12 * fEpsilon); |
264 | 0 | return fRes; |
265 | 0 | } |
266 | | } // namespace mu |