LibreVNA/Software/PC_Application/Traces/Math/parser/mpTest.cpp

1839 lines
71 KiB
C++
Raw Normal View History

/** \file
\brief Implementation of the unit test for muParserX.
<pre>
__________ ____ ___
_____ __ _\______ \_____ _______ ______ __________\ \/ /
/ \| | \ ___/\__ \\_ __ \/ ___// __ \_ __ \ /
| Y Y \ | / | / __ \| | \/\___ \\ ___/| | \/ \
|__|_| /____/|____| (____ /__| /____ >\___ >__| /___/\ \
\/ \/ \/ \/ \_/
Copyright (C) 2016 Ingo Berg
All rights reserved.
muParserX - A C++ math parser library with array and string support
Copyright (c) 2016, Ingo Berg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</pre>
*/
#include "mpTest.h"
#include "mpValue.h"
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <complex>
#include <limits>
#define MUP_CONST_PI 3.141592653589793238462643
#define MUP_CONST_E 2.718281828459045235360287
using namespace std;
MUP_NAMESPACE_START
//-----------------------------------------------------------------------------------------------
//
// class OprtStrAdd
//
//-----------------------------------------------------------------------------------------------
class DbgSillyAdd : public IOprtBin
{
public:
DbgSillyAdd()
:IOprtBin(_T("++"), (int)prADD_SUB, oaLEFT)
{}
//-----------------------------------------------------------------------------------------------
void Eval(ptr_val_type& ret, const ptr_val_type *arg, int argc)
{
assert(argc == 2);
float_type a = arg[0]->GetFloat();
float_type b = arg[1]->GetFloat();
*ret = a + b;
}
//-----------------------------------------------------------------------------------------------
const char_type* GetDesc() const
{
return _T("internally used operator without special meaning for unit testing");
}
//-----------------------------------------------------------------------------------------------
IToken* Clone() const
{
return new DbgSillyAdd(*this);
}
};
//------------------------------------------------------------------------------
class FunTest0 : public ICallback
{
public:
FunTest0() : ICallback(cmFUNC, _T("test0"), 0)
{}
virtual void Eval(ptr_val_type &ret, const ptr_val_type * /*a_pArg*/, int /*a_iArgc*/)
{
*ret = 0.0;
}
virtual const char_type* GetDesc() const
{
return _T("");
}
virtual IToken* Clone() const
{
return new FunTest0(*this);
}
}; // class FunTest0
//---------------------------------------------------------------------------
int ParserTester::c_iCount = 0;
//---------------------------------------------------------------------------
ParserTester::ParserTester()
:m_vTestFun()
, m_stream(&console())
{
AddTest(&ParserTester::TestParserValue);
AddTest(&ParserTester::TestUndefVar);
AddTest(&ParserTester::TestErrorCodes);
AddTest(&ParserTester::TestEqn);
AddTest(&ParserTester::TestIfElse);
AddTest(&ParserTester::TestStringFun);
AddTest(&ParserTester::TestMatrix);
AddTest(&ParserTester::TestComplex);
AddTest(&ParserTester::TestVector);
AddTest(&ParserTester::TestBinOp);
AddTest(&ParserTester::TestPostfix);
AddTest(&ParserTester::TestInfix);
AddTest(&ParserTester::TestMultiArg);
AddTest(&ParserTester::TestScript);
AddTest(&ParserTester::TestValReader);
AddTest(&ParserTester::TestIssueReports);
ParserTester::c_iCount = 0;
}
//---------------------------------------------------------------------------
int ParserTester::TestIssueReports()
{
int iNumErr = 0;
*m_stream << _T("testing github issue reports...");
// Github: Issue 55
iNumErr += ThrowTest(_T("{0,{0}}*{0,{0}}*{,{0}}*{0,{0}0,{0}}*{0,{0}}*{,{0}}*{0}*{000}"), ecUNEXPECTED_COMMA);
// Github: Issue 56
iNumErr += ThrowTest(_T("0M[,1][0/1M[0M]M]"), ecUNEXPECTED_COMMA);
// Github Issue 57:
iNumErr += ThrowTest(_T("{?{{{{:44"), ecUNEXPECTED_CONDITIONAL);
// Github Issue 60
iNumErr += ThrowTest(_T("0<01?1=:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1<:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1>:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1+:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1*:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1/:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1&:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1<<:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("0<01?1>>:1"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("{ ? 0 : 7m}-{7, -00007m}-{7M}"), ecUNEXPECTED_CONDITIONAL);
iNumErr += ThrowTest(_T("{ { { ? 2 }, 7:2 }*7m }"), ecUNEXPECTED_CONDITIONAL);
// Not too happy about the undefined code, but better than a crash of an assertion at runtime
iNumErr += ThrowTest(_T("{0<0?0,0:0<0}"), ecUNEXPECTED_COMMA);
// Github Issue 63
iNumErr += ThrowTest(_T("0<0-0--eye()"), ecINVALID_NUMBER_OF_PARAMETERS);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestUndefVar()
{
int iNumErr = 0;
*m_stream << _T("testing implicit definition of undefined variables...");
// Test 1: No variables defined, test detection of undefined variables
{
ParserX p;
p.SetExpr(_T("a+b+c+d"));
const mup::var_maptype &expr_var = p.GetExprVar();
const mup::var_maptype &var = p.GetVar();
// The expression contains 4 undefined variables
if (expr_var.size() != 4)
iNumErr++;
// There are no variables defined
if (var.size() != 0)
iNumErr++;
}
// Test 2: Variables were defined explicitely, test detection of variables
{
ParserX p;
// Now define the variables and perform the check again
Value vVarVal[] = { 1.0, 2.0, 3.0, 4.0 };
p.DefineVar(_T("a"), Variable(&vVarVal[0]));
p.DefineVar(_T("b"), Variable(&vVarVal[1]));
p.DefineVar(_T("c"), Variable(&vVarVal[2]));
p.DefineVar(_T("d"), Variable(&vVarVal[3]));
p.SetExpr(_T("a+b+c+d"));
const mup::var_maptype &expr_var = p.GetExprVar();
const mup::var_maptype &var = p.GetVar();
// The expression contains 4 undefined variables
if (expr_var.size() != 4)
iNumErr++;
// There are no variables defined
if (var.size() != 4)
iNumErr++;
}
// Test 3: Variables were defined implicitels, test detection of undefined variables
{
ParserX p;
// Now define the variables and perform the check again
p.EnableAutoCreateVar(true);
p.SetExpr(_T("a+b+c+d"));
const mup::var_maptype &expr_var = p.GetExprVar();
const mup::var_maptype &var = p.GetVar();
// The expression contains 4 undefined variables
if (expr_var.size() != 4)
iNumErr++;
// There are no variables defined
if (var.size() != 4)
iNumErr++;
}
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestMatrix()
{
int iNumErr = 0;
*m_stream << _T("testing matrix calculations...");
Value unity(3, 3, 0);
unity.At(0, 0) = 1.0;
unity.At(1, 1) = 1.0;
unity.At(2, 2) = 1.0;
Value va(3, 0);
va.At(0) = 1.0;
va.At(1) = 2.0;
va.At(2) = 3.0;
//Value m2(3, 3, 0);
//m2.At(0, 0) = 1; m2.At(0, 1) = 2; m2.At(0, 2) = 3;
//m2.At(1, 0) = 4; m2.At(1, 1) = 5; m2.At(1, 2) = 6;
//m2.At(2, 0) = 7; m2.At(2, 1) = 8; m2.At(2, 2) = 9;
Value m1_plus_m2(3, 3, 0);
m1_plus_m2.At(0, 0) = 2.0; m1_plus_m2.At(0, 1) = 2.0; m1_plus_m2.At(0, 2) = 3.0;
m1_plus_m2.At(1, 0) = 4.0; m1_plus_m2.At(1, 1) = 6.0; m1_plus_m2.At(1, 2) = 6.0;
m1_plus_m2.At(2, 0) = 7.0; m1_plus_m2.At(2, 1) = 8.0; m1_plus_m2.At(2, 2) = 10.0;
Value m2_minus_m1(3, 3, 0);
m2_minus_m1.At(0, 0) = 0.0; m2_minus_m1.At(0, 1) = 2.0; m2_minus_m1.At(0, 2) = 3.0;
m2_minus_m1.At(1, 0) = 4.0; m2_minus_m1.At(1, 1) = 4.0; m2_minus_m1.At(1, 2) = 6.0;
m2_minus_m1.At(2, 0) = 7.0; m2_minus_m1.At(2, 1) = 8.0; m2_minus_m1.At(2, 2) = 8.0;
Value m2_times_10(3, 3, 0);
m2_times_10.At(0, 0) = 10.0; m2_times_10.At(0, 1) = 20.0; m2_times_10.At(0, 2) = 30.0;
m2_times_10.At(1, 0) = 40.0; m2_times_10.At(1, 1) = 50.0; m2_times_10.At(1, 2) = 60.0;
m2_times_10.At(2, 0) = 70.0; m2_times_10.At(2, 1) = 80.0; m2_times_10.At(2, 2) = 90.0;
Value va_times_vb_transp(3, 3, 0);
va_times_vb_transp.At(0, 0) = 4.0; va_times_vb_transp.At(0, 1) = 3.0; va_times_vb_transp.At(0, 2) = 2.0;
va_times_vb_transp.At(1, 0) = 8.0; va_times_vb_transp.At(1, 1) = 6.0; va_times_vb_transp.At(1, 2) = 4.0;
va_times_vb_transp.At(2, 0) = 12.0; va_times_vb_transp.At(2, 1) = 9.0; va_times_vb_transp.At(2, 2) = 6.0;
Value size_3x6(1, 2, 0);
size_3x6.At(0, 0) = 3.0;
size_3x6.At(0, 1) = 6.0;
Value size_3x3(1, 2, 0);
size_3x3.At(0, 0) = 3.0;
size_3x3.At(0, 1) = 3.0;
Value size_3x1(1, 2, 0);
size_3x1.At(0, 0) = 3.0;
size_3x1.At(0, 1) = 1.0;
Value size_1x3(1, 2, 0);
size_1x3.At(0, 0) = 1.0;
size_1x3.At(0, 1) = 3.0;
// Check matrix dimension mismatch error
iNumErr += ThrowTest(_T("\"hallo\"+m1"), ecEVAL);
iNumErr += ThrowTest(_T("m1+\"hallo\""), ecEVAL);
iNumErr += ThrowTest(_T("va+m1"), ecMATRIX_DIMENSION_MISMATCH);
iNumErr += ThrowTest(_T("m1+va"), ecMATRIX_DIMENSION_MISMATCH);
iNumErr += ThrowTest(_T("va-m1"), ecMATRIX_DIMENSION_MISMATCH);
iNumErr += ThrowTest(_T("m1-va"), ecMATRIX_DIMENSION_MISMATCH);
iNumErr += ThrowTest(_T("va*m1"), ecMATRIX_DIMENSION_MISMATCH);
iNumErr += ThrowTest(_T("va+eye(2)"), ecMATRIX_DIMENSION_MISMATCH);
// Issue 63:
iNumErr += ThrowTest(_T("0-0-eye()"), ecINVALID_NUMBER_OF_PARAMETERS);
iNumErr += ThrowTest(_T("m1[1]"), ecINDEX_DIMENSION);
iNumErr += ThrowTest(_T("m1[1,2,3]"), ecINDEX_DIMENSION);
iNumErr += ThrowTest(_T("va[1,2]"), ecINDEX_OUT_OF_BOUNDS); // va has 1 column, 3 rows -> the coulumn index is referencing the third column
iNumErr += ThrowTest(_T("a+m1"), ecEVAL);
iNumErr += ThrowTest(_T("m1+a"), ecEVAL);
iNumErr += ThrowTest(_T("a-m1"), ecEVAL);
iNumErr += ThrowTest(_T("m1-a"), ecEVAL);
iNumErr += ThrowTest(_T("va[,1]"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("va[{1]"), ecMISSING_CURLY_BRACKET);
iNumErr += ThrowTest(_T("{,1}"), ecUNEXPECTED_COMMA);
// sample expressions
iNumErr += EqnTest(_T("m1"), unity, true);
iNumErr += EqnTest(_T("m1*m1"), unity, true);
iNumErr += EqnTest(_T("m1+m2"), m1_plus_m2, true);
iNumErr += EqnTest(_T("m2-m1"), m2_minus_m1, true);
iNumErr += EqnTest(_T("10*m2"), m2_times_10, true);
iNumErr += EqnTest(_T("m2*10"), m2_times_10, true);
iNumErr += EqnTest(_T("5*m2*b"), m2_times_10, true);
iNumErr += EqnTest(_T("b*m2*5"), m2_times_10, true);
iNumErr += EqnTest(_T("m1*va"), va, true);
// ones
Value ones_3(3, 1.0);
Value ones_3x3(3, 3, 1.0);
iNumErr += ThrowTest(_T("ones(1,2,3)"), ecINVALID_NUMBER_OF_PARAMETERS);
iNumErr += ThrowTest(_T("ones()"), ecINVALID_NUMBER_OF_PARAMETERS);
iNumErr += EqnTest(_T("ones(1,1)"), 1.0, true);
iNumErr += EqnTest(_T("ones(1)"), 1.0, true);
iNumErr += EqnTest(_T("ones(3,3)"), ones_3x3, true);
iNumErr += EqnTest(_T("ones(3,1)"), ones_3, true);
iNumErr += EqnTest(_T("ones(3)"), ones_3, true);
iNumErr += EqnTest(_T("size(ones(3,3))"), size_3x3, true); // check return value dimension
iNumErr += EqnTest(_T("size(ones(1,3))"), size_1x3, true); // check return value dimension
iNumErr += EqnTest(_T("size(ones(3,1))"), size_3x1, true); // check return value dimension
iNumErr += EqnTest(_T("size(ones(3))"), size_3x3, true); // check return value dimension
// zeros
iNumErr += ThrowTest(_T("zeros()"), ecINVALID_NUMBER_OF_PARAMETERS);
iNumErr += EqnTest(_T("size(zeros(3,3))"), size_3x3, true); // check return value dimension
iNumErr += EqnTest(_T("size(zeros(1,3))"), size_1x3, true); // check return value dimension
iNumErr += EqnTest(_T("size(zeros(3,1))"), size_3x1, true); // check return value dimension
iNumErr += EqnTest(_T("size(zeros(3))"), size_3x3, true); // check return value dimension
// eye
iNumErr += ThrowTest(_T("eye()"), ecINVALID_NUMBER_OF_PARAMETERS);
iNumErr += EqnTest(_T("size(eye(3,3))"), size_3x3, true); // check return value dimension
iNumErr += EqnTest(_T("size(eye(1,3))"), size_1x3, true); // check return value dimension
iNumErr += EqnTest(_T("size(eye(3,1))"), size_3x1, true); // check return value dimension
iNumErr += EqnTest(_T("size(eye(3))"), size_3x3, true); // check return value dimension
iNumErr += EqnTest(_T("size(eye(3,6))"), size_3x6, true); // check return value dimension
// transposition
iNumErr += EqnTest(_T("va'*vb"), 16.0, true);
iNumErr += EqnTest(_T("2*va'*vb"), 32.0, true);
iNumErr += EqnTest(_T("va*vb'"), va_times_vb_transp, true);
// index operator
// erster index: Zeilenindex, zweiter index: Spaltenindex
iNumErr += EqnTest(_T("va[0]"), 1.0, true);
iNumErr += EqnTest(_T("va[1]"), 2.0, true);
iNumErr += EqnTest(_T("va[2]"), 3.0, true);
// Use two dimensional index operator on a vector
iNumErr += EqnTest(_T("va[0,0]"), 1.0, true);
iNumErr += EqnTest(_T("va[1,0]"), 2.0, true);
iNumErr += EqnTest(_T("va[2,0]"), 3.0, true);
// Now test the same with a transposed vector:
iNumErr += EqnTest(_T("va'[0]"), 1.0, true);
iNumErr += EqnTest(_T("va'[1]"), 2.0, true);
iNumErr += EqnTest(_T("va'[2]"), 3.0, true);
// Use two dimensional index operator on a vector
iNumErr += EqnTest(_T("va'[0,0]"), 1.0, true);
iNumErr += EqnTest(_T("va'[0,1]"), 2.0, true);
iNumErr += EqnTest(_T("va'[0,2]"), 3.0, true);
iNumErr += EqnTest(_T("(va')[0,2]"), 3.0, true); // <- Index operator after closing bracket is ok
// vector creation
iNumErr += EqnTest(_T("{1,2,3}'"), va, true);
iNumErr += EqnTest(_T("{a,2,3}'"), va, true); // that was an actual bug: variable a was overwritten
// assignment to element:
iNumErr += ThrowTest(_T("va'[0]=123"), ecASSIGNEMENT_TO_VALUE);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestComplex()
{
int iNumErr = 0;
*m_stream << _T("testing complex calculations...");
// complex numbers
// ca=1+i, cb=2+3i, cc=3+4i
iNumErr += EqnTest(_T("ca==1+i"), true, true);
iNumErr += EqnTest(_T("ca==ca"), true, true);
iNumErr += EqnTest(_T("ca!=1+i"), false, true);
iNumErr += EqnTest(_T("ca!=ca"), false, true);
iNumErr += EqnTest(_T("ca!=cb"), true, true);
iNumErr += EqnTest(_T("ca!=va"), true, true);
iNumErr += EqnTest(_T("ca==va"), false, true);
// When comparing complex number Matlab/Octave compare only the real part
// I'll do the same...
iNumErr += EqnTest(_T("ca<10+i"), true, true);
iNumErr += EqnTest(_T("ca>10+i"), false, true);
iNumErr += EqnTest(_T("ca<=10+i"), true, true);
iNumErr += EqnTest(_T("ca>=10+i"), false, true);
iNumErr += EqnTest(_T("ca<=1"), true, true);
iNumErr += EqnTest(_T("ca>=1"), true, true);
// complex numbers
iNumErr += EqnTest(_T("i*i"), -1.0, true, 0);
iNumErr += EqnTest(_T("1i"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("norm(3+4i)"), 25.0, true, 0);
iNumErr += EqnTest(_T("norm(4i+3)"), 25.0, true, 0);
iNumErr += EqnTest(_T("norm(3i+4)"), 25.0, true, 0);
iNumErr += EqnTest(_T("real(4.1i+3.1)"), (float_type)3.1, true, 0);
iNumErr += EqnTest(_T("imag(3.1i+4.1)"), (float_type)3.1, true, 0);
iNumErr += EqnTest(_T("real(3.1)"), (float_type)3.1, true, 0);
iNumErr += EqnTest(_T("imag(2.1i)"), (float_type)2.1, true, 0);
iNumErr += EqnTest(_T("-(4i+5)"), cmplx_type(-5, -4), true, 0);
iNumErr += EqnTest(_T("sqrt(-1)"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("(-1)^0.5"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("(-3)^(4/3)"), std::pow(cmplx_type(-3, 0),
cmplx_type(4.0 / 3, 0)), true, 0);
// Issue 41: Complex pow of small numbers zeros out the imaginary part
// https://code.google.com/p/muparserx/issues/detail?id=41
iNumErr += EqnTest(_T("(1e-15 + 1e-15*i) ^ 2"), std::pow(cmplx_type(1e-15, 1e-15), 2), true, 0);
iNumErr += EqnTest(_T("sqrt(i*i)"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("sqrt(f)"), cmplx_type(0, 1), true, 1);
iNumErr += EqnTest(_T("sqrt(2-3)"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("sqrt(a-b)"), cmplx_type(0, 1), true, 2);
iNumErr += EqnTest(_T("sqrt((2-3))"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("sqrt((a-b))"), cmplx_type(0, 1), true, 2);
iNumErr += EqnTest(_T("sqrt(-(1))"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("sqrt((-1))"), cmplx_type(0, 1), true, 0);
iNumErr += EqnTest(_T("sqrt(-(-1))"), cmplx_type(1, 0), true, 0);
iNumErr += EqnTest(_T("sqrt(1)"), cmplx_type(1, 0), true, 0);
iNumErr += EqnTest(_T("a=1+2i"), cmplx_type(1, 2), true, 1);
iNumErr += EqnTest(_T("-(1+2i)"), cmplx_type(-1, -2), true, 0);
iNumErr += EqnTest(_T("-(-1-2i)"), cmplx_type(1, 2), true, 0);
iNumErr += EqnTest(_T("a*i"), cmplx_type(0, 1), true, 1);
iNumErr += EqnTest(_T("-(a+b*i)"), cmplx_type(-1, -2), true, 2);
iNumErr += EqnTest(_T("-(-a-b*i)"), cmplx_type(1, 2), true, 2);
iNumErr += EqnTest(_T("(2+4i)*(8-6i)"), cmplx_type(40, 20), true, 0);
// Issue 17: Wrong result on complex power.
iNumErr += EqnTest(_T("(-0.27 + 0.66*i)^2"), cmplx_type(-0.3627, -0.3564), true, 0);
iNumErr += EqnTest(_T("(-1+5i)^2"), cmplx_type(-24, -10), true, 0);
iNumErr += EqnTest(_T("c=(a=1+2i)"), cmplx_type(1, 2), true, 2);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestParserValue()
{
int iNumErr = 0;
*m_stream << _T("testing parser value types...");
// Define values and variables for each type
Value bVal = true;
Value fVal = (float_type)3.14;
Value sVal = string_type(_T("hello world"));
Value sVal1 = _T("hello world"); // Test assignment from const char* to string
Value cVal = cmplx_type(1, 1);
Value aVal(2, 0);
aVal.At(0) = (float_type)2.0;
aVal.At(1) = (float_type)3.0;
// Create a 3x3 matrix
Value matrix(3, 0);
matrix.At(0) = Value(3, 0);
matrix.At(1) = Value(3, 0);
matrix.At(2) = Value(3, 0);
Variable bVar(&bVal),
fVar(&fVal),
sVar(&sVal),
sVar1(&sVal1),
cVar(&cVal),
aVar(&aVal);
// Check the value types
try
{
// Test if matrix values do work
if (!matrix.IsMatrix() || matrix.GetRows() != 3)
iNumErr++;
std::size_t sz = matrix.GetRows();
for (std::size_t i = 0; i < sz; ++i)
{
std::size_t dim_row = matrix.At(static_cast<int>(i)).GetRows();
if (dim_row != 3)
{
iNumErr++;
console() << _T("\n Array dimension mismatch in matrix row ") << i
<< _T(" (expected=3; dim=") << dim_row << _T(")");
}
}
// test type checking of values
if (!fVal.IsScalar() || fVal.IsMatrix() || fVal.GetType() != 'f') iNumErr++;
if (!cVal.IsScalar() || cVal.IsMatrix() || cVal.GetType() != 'c') iNumErr++;
if (aVal.IsScalar() || !aVal.IsMatrix() || aVal.GetType() != 'm') iNumErr++;
if (sVal.IsScalar() || sVal.IsMatrix() || sVal.GetType() != 's') iNumErr++;
if (sVal1.IsScalar() || sVal1.IsMatrix() || sVal1.GetType() != 's') iNumErr++;
if (bVal.IsScalar() || bVal.IsMatrix() || bVal.GetType() != 'b') iNumErr++;
// test type checking of variables
if (!fVar.IsScalar() || fVar.IsMatrix() || fVar.GetType() != 'f') iNumErr++;
if (!cVar.IsScalar() || cVar.IsMatrix() || cVar.GetType() != 'c') iNumErr++;
if (aVar.IsScalar() || !aVar.IsMatrix() || aVar.GetType() != 'm') iNumErr++;
if (sVar.IsScalar() || sVar.IsMatrix() || sVar.GetType() != 's') iNumErr++;
if (sVar1.IsScalar() || sVar1.IsMatrix() || sVar1.GetType() != 's') iNumErr++;
if (bVar.IsScalar() || bVar.IsMatrix() || bVar.GetType() != 'b') iNumErr++;
// Issue 33: https://code.google.com/p/muparserx/issues/detail?id=33
// Remark: Type information was not properly set when invoking +=, -= operators
{
Value x = 1.0;
Value y = cmplx_type(0, 1);
x += y;
if (x.GetImag() != 1 || x.GetFloat() != 1 || x.GetType() != 'c')
{
*m_stream << _T("\nValue::operator+=(...) failed.");
iNumErr++;
}
x = 1.0;
y = cmplx_type(0, 1);
x -= y;
if (x.GetImag() != -1 || x.GetFloat() != 1 || x.GetType() != 'c')
{
*m_stream << _T("\nValue::operator-=(...) failed.");
iNumErr++;
}
}
}
catch (...)
{
iNumErr++;
}
bool bError;
#define VALUE_THROWCHECK(VAR, FAIL, MEMBER) \
bError = (FAIL); \
try \
{ \
VAR.MEMBER(); \
} \
catch (...) \
{ \
bError ^= true; \
} \
iNumErr += (bError) ? 1 : 0; \
c_iCount++;
// Check if the getter functions really throw an exception
// when used with an incorrect value type
// Case 1: test float values
VALUE_THROWCHECK(fVal, false, GetFloat)
VALUE_THROWCHECK(fVal, false, GetImag)
VALUE_THROWCHECK(fVal, true, GetBool)
VALUE_THROWCHECK(fVal, true, GetString)
VALUE_THROWCHECK(fVal, true, GetArray)
// for variables
VALUE_THROWCHECK(fVar, false, GetFloat)
VALUE_THROWCHECK(fVar, false, GetImag)
VALUE_THROWCHECK(fVar, true, GetBool)
VALUE_THROWCHECK(fVar, true, GetString)
VALUE_THROWCHECK(fVar, true, GetArray)
// Case 2: test bool values
VALUE_THROWCHECK(bVal, false, GetFloat)
VALUE_THROWCHECK(bVal, true, GetImag)
VALUE_THROWCHECK(bVal, false, GetBool)
VALUE_THROWCHECK(bVal, true, GetString)
VALUE_THROWCHECK(bVal, true, GetArray)
// for variables
VALUE_THROWCHECK(bVar, false, GetFloat)
VALUE_THROWCHECK(bVar, true, GetImag)
VALUE_THROWCHECK(bVar, false, GetBool)
VALUE_THROWCHECK(bVar, true, GetString)
VALUE_THROWCHECK(bVar, true, GetArray)
// Case 3: test string values
// VALUE_THROWCHECK(sVal, true, GetFloat)
VALUE_THROWCHECK(sVal, true, GetImag)
VALUE_THROWCHECK(sVal, true, GetBool)
VALUE_THROWCHECK(sVal, false, GetString)
VALUE_THROWCHECK(sVal, true, GetArray)
// for variables
// VALUE_THROWCHECK(sVar, true, GetFloat)
VALUE_THROWCHECK(sVar, true, GetImag)
VALUE_THROWCHECK(sVar, true, GetBool)
VALUE_THROWCHECK(sVar, false, GetString)
VALUE_THROWCHECK(sVar, true, GetArray)
// Case 4: test array values
// VALUE_THROWCHECK(aVal, true, GetFloat)
VALUE_THROWCHECK(aVal, true, GetImag)
VALUE_THROWCHECK(aVal, true, GetBool)
VALUE_THROWCHECK(aVal, true, GetString)
VALUE_THROWCHECK(aVal, false, GetArray)
// for variables
// VALUE_THROWCHECK(aVar, true, GetFloat)
VALUE_THROWCHECK(aVar, true, GetImag)
VALUE_THROWCHECK(aVar, true, GetBool)
VALUE_THROWCHECK(aVar, true, GetString)
VALUE_THROWCHECK(aVar, false, GetArray)
// Case 5: test complex values
// VALUE_THROWCHECK(cVal, false, GetFloat)
VALUE_THROWCHECK(cVal, false, GetImag)
VALUE_THROWCHECK(cVal, true, GetBool)
VALUE_THROWCHECK(cVal, true, GetString)
VALUE_THROWCHECK(cVal, true, GetArray)
// for variables
// VALUE_THROWCHECK(cVar, false, GetFloat)
VALUE_THROWCHECK(cVar, false, GetImag)
VALUE_THROWCHECK(cVar, true, GetBool)
VALUE_THROWCHECK(cVar, true, GetString)
VALUE_THROWCHECK(cVar, true, GetArray)
#undef VALUE_THROWCHECK
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestErrorCodes()
{
int iNumErr = 0;
*m_stream << _T("testing error codes...");
iNumErr += ThrowTest(_T("a,b"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("(a,b)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("((a,b))"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("2*1,2"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sin(1,2)"), ecTOO_MANY_PARAMS);
// Invalid expression
// Issue 20: http://code.google.com/p/muparserx/issues/detail?id=20
iNumErr += ThrowTest(_T(" "), ecUNEXPECTED_EOF);
iNumErr += ThrowTest(_T("sin(nonexistent_var)"), ecUNASSIGNABLE_TOKEN, 4, _T("nonexistent_var"));
// Invalid function argument types
iNumErr += ThrowTest(_T("sin(\"test\")"), ecEVAL, 0);
iNumErr += ThrowTest(_T("max(1, \"test\")"), ecEVAL, 0);
iNumErr += ThrowTest(_T("max(1,sin(8), \"t\")"), ecEVAL, 0);
iNumErr += ThrowTest(_T("str2dbl(sin(3.14))"), ecEVAL, 0);
// Invalid unary operator argument types
iNumErr += ThrowTest(_T("\"test\"n"), ecEVAL, 6); // (nano can only be applied to floats)
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)+\"hallo\""), ecEVAL);
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)-\"hallo\""), ecEVAL);
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)*\"hallo\""), ecEVAL);
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)/\"hallo\""), ecEVAL);
iNumErr += ThrowTest(_T("10+va"), ecEVAL, 2);
// Type conflicts in binary operators
iNumErr += ThrowTest(_T("\"test\" // 8"), ecEVAL, 7);
iNumErr += ThrowTest(_T("8//\"test\""), ecEVAL, 1);
iNumErr += ThrowTest(_T("5//8"), ecEVAL, 1);
iNumErr += ThrowTest(_T("\"t\"//sin(8)"), ecEVAL, 3);
iNumErr += ThrowTest(_T("sin(8)//\"t\""), ecEVAL, 6);
// Unexpected end of expression
iNumErr += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);
iNumErr += ThrowTest(_T("8*"), ecUNEXPECTED_EOF);
iNumErr += ThrowTest(_T("3+("), ecUNEXPECTED_EOF);
iNumErr += ThrowTest(_T("3+sin"), ecUNEXPECTED_EOF);
iNumErr += ThrowTest(_T("(2+"), ecUNEXPECTED_EOF);
iNumErr += ThrowTest(_T("3+)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("3)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("(3))"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("()"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("(2+)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("sin(cos)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("sin(())"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("sin()"), ecTOO_FEW_PARAMS);
iNumErr += ThrowTest(_T("sin)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("pi)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("a)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("2(-m)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("2(m)"), ecUNEXPECTED_PARENS);
iNumErr += ThrowTest(_T("(1+2"), ecMISSING_PARENS);
iNumErr += ThrowTest(_T("((3)"), ecMISSING_PARENS);
// The behaviour in the next function depends on whether
// implicit variable creation is allowed or not. (momentarily its active)
iNumErr += ThrowTest(_T("5z)"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("sin(3)xyz"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("5t6"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("5 t 6"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("ksdfj"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("-m"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("m4"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("sin(m)"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("m m"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("m(8)"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("4 + m"), ecUNASSIGNABLE_TOKEN);
// unexpected operator
iNumErr += ThrowTest(_T("5+*3)"), ecUNEXPECTED_OPERATOR);
// unexpected comma (used without a function)
iNumErr += ThrowTest(_T(",3"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sin(,sin(8))"), ecUNEXPECTED_COMMA);
// unexpected variable
iNumErr += ThrowTest(_T("a _xxx_ b"), ecUNASSIGNABLE_TOKEN, 2); // if a variable factory is installed ecUNEXPECTED_VAR
iNumErr += ThrowTest(_T("sin(3)cos(3)"), ecUNEXPECTED_FUN);
iNumErr += ThrowTest(_T("sin(3)3"), ecUNEXPECTED_VAL);
iNumErr += ThrowTest(_T("sin(3)+"), ecUNEXPECTED_EOF);
// value recognition
iNumErr += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex value
iNumErr += ThrowTest(_T("1+0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex value
iNumErr += ThrowTest(_T("a+0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex value
// index operator
iNumErr += ThrowTest(_T("3n[1]"), ecINDEX_OUT_OF_BOUNDS); // Indexing a scalar is ok, but this index is out of bounds (0 would be ok)
iNumErr += ThrowTest(_T("min(3,]"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("sin(]"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("va[]"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("3+]"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("sin[a)"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("1+[8]"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("1[8]"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("[1]"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("]1"), ecUNEXPECTED_SQR_BRACKET);
iNumErr += ThrowTest(_T("va[[3]]"), ecUNEXPECTED_SQR_BRACKET);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestStringFun()
{
int iNumErr = 0;
*m_stream << _T("testing string functions...");
// escape sequences
iNumErr += EqnTest(_T("\"\\\"quoted_string\\\"\""), _T("\"quoted_string\""), true); // "\"quoted_string\"" -> "quoted_string"
iNumErr += EqnTest(_T("\"\\\"\\\"\""), _T("\"\""), true); // "\"\"" -> ""
iNumErr += EqnTest(_T("\"\\\\\""), _T("\\"), true); // "\\" -> \ (single backslash)
// String functions
iNumErr += EqnTest(_T("strlen(\"12345\")"), 5.0, true);
iNumErr += EqnTest(_T("strlen(toupper(\"abcde\"))"), 5.0, true);
iNumErr += EqnTest(_T("sin(0)+(float)strlen(\"12345\")"), (float_type)5.0, true);
iNumErr += EqnTest(_T("10*(float)strlen(toupper(\"12345\"))"), (float_type)50.0, true);
iNumErr += EqnTest(_T("\"hello \"//\"world\""), string_type(_T("hello world")), true);
iNumErr += EqnTest(_T("toupper(\"hello \")//\"world\""), string_type(_T("HELLO world")), true);
iNumErr += EqnTest(_T("\"hello \"//toupper(\"world\")//\" !!!\""), string_type(_T("hello WORLD !!!")), true);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestPostfix()
{
int iNumErr = 0;
*m_stream << _T("testing postfix operators...");
// application
iNumErr += EqnTest(_T("1n"), (float_type)1e-9, true);
iNumErr += EqnTest(_T("8n"), (float_type)8e-9, true);
iNumErr += EqnTest(_T("8n"), (float_type)123.0, false);
iNumErr += EqnTest(_T("3m+5"), (float_type)5.003, true);
iNumErr += EqnTest(_T("1000m"), (float_type)1.0, true);
iNumErr += EqnTest(_T("1000 m"), (float_type)1.0, true);
iNumErr += EqnTest(_T("(a)m"), (float_type)1e-3, true);
iNumErr += EqnTest(_T("-(a)m"), (float_type)-1e-3, true);
iNumErr += EqnTest(_T("-2m"), (float_type)-2e-3, true);
iNumErr += EqnTest(_T("a++b"), 3.0, true);
iNumErr += EqnTest(_T("a ++ b"), 3.0, true);
iNumErr += EqnTest(_T("1++2"), 3.0, true);
iNumErr += EqnTest(_T("1 ++ 2"), 3.0, true);
iNumErr += EqnTest(_T("2+(a*1000)m"), (float_type)3.0, true);
// some incorrect results
iNumErr += EqnTest(_T("1000m"), (float_type)0.1, false);
iNumErr += EqnTest(_T("(a)m"), (float_type)2.0, false);
// factorial operator
iNumErr += EqnTest(_T("5!"), 120.0, true);
iNumErr += EqnTest(_T("-5!"), -120.0, true);
iNumErr += ThrowTest(_T("(-5)!"), ecDOMAIN_ERROR);
// Special tests for systems not supporting IEEE 754
if (!std::numeric_limits<float_type>::is_iec559)
{
iNumErr += ThrowTest(_T("123456!"), ecOVERFLOW);
}
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestInfix()
{
int iNumErr = 0;
*m_stream << _T("testing infix operators...");
float_type a = 1;
float_type b = 2;
iNumErr += EqnTest(_T("-1"), (float_type)-1.0, true);
iNumErr += EqnTest(_T("-(-1)"), (float_type)1.0, true);
iNumErr += EqnTest(_T("-(-1)*2"), (float_type)2.0, true);
iNumErr += EqnTest(_T("-(-2)*sqrt(4)"), (float_type)4.0, true);
iNumErr += EqnTest(_T("-a"), -a, true);
iNumErr += EqnTest(_T("-(a)"), -(a), true);
iNumErr += EqnTest(_T("-(-a)"), -(-a), true);
iNumErr += EqnTest(_T("-(-a)*2"), -(-a) * 2, true);
iNumErr += EqnTest(_T("-(8)"), (float_type)-8.0, true);
iNumErr += EqnTest(_T("-8"), (float_type)-8.0, true);
iNumErr += EqnTest(_T("-(2+1)"), (float_type)-3.0, true);
//iNumErr += EqnTest("-(f1of1(1+2*3)+1*2)", -9.0, true);
//iNumErr += EqnTest("-(-f1of1(1+2*3)+1*2)", 5.0, true);
iNumErr += EqnTest(_T("-sin(8)"), (float_type)-0.989358, true);
iNumErr += EqnTest(_T("-sin(8)"), (float_type)0.989358, false);
iNumErr += EqnTest(_T("3-(-a)"), (float_type)4.0, true);
iNumErr += EqnTest(_T("3--a"), (float_type)4.0, true);
iNumErr += EqnTest(_T("2++4"), (float_type)6.0, true);
iNumErr += EqnTest(_T("--1"), (float_type)1.0, true);
// sign precedence
// Issue 14: https://code.google.com/p/muparserx/issues/detail?id=14
iNumErr += EqnTest(_T("-3^2"), -9.0, true);
iNumErr += EqnTest(_T("-b^2^3-b^8"), -std::pow(b, std::pow(2.0, 3.0)) - std::pow(b, 8), true);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestMultiArg()
{
int iNumErr = 0;
*m_stream << _T("testing multiarg functions...");
// Multiarg functions being called without any parameters
iNumErr += ThrowTest(_T("min()"), ecTOO_FEW_PARAMS);
iNumErr += ThrowTest(_T("max()"), ecTOO_FEW_PARAMS);
iNumErr += ThrowTest(_T("sum()"), ecTOO_FEW_PARAMS);
// application
iNumErr += EqnTest(_T("max(1,8,9,(int)6)"), (float_type)9.0, true);
iNumErr += EqnTest(_T("max((int)6, 1+2, 4, -9)"), (float_type)6.0, true);
iNumErr += EqnTest(_T("min((int)6, 1+2, 4, -9)"), (float_type)-9.0, true);
//
iNumErr += EqnTest(_T("a=test0()"), (float_type)0, true);
iNumErr += EqnTest(_T("b=a+test0()"), (float_type)1, true);
// added as response to this bugreport:
// http://code.google.com/p/muparserx/issues/detail?id=1
// cause of the error: Function tokens were not cloned in the tokenreader when beeing found.
// a pointer to the one and only function onject was returned instead
// consequently the argument counter was overwritten by the second function call
// causing an assertion later on.
iNumErr += EqnTest(_T("sum(1,2)/sum(3,4)"), (float_type)0.428571, true);
iNumErr += EqnTest(_T("3/sum(3,4,5)"), (float_type)0.25, true);
iNumErr += EqnTest(_T("sum(3)/sum(3,4,5)"), (float_type)0.25, true);
iNumErr += EqnTest(_T("sum(3)+sum(3,4,5)"), (float_type)15, true);
iNumErr += EqnTest(_T("sum(1,2)/sum(3,4,5)"), (float_type)0.25, true);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestVector()
{
int iNumErr = 0;
*m_stream << _T("testing vector operations...");
// Vector operations
iNumErr += ThrowTest(_T("10+2*va"), ecEVAL); // fail: number + vector
iNumErr += ThrowTest(_T("10+va*2"), ecEVAL); // fail: number + vector
iNumErr += ThrowTest(_T("va+vc"), ecMATRIX_DIMENSION_MISMATCH); // fail: vectors of different size
iNumErr += ThrowTest(_T("va-vc"), ecMATRIX_DIMENSION_MISMATCH); // fail: vectors of different size
iNumErr += ThrowTest(_T("va*vc"), ecMATRIX_DIMENSION_MISMATCH); // fail: vectors of different size
iNumErr += ThrowTest(_T("va*vb"), ecMATRIX_DIMENSION_MISMATCH); // fail: matrix dimension mismatch
iNumErr += ThrowTest(_T("va*va"), ecMATRIX_DIMENSION_MISMATCH); // fail: matrix dimension mismatch
iNumErr += ThrowTest(_T("(va*vb)*b"), ecMATRIX_DIMENSION_MISMATCH); // fail: matrix dimension mismatch
iNumErr += ThrowTest(_T("va[1.23]"), ecTYPE_CONFLICT_IDX, 7); // fail: float value used as index
iNumErr += ThrowTest(_T("va[sin(8)]"), ecTYPE_CONFLICT_IDX, 9); // fail: float value used as index
iNumErr += ThrowTest(_T("va[-1]"), ecINDEX_OUT_OF_BOUNDS); // fail: negative value used as an index
iNumErr += ThrowTest(_T("va[c]"), ecINDEX_OUT_OF_BOUNDS);
iNumErr += ThrowTest(_T("va[(3)]"), ecINDEX_OUT_OF_BOUNDS);
iNumErr += ThrowTest(_T("a[1]"), ecINDEX_OUT_OF_BOUNDS); // indexing a scalar is ok, but this index is out of bounds (0 would be ok...)
iNumErr += ThrowTest(_T("va[1"), ecMISSING_SQR_BRACKET);
iNumErr += ThrowTest(_T("va[1]]"), ecUNEXPECTED_SQR_BRACKET);
//iNumErr += ThrowTest(_T("va==9"), ecEVAL);
//iNumErr += ThrowTest(_T("va==a"), ecEVAL);
//iNumErr += ThrowTest(_T("a==va"), ecEVAL);
//iNumErr += ThrowTest(_T("9==va"), ecEVAL);
Value v(3, 0);
v.At(0) = (float_type)5.0;
v.At(1) = (float_type)5.0;
v.At(2) = (float_type)5.0;
iNumErr += EqnTest(_T("va+vb"), v, true);
v.At(0) = (float_type)5.0;
v.At(1) = (float_type)5.0;
v.At(2) = (float_type)6.0;
iNumErr += EqnTest(_T("va+vb"), v, false);
v.At(0) = (float_type)-1.0;
v.At(1) = (float_type)-2.0;
v.At(2) = (float_type)-3.0;
iNumErr += EqnTest(_T("-va"), v, true);
iNumErr += EqnTest(_T("sizeof(va+vb)"), 3.0, true);
iNumErr += EqnTest(_T("sizeof(va-vb)"), 3.0, true);
iNumErr += EqnTest(_T("va==vb"), false, true);
iNumErr += EqnTest(_T("va!=vb"), true, true);
//iNumErr += EqnTest(_T("va<vb"), false, true);
//iNumErr += EqnTest(_T("va>vb"), true, true);
//iNumErr += EqnTest(_T("va<=vb"), false, true);
//iNumErr += EqnTest(_T("va>=vb"), true, true);
iNumErr += EqnTest(_T("vb[va[0]]"), 3.0, true);
iNumErr += EqnTest(_T("m1[0,0]+m1[1,1]+m1[2,2]"), 3.0, true);
iNumErr += EqnTest(_T("vb[m1[0,0]]"), 3.0, true);
iNumErr += EqnTest(_T("m1[0,0]=2"), 2.0, true);
iNumErr += EqnTest(_T("m1[1,1]=2"), 2.0, true);
iNumErr += EqnTest(_T("m1[2,2]=2"), 2.0, true);
iNumErr += EqnTest(_T("va[0]=12.3"), (float_type)12.3, true);
iNumErr += EqnTest(_T("va[1]=12.3"), (float_type)12.3, true);
iNumErr += EqnTest(_T("va[2]=12.3"), (float_type)12.3, true);
iNumErr += EqnTest(_T("va[0]"), 1.0, true);
iNumErr += EqnTest(_T("va[1]"), 2.0, true);
iNumErr += EqnTest(_T("va[2]"), 3.0, true);
iNumErr += EqnTest(_T("(va[2])"), 3.0, true);
iNumErr += EqnTest(_T("va[a]"), 2.0, true);
iNumErr += EqnTest(_T("(va[a])"), 2.0, true);
iNumErr += EqnTest(_T("va[b]"), 3.0, true);
iNumErr += EqnTest(_T("va[(2)]"), 3.0, true);
iNumErr += EqnTest(_T("va[-(-2)]"), 3.0, true);
iNumErr += EqnTest(_T("(va[(2)])"), 3.0, true);
iNumErr += EqnTest(_T("(va[-(-2)])"), 3.0, true);
iNumErr += EqnTest(_T("va[1+1]"), 3.0, true);
iNumErr += EqnTest(_T("va[(int)sin(8)+1]"), 2.0, true);
iNumErr += EqnTest(_T("va[2]+4"), 7.0, true);
iNumErr += EqnTest(_T("4+va[2]"), 7.0, true);
iNumErr += EqnTest(_T("va[2]*4"), 12.0, true);
iNumErr += EqnTest(_T("4*va[2]"), 12.0, true);
iNumErr += EqnTest(_T("va[2]+a"), 4.0, true);
iNumErr += EqnTest(_T("a+va[2]"), 4.0, true);
iNumErr += EqnTest(_T("va[2]*b"), 6.0, true);
iNumErr += EqnTest(_T("b*va[2]"), 6.0, true);
// Issue 68 (and related issues):
iNumErr += EqnTest(_T("(abs(-3)+2)>=min(6,5)"), true, true);
iNumErr += EqnTest(_T("(abs(-3))>abs(2)"), true, true);
iNumErr += EqnTest(_T("min(1,2,-3)>-4"), true, true);
iNumErr += EqnTest(_T("(abs(-3))>-2"), true, true);
iNumErr += EqnTest(_T("abs(-3)>abs(2)"), true, true);
// Issue 42:
// https://code.google.com/p/muparserx/issues/detail?id=42
v.At(0) = (float_type)1.0;
v.At(1) = (float_type)0.0;
v.At(2) = (float_type)0.0;
iNumErr += EqnTest(_T("{1,0,0}'"), v, true);
iNumErr += EqnTest(_T("{(1),0,0}'"), v, true);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestBinOp()
{
int iNumErr = 0;
*m_stream << _T("testing binary operators...");
float_type a = 1;
// standard aperators
iNumErr += EqnTest(_T("1+7"), (float_type)8.0, true);
iNumErr += EqnTest(_T("10-1"), (float_type)9.0, true);
iNumErr += EqnTest(_T("3*4"), (float_type)12.0, true);
iNumErr += EqnTest(_T("10/2"), (float_type)5.0, true);
// operator associativity
iNumErr += EqnTest(_T("2^2^3"), (float_type)256.0, true);
iNumErr += EqnTest(_T("3+4*2/(1-5)^2^3"), (float_type)3.0001220703125, true);
iNumErr += EqnTest(_T("1/2/3"), (float_type)1.0 / (float_type)6.0, true);
// operator precedencs
iNumErr += EqnTest(_T("1+2-3*4/5^6"), (float_type)2.99923, true);
iNumErr += EqnTest(_T("a+b-c*4/5^6"), (float_type)2.99923, true);
iNumErr += EqnTest(_T("1^2/3*4-5+6"), (float_type)2.3333, true);
iNumErr += EqnTest(_T("a^b/c*4-5+6"), (float_type)2.3333, true);
iNumErr += EqnTest(_T("1+2*3"), (float_type)7.0, true);
iNumErr += EqnTest(_T("a+b*c"), (float_type)7.0, true);
iNumErr += EqnTest(_T("(1+2)*3"), (float_type)9.0, true);
iNumErr += EqnTest(_T("(a+b)*c"), (float_type)9.0, true);
iNumErr += EqnTest(_T("(1+2)*(-3)"), (float_type)-9.0, true);
iNumErr += EqnTest(_T("(a+b)*(-c)"), (float_type)-9.0, true);
iNumErr += EqnTest(_T("2/4"), (float_type)0.5, true);
iNumErr += EqnTest(_T("4&4"), 4.0, true);
iNumErr += EqnTest(_T("2+2&(a+b+c)"), 4.0, true);
iNumErr += EqnTest(_T("3&3"), 3.0, true);
iNumErr += EqnTest(_T("c&3"), 3.0, true);
iNumErr += EqnTest(_T("(c)&3"), 3.0, true);
iNumErr += EqnTest(_T("(a+b)&3"), 3.0, true);
iNumErr += EqnTest(_T("(a+b+c)&6"), 6.0, true);
iNumErr += EqnTest(_T("(1+2+3)&6"), 6.0, true);
iNumErr += EqnTest(_T("3&c"), 3.0, true);
iNumErr += EqnTest(_T("(a<<1)+2"), 4.0, true);
iNumErr += EqnTest(_T("(a<<2)+2"), 6.0, true);
iNumErr += EqnTest(_T("(a<<3)+2"), 10.0, true);
iNumErr += EqnTest(_T("(a<<4)+2"), 18.0, true);
iNumErr += EqnTest(_T("(a<<5)+2"), 34.0, true);
iNumErr += EqnTest(_T("1<<31"), (float_type)2147483648, true);
iNumErr += EqnTest(_T("-1<<31"), (float_type)-2147483648.0, true);
iNumErr += EqnTest(_T("1<<45"), (float_type)35184372088832.0, true);
iNumErr += EqnTest(_T("-1<<45"), (float_type)-35184372088832.0, true);
iNumErr += EqnTest(_T("8<<-2"), 2.0, true);
iNumErr += EqnTest(_T("8<<-4"), 0.0, true);
// Issue 25: http://code.google.com/p/muparserx/issues/detail?id=25
iNumErr += ThrowTest(_T("55<<2222222"), ecOVERFLOW);
// Issue 16: http://code.google.com/p/muparserx/issues/detail?id=16
iNumErr += EqnTest(_T("true == true && false"), true == true && false, true);
iNumErr += EqnTest(_T("false == true && false"), false == true && false, true);
iNumErr += EqnTest(_T("a==1.0 && a==1.0"), a == 1.0 && a == 1.0, true);
// bool operators for comparing values
iNumErr += EqnTest(_T("a<b"), true, true);
iNumErr += EqnTest(_T("b>a"), true, true);
iNumErr += EqnTest(_T("a>a"), false, true);
iNumErr += EqnTest(_T("a<a"), false, true);
iNumErr += EqnTest(_T("a>a"), false, true);
iNumErr += EqnTest(_T("a<=a"), true, true);
iNumErr += EqnTest(_T("a<=b"), true, true);
iNumErr += EqnTest(_T("b<=a"), false, true);
iNumErr += EqnTest(_T("a>=a"), true, true);
iNumErr += EqnTest(_T("b>=a"), true, true);
iNumErr += EqnTest(_T("a>=b"), false, true);
// The following equations were raising type conflict errors once
// since the result of sqrt(1) is 1 which is an integer as fas as muParserX
// is concerned:
iNumErr += EqnTest(_T("sqrt(a)<sin(8)"), false, true);
iNumErr += EqnTest(_T("sqrt(a)<=sin(8)"), false, true);
iNumErr += EqnTest(_T("sqrt(a)>sin(8)"), true, true);
iNumErr += EqnTest(_T("sqrt(a)>=sin(8)"), true, true);
iNumErr += EqnTest(_T("sqrt(a)==sin(8)"), false, true);
iNumErr += EqnTest(_T("sqrt(a)!=sin(8)"), true, true);
iNumErr += EqnTest(_T("sqrt(a)+1.01"), (float_type)2.01, true);
iNumErr += EqnTest(_T("sqrt(a)-1.01"), (float_type)-0.01, true);
// interaction with sign operator
iNumErr += EqnTest(_T("3-(-a)"), 4.0, true);
iNumErr += EqnTest(_T("3--a"), 4.0, true);
// Problems with small bogus real/imag values introduced due to limited floating point accuracy
iNumErr += EqnTest(_T("(-2)^3"), -8.0, true); // may introduce incorrect imaginary value (When computed with the log/exp formula: -8 + 2.93e-15i)
iNumErr += EqnTest(_T("imag((-2)^3)==0"), true, true); // may introduce incorrect imaginary value (When computed with the log/exp formula: -8 + 2.93e-15i)
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestIfElse()
{
int iNumErr = 0;
*m_stream << _T("testing if-else conditional...");
float_type a = 1;
// test case copied from muparser (https://oss-fuzz.com/testcase-detail/4777121158529024)
iNumErr += ThrowTest(_T("3!=min(false?2>2,2>5,1:6)"), ecUNEXPECTED_COMMA);
// test case copied from muparser (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22922#c1)
iNumErr += ThrowTest(_T("1?2:0?(7:1)"), ecMISPLACED_COLON);
// test case copied from muparser (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23410)
// with variations
iNumErr += ThrowTest(_T(R"(false ? 4 : "", ? 4 : "", ? 4 : "")"), ecUNEXPECTED_COMMA);
iNumErr += EqnTest(_T(R"(false ? "four" : 4)"), 4, true);
iNumErr += EqnTest(_T(R"(true ? "four" : 4)"), "four", true);
iNumErr += EqnTest(_T(R"(true ? "foo" : "bar")"), "foo", true);
// test case and variations copied from muparser https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22938
iNumErr += ThrowTest(_T("sum(false?1,0,0:3)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sum(false?(1,0,0):3)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sum(2>3?2,4,2:4)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sum(2>3?2,4,sin(2):4)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sum(2>3?sin(a),4,2:4)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_COMMA);
// Test error detection
iNumErr += ThrowTest(_T(": 2"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL);
iNumErr += ThrowTest(_T("(a<b) ? (b<c) ? 1 : 2"), ecMISSING_ELSE_CLAUSE);
iNumErr += ThrowTest(_T("(a<b) ? 1"), ecMISSING_ELSE_CLAUSE);
iNumErr += ThrowTest(_T("(a<b) ? a"), ecMISSING_ELSE_CLAUSE);
iNumErr += ThrowTest(_T("(a<b) ? a+b"), ecMISSING_ELSE_CLAUSE);
iNumErr += ThrowTest(_T("a : b"), ecMISPLACED_COLON, 2);
iNumErr += ThrowTest(_T("1 : 2"), ecMISPLACED_COLON, 2);
iNumErr += ThrowTest(_T("(true) ? 1 : 2 : 3"), ecMISPLACED_COLON);
iNumErr += ThrowTest(_T("1==?"), ecUNEXPECTED_CONDITIONAL);
iNumErr += ThrowTest(_T("1+?"), ecUNEXPECTED_CONDITIONAL); // bin oprt + ?
iNumErr += ThrowTest(_T("1m?"), ecUNEXPECTED_CONDITIONAL); // postfix + ?
iNumErr += ThrowTest(_T("-?"), ecUNEXPECTED_CONDITIONAL); // infix + ?
iNumErr += EqnTest(_T("(true) ? 128 : 255"), 128.0, true);
iNumErr += EqnTest(_T("(1<2) ? 128 : 255"), 128.0, true);
iNumErr += EqnTest(_T("(a<b) ? 128 : 255"), 128.0, true);
iNumErr += EqnTest(_T("((a>b) ? true : false) ? 1 : 2"), 2.0, true);
iNumErr += EqnTest(_T("((a>b) ? true : false) ? 1 : sum((a>b) ? 1 : 2)"), 2.0, true);
iNumErr += EqnTest(_T("((a>b) ? false : true) ? 1 : sum((a>b) ? 1 : 2)"), 1.0, true);
iNumErr += EqnTest(_T("(true) ? 10 : 11"), 10.0, true);
iNumErr += EqnTest(_T("(true) ? a+b : c+d"), 3.0, true);
iNumErr += EqnTest(_T("(true) ? false : true"), false, true);
iNumErr += EqnTest(_T("(false) ? 10 : 11"), 11.0, true);
iNumErr += EqnTest(_T("(false) ? a+b : c+d"), 1.0, true);
iNumErr += EqnTest(_T("(false) ? false : true"), true, true);
iNumErr += EqnTest(_T("(a<b) ? 10 : 11"), 10.0, true);
iNumErr += EqnTest(_T("(a>b) ? 10 : 11"), 11.0, true);
iNumErr += EqnTest(_T("(a<b) ? c : d"), 3.0, true);
iNumErr += EqnTest(_T("(a>b) ? c : d"), -2.0, true);
iNumErr += EqnTest(_T("(a>b) ? true : false"), false, true);
// With multiarg functions
iNumErr += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2.0, true);
iNumErr += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102.0, true);
iNumErr += EqnTest(_T("sum((true) ? 1 : 2)"), 1.0, true);
iNumErr += EqnTest(_T("sum((true) ? 1 : 2, 100)"), 101.0, true);
iNumErr += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13.0, true);
iNumErr += EqnTest(_T("sum(3, (a<b) ? 3 : 10)"), 6., true);
iNumErr += EqnTest(_T("sum(3, (a>b) ? 3 : 10)*10"), 130.0, true);
iNumErr += EqnTest(_T("sum(3, (a<b) ? 3 : 10)*10"), 60.0, true);
iNumErr += EqnTest(_T("10*sum(3, (a>b) ? 3 : 10)"), 130.0, true);
iNumErr += EqnTest(_T("10*sum(3, (a<b) ? 3 : 10)"), 60.0, true);
iNumErr += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 60.0, true);
iNumErr += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 99.0, true);
iNumErr += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 360.0, true);
iNumErr += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 99.0, true);
iNumErr += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : sum(3, (a<b) ? 3 : 10)*10"), 60.0, true);
iNumErr += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128.0, true);
iNumErr += EqnTest(_T("(a>b)&&(a<b) ? 128 : 255"), 255.0, true);
iNumErr += EqnTest(_T("(1<2)&&(1<2) ? 128 : 255"), 128.0, true);
iNumErr += EqnTest(_T("(1>2)&&(1<2) ? 128 : 255"), 255.0, true);
iNumErr += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128.0, true);
iNumErr += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255.0, true);
iNumErr += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128.0, true);
iNumErr += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255.0, true);
// nested conditionals with brackets
iNumErr += EqnTest(_T("(a<b) ? ((b<c) ? 2*(a+b) : 2) : 3"), 6., true);
iNumErr += EqnTest(_T("(a<b) ? 3 : ((b<c) ? 2*(a+b) : 2)"), 3., true);
iNumErr += EqnTest(_T("(a<b) ? ((b>c) ? 1 : 2) : 3"), 2., true);
iNumErr += EqnTest(_T("(a>b) ? ((b<c) ? 1 : 2) : 3"), 3., true);
iNumErr += EqnTest(_T("(a>b) ? ((b>c) ? 1 : 2) : 3"), 3., true);
// nested conditionals without brackets
iNumErr += EqnTest(_T("(a<b) ? (b<c) ? 1 : 2 : 3"), 1., true);
iNumErr += EqnTest(_T("(a<b) ? (b>c) ? 1 : 2 : 3"), 2., true);
iNumErr += EqnTest(_T("(a>b) ? (b<c) ? 1 : 2 : 3"), 3., true);
iNumErr += EqnTest(_T("(a>b) ? (b>c) ? 1 : 2 : 3"), 3., true);
// Neue Tests
iNumErr += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128., true);
iNumErr += EqnTest(_T("(a>b)&&(a<b) ? 128 : 255"), 255., true);
iNumErr += EqnTest(_T("(1<2)&&(1<2) ? 128 : 255"), 128., true);
iNumErr += EqnTest(_T("(1>2)&&(1<2) ? 128 : 255"), 255., true);
iNumErr += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128., true);
iNumErr += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255., true);
iNumErr += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128., true);
iNumErr += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255., true);
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255., true);
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255., true);
iNumErr += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128., true);
iNumErr += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128., true);
iNumErr += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32., true);
iNumErr += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64., true);
iNumErr += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50., true);
iNumErr += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50., true);
iNumErr += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128., true);
iNumErr += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32., true);
iNumErr += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32., true);
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255., true);
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255., true);
iNumErr += EqnTest(_T("true ? false ? 128 : 255 : true ? 32 : 64"), 255., true);
// assignment operators
iNumErr += EqnTest(_T("a= false ? 128 : 255"), 255., true);
iNumErr += EqnTest(_T("a=((a>b)&&(a<b)) ? 128 : 255"), 255., true);
iNumErr += EqnTest(_T("c=(a<b)&&(a<b) ? 128 : 255"), 128., true);
iNumErr += EqnTest(_T("a=true?b=true?3:4:5"), 3., true);
iNumErr += EqnTest(_T("a=false?b=true?3:4:5"), 5., true);
iNumErr += EqnTest(_T("a=true?5:b=true?3:4"), 5., true);
iNumErr += EqnTest(_T("a=false?5:b=true?3:4"), 3., true);
// Issue 42: ?: operator must be last in expression
// https://code.google.com/p/muparserx/issues/detail?id=42
iNumErr += EqnTest(_T("abs(0.1) < 0.25 ? (-1) : (1) + 1"), fabs(0.1) < 0.25 ? (-1.) : (1.) + 1, true);
iNumErr += EqnTest(_T("abs(a) < 0.25 ? (-1) : (1) + 1"), fabs(a) < 0.25 ? (-1.) : (1.) + 1, true);
iNumErr += EqnTest(_T("(abs(a) < 0.25 ? -1 : 1)"), (abs(a) < 0.25 ? -1. : 1.), true);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestEqn()
{
int iNumErr = 0;
*m_stream << _T("testing sample equations...");
// test case copied from muparser: https://oss-fuzz.com/testcase-detail/5106868061208576
iNumErr += ThrowTest(_T(R"("","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",8)"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T(R"("","",9)"), ecUNEXPECTED_COMMA);
// test case copied from muparser: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23330#c1
iNumErr += ThrowTest(_T("6, +, +, +, +, +, +, +, +, +, +, +, +, +, +, 1, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +"), ecUNEXPECTED_COMMA);
iNumErr += ThrowTest(_T("1e1234"), ecUNASSIGNABLE_TOKEN);
iNumErr += ThrowTest(_T("-1e1234"), ecUNASSIGNABLE_TOKEN);
iNumErr += EqnTest(_T("-2--8"), (float_type)6.0, true);
iNumErr += EqnTest(_T("2*(a=9)*3"), 54., true);
// Functions
iNumErr += EqnTest(_T("10*strlen(toupper(\"12345\"))"), 50., true);
// hex value recognition
iNumErr += EqnTest(_T("0xff"), 255., true);
iNumErr += EqnTest(_T("10+0xff"), 265., true);
iNumErr += EqnTest(_T("0xff+10"), 265., true);
iNumErr += EqnTest(_T("10*0xff"), 2550., true);
iNumErr += EqnTest(_T("0xff*10"), 2550., true);
iNumErr += EqnTest(_T("10+0xff+1"), 266., true);
iNumErr += EqnTest(_T("1+0xff+10"), 266., true);
// ...
iNumErr += EqnTest(_T("exp(ln(7))"), 7., true);
iNumErr += EqnTest(_T("e^ln(7)"), 7., true);
iNumErr += EqnTest(_T("e^(ln(7))"), 7., true);
iNumErr += EqnTest(_T("(e^(ln(7)))"), 7., true);
iNumErr += EqnTest(_T("1-(e^(ln(7)))"), -6., true);
iNumErr += EqnTest(_T("2*(e^(ln(7)))"), 14., true);
iNumErr += EqnTest(_T("10^log10(5)"), 5., true);
iNumErr += EqnTest(_T("10^log10(5)"), 5., true);
iNumErr += EqnTest(_T("2^log2(4)"), (float_type)4.0, true);
iNumErr += EqnTest(_T("-(sin(0)+1)"), (float_type)-1.0, true);
iNumErr += EqnTest(_T("-(2^1.1)"), (float_type)-2.14354692, true);
// infix + postfix operator in arguments for binary operators (Reference: Matlab)
iNumErr += EqnTest(_T("-sin(8)m*6"), (float_type)-0.00593615, true);
iNumErr += EqnTest(_T("-sin(8)m/6"), (float_type)-1.6489e-4, true);
iNumErr += EqnTest(_T("-sin(8)m+6"), (float_type)5.99901, true);
iNumErr += EqnTest(_T("-sin(8)m-6"), (float_type)-6.000989, true);
iNumErr += EqnTest(_T("(cos(2.41)/b)"), (float_type)-0.372056, true);
// long formula (Reference: Matlab)
iNumErr += EqnTest(
_T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))")
_T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/")
_T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-")
_T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6")
_T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e")
_T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), (float_type)-12.23016549, true);
// long formula (Reference: Matlab)
iNumErr += EqnTest(_T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), (float_type)-7995810.09926, true);
/* <ibg 20100321 atan currently unsupported/>
// long formula (Reference: Matlab)
iNumErr += EqnTest(
"(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e"
")+a)))*2.77)", -2.16995656, true);
*/
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestScript()
{
int iNumErr = 0;
*m_stream << _T("testing script features...");
// Test error detection
iNumErr += ThrowTest(_T("sin(\n"), ecUNEXPECTED_NEWLINE);
iNumErr += ThrowTest(_T("1+\n"), ecUNEXPECTED_NEWLINE);
iNumErr += ThrowTest(_T("a*\n"), ecUNEXPECTED_NEWLINE);
iNumErr += ThrowTest(_T("va[\n"), ecUNEXPECTED_NEWLINE);
iNumErr += ThrowTest(_T("(true) ? \n"), ecUNEXPECTED_NEWLINE);
iNumErr += ThrowTest(_T("(true) ? 10:\n"), ecUNEXPECTED_NEWLINE);
// Expressions spanning multiple lines
iNumErr += EqnTest(_T("a=1\n")
_T("b=2\n")
_T("c=3\n")
_T("a+b+c"), 6., true);
// Ending an expression with a newline
iNumErr += EqnTest(_T("3\n"), 3., true);
iNumErr += EqnTest(_T("1+2\n"), 3., true);
iNumErr += EqnTest(_T("\n1+2\n"), 3., true);
iNumErr += EqnTest(_T("\n1+2\n\na+b"), 3., true);
// Testing comments
/* 20130107 Not yet...
iNumErr += EqnTest(_T("a=10 % this is a comment\n")
_T("b=23 % this is another comment\n")
_T("a+b"), 33, true);
*/
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
int ParserTester::TestValReader()
{
int iNumErr = 0;
*m_stream << _T("testing value reader...");
// Hex value reader
iNumErr += EqnTest(_T("0x1"), 1., true);
iNumErr += EqnTest(_T("0x1+0x2"), 3., true);
iNumErr += EqnTest(_T("0xff"), 255., true);
// Reading of binary values
iNumErr += EqnTest(_T("0b1"), 1., true);
iNumErr += EqnTest(_T("0b01"), 1., true);
iNumErr += EqnTest(_T("0b11"), 3., true);
iNumErr += EqnTest(_T("0b011"), 3., true);
iNumErr += EqnTest(_T("0b11111111"), 255., true);
iNumErr += EqnTest(_T("b*0b011"), 6.0, true);
iNumErr += EqnTest(_T("0b1111111111111111111111111111111"), 2147483647., true);
iNumErr += EqnTest(_T("0b10000000000000000000000000000000"), -2147483647. - 1, true);
iNumErr += EqnTest(_T("0b11111111111111111111111111111111"), -1., true);
iNumErr += ThrowTest(_T("0b100000000000000000000000000000000"), ecUNDEFINED);
// string value reader
iNumErr += EqnTest(_T("\"hallo\""), _T("hallo"), true);
// boolean value reader
iNumErr += EqnTest(_T("true"), true, true);
iNumErr += EqnTest(_T("false"), false, true);
// boolean value reader
iNumErr += EqnTest(_T("true"), true, true);
iNumErr += EqnTest(_T("false"), false, true);
// mixed
iNumErr += EqnTest(_T("0b011+0xef"), 242., true);
Assessment(iNumErr);
return iNumErr;
}
//---------------------------------------------------------------------------
void ParserTester::AddTest(testfun_type a_pFun)
{
m_vTestFun.push_back(a_pFun);
}
//---------------------------------------------------------------------------
void ParserTester::Run()
{
int iStat = 0;
try
{
for (int i = 0; i < (int)m_vTestFun.size(); ++i)
iStat += (this->*m_vTestFun[i])();
}
catch (ParserError &e)
{
*m_stream << e.GetMsg() << endl;
*m_stream << e.GetToken() << endl;
Abort();
}
catch (std::exception &e)
{
*m_stream << e.what() << endl;
Abort();
}
catch (...)
{
*m_stream << _T("Internal error");
Abort();
}
if (iStat == 0)
{
*m_stream << _T("Test passed (") << ParserTester::c_iCount << _T(" expressions)") << endl;
}
else
{
*m_stream << _T("Test failed with ") << iStat
<< _T(" errors (") << ParserTester::c_iCount
<< _T(" expressions)") << endl;
}
ParserTester::c_iCount = 0;
#ifdef MUP_LEAKAGE_REPORT
// All tokens must have been destroyed by now, verify this
IToken::LeakageReport();
#endif
}
//---------------------------------------------------------------------------
int ParserTester::ThrowTest(const string_type &a_sExpr, int a_nErrc, int a_nPos, string_type a_sIdent)
{
ParserTester::c_iCount++;
try
{
ParserX p;
// Add variables
Value vVarVal[] = { 1., 2., 3., -2. };
p.DefineVar(_T("a"), Variable(&vVarVal[0]));
p.DefineVar(_T("b"), Variable(&vVarVal[1]));
p.DefineVar(_T("c"), Variable(&vVarVal[2]));
p.DefineVar(_T("d"), Variable(&vVarVal[3]));
// array variables
Value aVal1(3, 0);
aVal1.At(0) = (float_type)1.0;
aVal1.At(1) = (float_type)2.0;
aVal1.At(2) = (float_type)3.0;
Value aVal2(3, 0);
aVal2.At(0) = (float_type)4.0;
aVal2.At(1) = (float_type)3.0;
aVal2.At(2) = (float_type)2.0;
Value aVal3(4, 0);
aVal3.At(0) = (float_type)4.0;
aVal3.At(1) = (float_type)3.0;
aVal3.At(2) = (float_type)2.0;
aVal3.At(3) = (float_type)5.0;
Value aVal4(4, 0);
aVal4.At(0) = (float_type)4.0;
aVal4.At(1) = false;
aVal4.At(2) = _T("hallo");
// Matrix variables
Value m1(3, 3, 0);
m1.At(0, 0) = 1.;
m1.At(1, 1) = 1.;
m1.At(2, 2) = 1.;
Value m2(3, 3, 0);
m2.At(0, 0) = 1.; m2.At(0, 1) = 2.; m2.At(0, 2) = 3.;
m2.At(1, 0) = 4.; m2.At(1, 1) = 5.; m2.At(1, 2) = 6.;
m2.At(2, 0) = 7.; m2.At(2, 1) = 8.; m2.At(2, 2) = 9.;
p.DefineVar(_T("m1"), Variable(&m1));
p.DefineVar(_T("m2"), Variable(&m2));
p.DefineVar(_T("va"), Variable(&aVal1));
p.DefineVar(_T("vb"), Variable(&aVal2));
p.DefineVar(_T("vc"), Variable(&aVal3));
p.DefineVar(_T("vd"), Variable(&aVal4));
p.SetExpr(a_sExpr);
Value fRes = p.Eval();
}
catch (ParserError &e)
{
// output the formula in case of an failed test
if (a_nErrc != e.GetCode())
{
*m_stream << _T("\n ")
<< _T("Expression: \"") << a_sExpr
<< _T("\" Code:") << e.GetCode()
<< _T(" Expected:") << a_nErrc;
}
// Check whether the error is reported at the correct expression position
if (a_nPos != -1 && a_nPos != e.GetPos())
{
*m_stream << _T("\n ")
<< _T("Invalid error position: \"") << a_sExpr
<< _T("\" Pos:") << e.GetPos()
<< _T(" Expected:") << a_nPos;
}
if (a_sIdent.length() && a_sIdent != e.GetContext().Ident)
{
*m_stream << _T("\n ")
<< _T("Invalid identifier: \"") << a_sExpr
<< _T("\" Ident:") << e.GetContext().Ident
<< _T(" Expected:") << a_sIdent;
}
return (a_nErrc == e.GetCode() && (a_nPos == -1 || a_nPos == e.GetPos())) ? 0 : 1;
}
*m_stream << _T("\n ")
<< _T("Expression failed: \"")
<< a_sExpr
<< _T("\" (no exception raised).");
return 1;
}
//---------------------------------------------------------------------------
int ParserTester::EqnTest(const string_type &a_str, Value a_val, bool a_fPass, int nExprVar)
{
ParserTester::c_iCount++;
int iRet(1);
Value fVal[5];
try
{
// p1 is a pointer since I'm going to delete it in order to test if
// parsers after copy construction still refer to members of the deleted object.
// !! If this is the case this function will crash !!
std::unique_ptr<ParserX> p1(new ParserX());
// Add variables
Value vVarVal[] = { 1., 2., 3., -2., -1. };
// m1 ist die Einheitsmatrix
Value m1(3, 3, 0);
m1.At(0, 0) = 1.;
m1.At(1, 1) = 1.;
m1.At(2, 2) = 1.;
// m2 ist die Einheitsmatrix
Value m2(3, 3, 0);
m2.At(0, 0) = 1.; m2.At(0, 1) = 2.; m2.At(0, 2) = 3.;
m2.At(1, 0) = 4.; m2.At(1, 1) = 5.; m2.At(1, 2) = 6.;
m2.At(2, 0) = 7.; m2.At(2, 1) = 8.; m2.At(2, 2) = 9.;
p1->DefineOprt(new DbgSillyAdd);
p1->DefineFun(new FunTest0);
p1->DefineVar(_T("a"), Variable(&vVarVal[0]));
p1->DefineVar(_T("b"), Variable(&vVarVal[1]));
p1->DefineVar(_T("c"), Variable(&vVarVal[2]));
p1->DefineVar(_T("d"), Variable(&vVarVal[3]));
p1->DefineVar(_T("f"), Variable(&vVarVal[4]));
p1->DefineVar(_T("m1"), Variable(&m1));
p1->DefineVar(_T("m2"), Variable(&m2));
// Add constants
p1->DefineConst(_T("const"), 1.);
p1->DefineConst(_T("const1"), 2.);
p1->DefineConst(_T("const2"), 3.);
// some vector variables
Value aVal1(3, 0);
aVal1.At(0) = (float_type)1.0;
aVal1.At(1) = (float_type)2.0;
aVal1.At(2) = (float_type)3.0;
Value aVal2(3, 0);
aVal2.At(0) = (float_type)4.0;
aVal2.At(1) = (float_type)3.0;
aVal2.At(2) = (float_type)2.0;
p1->DefineVar(_T("va"), Variable(&aVal1));
p1->DefineVar(_T("vb"), Variable(&aVal2));
// complex variables
Value cVal[3];
cVal[0] = mup::cmplx_type(1, 1);
cVal[1] = mup::cmplx_type(2, 3);
cVal[2] = mup::cmplx_type(3, 4);
p1->DefineVar(_T("ca"), Variable(&cVal[0]));
p1->DefineVar(_T("cb"), Variable(&cVal[1]));
p1->DefineVar(_T("cc"), Variable(&cVal[2]));
p1->SetExpr(a_str);
fVal[0] = p1->Eval();
// Test copy and assignement operators
std::vector<ParserX> vParser;
vParser.push_back(*p1); // Push p1 into the vector
ParserX p2 = vParser[0]; // take parser from vector
// destroy the originals from p2
vParser.clear(); // delete the vector
p1.reset(0); // delete the original
fVal[1] = p2.Eval(); // If copy constructions does not work
// we may see a crash here
// Test assignement operator
// additionally disable Optimizer this time
ParserX p3;
p3 = p2;
fVal[2] = p3.Eval(); // If assignment does not work
// we may see a crash here
// Calculating a second time will parse from rpn rather than from
// string. The result must be the same...
fVal[3] = p3.Eval();
// Calculate yet another time. There is the possibility of
// changing variables as a side effect of expression
// evaluation. So there are really bugs that could make this fail...
fVal[4] = p3.Eval();
// Check i number of used variables is correct
if (nExprVar != -1)
{
std::size_t n2 = p2.GetExprVar().size();
std::size_t n3 = p3.GetExprVar().size();
if (n2 + n3 != 2 * n2 || int(n2) != nExprVar)
{
*m_stream << _T(" Number of expression variables is incorrect. (expected: ")
<< nExprVar << _T("; detected: ") << n2 << _T(")");
}
}
// Check the three results
// 1.) computed results must have identic type
char_type cType = fVal[0].GetType();
bool bStat = cType == fVal[1].GetType() &&
cType == fVal[2].GetType() &&
cType == fVal[3].GetType() &&
cType == fVal[4].GetType();
if (!bStat)
{
*m_stream << _T("\n ") << a_str << _T(" : inconsistent result type (")
<< fVal[0].GetType() << _T(", ")
<< fVal[1].GetType() << _T(", ")
<< fVal[2].GetType() << _T(", ")
<< fVal[3].GetType() << _T(", ")
<< fVal[4].GetType() << _T(")");
return 1;
}
if ((cType == 'c' || a_val.GetType() == 'c') && cType != a_val.GetType())
{
*m_stream << _T("\n ") << a_str << _T(" : Complex value sliced!");
return 1;
}
// Compare the results
switch (cType)
{
case 'i':
case 'b':
case 's': bStat = (a_val == fVal[0] &&
a_val == fVal[1] &&
a_val == fVal[2] &&
a_val == fVal[3] &&
a_val == fVal[4]);
break;
// We need more attention for comaring float values due to floating point
// inaccuracies.
case 'f':
{
bStat = true;
int num = sizeof(fVal) / sizeof(Value);
for (int i = 0; i < num; ++i)
bStat &= (fabs(a_val.GetFloat() - fVal[i].GetFloat()) <= fabs(fVal[i].GetFloat()*0.0001));
}
break;
case 'c':
{
bStat = true;
int num = sizeof(fVal) / sizeof(Value);
for (int i = 0; i < num; ++i)
{
bStat &= (fabs(a_val.GetFloat() - fVal[i].GetFloat()) <= std::max((float_type)1e-15, fabs(fVal[i].GetFloat() * (float_type)0.0000001)));
bStat &= (fabs(a_val.GetImag() - fVal[i].GetImag()) <= std::max((float_type)1e-15, fabs(fVal[i].GetImag() * (float_type)0.0000001)));
}
}
break;
case 'm':
{
bStat = true;
int num = sizeof(fVal) / sizeof(Value);
for (int i = 0; i < num; ++i)
{
struct CheckArray
{
CheckArray()
{}
bool Check(IValue &v1, IValue &v2)
{
if (v1.GetType() != v2.GetType())
return false;
if (v1.GetRows() != v2.GetRows())
return false;
if (v1.IsMatrix())
{
for (int i = 0; i < v1.GetRows(); ++i)
{
for (int j = 0; j < v1.GetCols(); ++j)
{
if (!Check(v1.At(i, j), v2.At(i, j)))
return false;
}
}
return true;
}
else
{
return (fabs(v1.GetFloat() - v2.GetFloat()) <= std::max((float_type)1e-15, fabs(v1.GetFloat() * (float_type)0.0000001)));
}
}
} checker;
bStat = checker.Check(a_val, fVal[i]);
if (!bStat)
break;
}
}
break;
default:
throw std::runtime_error("Parser return value has an unexpected typecode.");
}
iRet = (bStat == a_fPass) ? 0 : 1;
}
catch (ParserError &e)
{
*m_stream << _T("\n ") << a_str << _T(" : ") << e.GetMsg();
return 1;
}
catch (std::exception &e)
{
*m_stream << _T("\n ") << a_str << _T(" : ") << e.what() << _T("\n");
return 1;
}
catch (...)
{
*m_stream << _T("\n \"") << a_str << _T("\" : ") << _T("Unexpected Eception");
return 1;
}
if (iRet)
{
*m_stream << _T("\n ") << a_str << _T(" : ") << _T("(at least one incorrect result ")
<< fVal[0] << _T(", ")
<< fVal[1] << _T(", ")
<< fVal[2] << _T(", ")
<< fVal[3] << _T(", ")
<< fVal[4] << _T("; expected=") << a_val << _T(")");
}
return iRet;
}
//---------------------------------------------------------------------------
/** \brief Internal error in test class Test is going to be aborted. */
void ParserTester::Abort() const
{
*m_stream << _T("\nTest failed (internal error in test class)") << endl;
while (!getchar());
exit(-1);
}
//---------------------------------------------------------------------------
void ParserTester::Assessment(int a_iNumErr) const
{
if (a_iNumErr == 0)
*m_stream << _T("passed") << endl;
else
*m_stream << _T("\n failed with ") << a_iNumErr << _T(" errors") << endl;
}
MUP_NAMESPACE_END