/*
								+---------------------------------+
								|                                 |
								| ***  Expression evaluation  *** |
								|                                 |
								|  Copyright  © -tHE SWINe- 2011  |
								|                                 |
								|           ExpEval.inl           |
								|                                 |
								+---------------------------------+
*/

#pragma once
#ifndef __EXPRESSION_EVALUATOR_INLINES_INCLUDED
#define __EXPRESSION_EVALUATOR_INLINES_INCLUDED

/**
 *	@file ExpEval.inl
 *	@author -tHE SWINe-
 *	@brief simple expression evaluation template
 *	@date 2011
 */

/*
 *								=== CExpEvalLexer ===
 */

namespace exp_eval {

/**
 *	@brief a simple lexical analyzer for the expression evaluation class
 */
class CExpEvalLexer {
protected:
	mutable const char *m_p_s_expr; /**< @brief string with expression being analyzed */

public:
	/**
	 *	@brief default constructor
	 *	@param[in] p_s_expr is the string with expression being analyzed
	 */
	CExpEvalLexer(const char *p_s_expr);

	/**
	 *	@brief reads a single-character operator
	 *	@param[in] p_s_op_list is a list of single-char operators in
	 *		a null-terminated string (eg. "+*-/()!~^&|")
	 *	@return Returns character code of the operator, or 0 in case
	 *		the operator was not found on input.
	 *	@note This should be used in conjunction with n_PeekOperator2()
	 *		to make sure the detected operator is not a part of two-character
	 *		operator (eg. reading "||" as "|").
	 */
	int n_LookForOperator(const char *p_s_op_list);

	/**
	 *	@brief looks for a two-character operator
	 *	@param[in] p_s_twochar_op_list is a list of two-char operators in
	 *		a null-terminated string (eg. "==!=<=>=<<>>")
	 *	@return Returns (zero-based) index of the operator in the list in case
	 *		the corresponding operator was found on input, otherwise returns -1.
	 *	@note This function doesn't modify cursor position (except for skipping whitespace).
	 */
	int n_PeekOperator2(const char *p_s_twochar_op_list) const;

	/**
	 *	@brief reads a two-character operator
	 *	@param[in] p_s_twochar_op_list is a list of two-char operators in
	 *		a null-terminated string (eg. "==!=<=>=<<>>")
	 *	@return Returns (zero-based) index of the operator in the list in case
	 *		the corresponding operator was found on input, otherwise returns -1.
	 */
	int n_LookForOperator2(const char *p_s_twochar_op_list);

	/**
	 *	@brief reads a "C" identifier
	 *
	 *	@param[out] r_p_ident is filled with pointer to the first identifier character
	 *	@param[out] r_n_length is filled with length of the identifier in characters
	 *
	 *	@return Returns true on success (there was an identifier on input),
	 *		otherwise returns false (the output values are undefined in such case).
	 *
	 *	@note This supports chaining identifier names using '.', provided
	 *		there are no whitespace characters (this is kind of dirty hack).
	 */
	bool LookForIdentifier(const char *&r_p_ident, size_t &r_n_length);

	/**
	 *	@brief reads a number literal
	 *
	 *	@param[out] r_n_value is filled with integer numeric literal value
	 *	@param[out] r_f_value is filled with floating-point numeric literal value
	 *	@param[out] r_b_is_integer is set if the parsed number was integer
	 *
	 *	@return Returns true on success (there was a number on input),
	 *		otherwise returns false (the output value is undefined in such case).
	 *
	 *	@note This reads <tt>0x[0-9a-fA-F]+ | [+\-][0-9]+ | [+\-][0-9]*.[0-9]+([eE][+\-]?[0-9]+)?</tt>.
	 *	@note Both r_n_value and r_f_value always contain the parsed number
	 *		value, r_b_is_integer is kind of guideline of which value to use.
	 */
	bool LookForNumberLiteral(int64_t &r_n_value, double &r_f_value, bool &r_b_is_integer);

	/**
	 *	@brief reads a number literal and converts it to the required type
	 *	@param[in] _Ty is data type to store literal in
	 *	@param[out] r_t_value is filled with numeric literal value
	 *	@return Returns true on success (there was a number on input),
	 *		otherwise returns false (the output value is undefined in such case).
	 *	@note This reads <tt>0x[0-9a-fA-F]+ | [+\-][0-9]+ | [+\-][0-9]*.[0-9]+([eE][+\-]?[0-9]+)?</tt>.
	 */
	template <class _Ty>
	bool LookForNumberLiteral(_Ty &r_t_value);

	/**
	 *	@brief decides whether the cursor is at the end of the input string
	 *	@return Returns true if there is nothing but whitespace left on input,
	 *		otherwise returns false.
	 */
	bool b_AtEnd() const;

	/**
	 *	@brief gets cursor position
	 *	@return Returns pointer to the string being analyzed,
	 *		pointing at the current cursor position.
	 */
	const char *p_s_CurrentPosition() const;

protected:
	void SkipSpace() const;
};

} // ~exp_eval

template <class _Ty>
bool exp_eval::CExpEvalLexer::LookForNumberLiteral(_Ty &r_t_value)
{
	int64_t n_value;
	double f_value;
	bool b_is_integer;
	if(LookForNumberLiteral(n_value, f_value, b_is_integer)) {
		if(b_is_integer)
			r_t_value = _Ty(n_value);
		else
			r_t_value = _Ty(f_value);
		return true;
	}
	return false;
}

/*
 *								=== ~CExpEvalLexer ===
 */

/*
 *								=== CExpression::TNode ===
 */

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode::TNode(TNode *p_left, TNode *p_right, TNode *p_third)
	:p_eval(0), p_value(0)
{
	p_node[0] = p_left;
	p_node[1] = p_right;
	p_node[2] = p_third;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode::~TNode()
{
	for(int n = 0; n < max_FunctionParam_Num; ++ n) {
		if(p_node[n])
			delete p_node[n];
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
size_t CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode::n_Node_Num() const
{
	size_t n_node_num = 1; // self
	for(int n = 0; n < max_FunctionParam_Num; ++ n) {
		if(p_node[n])
			n_node_num += p_node[n]->n_Node_Num();
	}
	return n_node_num;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
void CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode::Eliminate_ConstExpressions(
	_Ty (*p_eval_var_ptr)(const TNode &r_this), _Ty (*p_eval_const_ptr)(const TNode &r_this)) // throw(std::runtime_error)
{
	if((p_node[0] || p_node[1] || p_node[2]) && b_IsConstant(p_eval_var_ptr)) {
		_Ty _t_value = (*p_eval)(*this);
		// evaluate self

		p_eval = p_eval_const_ptr;
		t_value = _t_value;
		// change node type to const

		for(int n = 0; n < max_FunctionParam_Num; ++ n) {
			if(p_node[n]) {
				delete p_node[n];
				p_node[n] = 0;
			}
		}
		// delete subnodes

		return;
	}
	// this node is non-leaf constant, change it to a number

	for(int n = 0; n < max_FunctionParam_Num; ++ n) {
		if(p_node[n])
			p_node[n]->Eliminate_ConstExpressions(p_eval_var_ptr, p_eval_const_ptr);
	}
	// recursively try the subnodes
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
bool CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode::b_IsConstant(
	_Ty (*p_eval_var_ptr)(const TNode &r_this))
{
	if(p_eval == p_eval_var_ptr) { // if we ever support rand() function, this will be broken
		_ASSERTE(!p_node[0] && !p_node[1] && !p_node[2]);
		// variable nodes should not have subnodes

		return false;
	}
	// determine whether the function is evaluating a variable (then it is not a constant)

	if((p_node[0] && !p_node[0]->b_IsConstant(p_eval_var_ptr)) ||
	   (p_node[1] && !p_node[1]->b_IsConstant(p_eval_var_ptr)) ||
	   (p_node[2] && !p_node[2]->b_IsConstant(p_eval_var_ptr)))
		return false;
	// if any of the subnodes is variable, this node is not a constant

	return true;
	// this node is a function of constants: the result is a constant
}

/*
 *								=== ~CExpression::TNode ===
 */

/*
 *								=== CExpression ===
 */

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
CExpression<_Ty, _TyInt, b_integer_exception_handling>::CExpression(const char *p_s_expression)
	:m_p_tree(0)
{
	if(!Parse(p_s_expression)) {
		if(m_p_tree)
			delete m_p_tree;
		m_p_tree = 0; // to mark error
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
CExpression<_Ty, _TyInt, b_integer_exception_handling>::~CExpression()
{
	if(m_p_tree)
		delete m_p_tree;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
bool CExpression<_Ty, _TyInt, b_integer_exception_handling>::RegisterFunction(const char *p_s_name, size_t n_arg_num, _Ty (*p_func)(const TNode&))
{
	if(n_arg_num > max_FunctionParam_Num)
		return false; // too many args
	if(m_function_name_table.find(p_s_name) != m_function_name_table.end())
		return false; // already exists
	try {
		std::set<std::string>::iterator p_fname_it = m_function_name_table.insert(p_s_name);
		// put the name in a table so it can be deleted by the caller

		TFuncEntry t_func;
		t_func.n_arg_num = n_arg_num;
		t_func.n_length = (*p_fname_it).length();
		t_func.p_func = p_func;
		t_func.p_s_name = (*p_fname_it).c_str();
		// make a function entry

		m_function_table.push_back(t_func);
		// put it in the table
	} catch(std::bad_alloc&) {
		return false;
	}
	return true;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
bool CExpression<_Ty, _TyInt, b_integer_exception_handling>:: Parse(const char *p_s_expression)
{
	if(m_p_tree) {
		delete m_p_tree;
		m_p_tree = 0;
	}
	m_symbol_table.clear();
	// cleanup

	CExpEvalLexer lexer(p_s_expression);
	// initialize lexer

	if(!(m_p_tree = p_Parse_La(lexer)))
		return false;
	// parse

	if(!lexer.b_AtEnd()) {
		delete m_p_tree;
		m_p_tree = 0;
		return false;
	}
	// make sure there's nothing left

	return true;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
size_t CExpression<_Ty, _TyInt, b_integer_exception_handling>::n_Optimize() // throw(std::runtime_error)
{
	if(!m_p_tree)
		return size_t(-1);

	size_t n_node_num = m_p_tree->n_Node_Num();
	m_p_tree->Eliminate_ConstExpressions(&EvalVar, &EvalConst);
	return n_node_num - m_p_tree->n_Node_Num(); // return how many nodes we saved
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
bool CExpression<_Ty, _TyInt, b_integer_exception_handling>:: SetVariable(const char *p_s_symbol_name, _Ty t_value)
{
	TVariableIter p_sym_it = m_symbol_table.find(p_s_symbol_name);
	if(p_sym_it != m_symbol_table.end()) {
		(*p_sym_it).second = t_value;
		return true;
	}
	return false;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::t_Evaluate() const // throw(std::runtime_error)
{
	if(!m_p_tree)
		return _Ty(0);
	return (*m_p_tree->p_eval)(*m_p_tree);
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_La(CExpEvalLexer &r_lexer)
{
	TNode *p_head, *p_rightmost = 0;
	if(!(p_head = p_Parse_Lb(r_lexer)))
		return 0;
	// parse the head (becomes the condition of the ternary operator if there is any)

	for(;;) {
		if(!r_lexer.n_LookForOperator("?"))
			return p_head;
		// do we have ternary op?

		TNode *p_true;
		if(!(p_true = p_Parse_La(r_lexer))) {
			delete p_head;
			return 0;
		}
		// "true" value

		if(r_lexer.n_PeekOperator2("::") >= 0 || !r_lexer.n_LookForOperator(":")) {
			delete p_head;
			delete p_true;
			return 0;
		}
		// ':'

		TNode *p_false;
		if(!(p_false = p_Parse_Lb(r_lexer))) { // note it could call the same level to solve right associativity by recursion (but i don't want that)
			delete p_head;
			delete p_true;
			return 0;
		}
		// false value

		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode())) {
			delete p_head;
			delete p_true;
			delete p_false;
			return 0;
		}
		// alloc the new node

		p_new->p_eval = &EvalTernary;
		if(!p_rightmost) {
			p_new->p_node[0] = p_head;
			p_new->p_node[1] = p_true;
			p_new->p_node[2] = p_false;
			// this is the first ternary operator encountered

			// p_head? p_true : p_false
			p_head = p_rightmost = p_new;
		} else {
			// p_rightmost is ternary operator itself
			// p_rightmost->p_node[0]? p_rightmost->p_node[1] : p_rightmost->p_node[2]? p_true : p_false

			p_new->p_node[0] = p_rightmost->p_node[2]; // use p_rightmost->p_node[2] as the condition in the new ternary op
			p_new->p_node[1] = p_true;
			p_new->p_node[2] = p_false;
			p_rightmost->p_node[2] = p_new; // use the new ternary op as right node in the right-most ternary op
			// wrap the new ternary operator in the rightmost one

			p_rightmost = p_new;
			// the new ternary operator becomes the rightmost one
		}
		// handle right associativity of the ternary operator
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lb(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Lc(r_lexer)))
		return 0;
	for(;;) {
		if(r_lexer.n_LookForOperator2("||") < 0)
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Lc(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = &EvalBoolOr;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lc(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Ld(r_lexer)))
		return 0;
	for(;;) {
		if(r_lexer.n_LookForOperator2("^^") < 0)
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Ld(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = &EvalBoolXor;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Ld(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Le(r_lexer)))
		return 0;
	for(;;) {
		if(r_lexer.n_LookForOperator2("&&") < 0)
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Le(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = &EvalBoolAnd;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Le(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Lf(r_lexer)))
		return 0;
	for(;;) {
		if(r_lexer.n_PeekOperator2("||") >= 0 || !r_lexer.n_LookForOperator("|"))
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Lf(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = &EvalBitOr;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lf(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Lg(r_lexer)))
		return 0;
	for(;;) {
		if(r_lexer.n_PeekOperator2("^^") >= 0 || !r_lexer.n_LookForOperator("^"))
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Lg(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = &EvalBitXor;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lg(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Lh(r_lexer)))
		return 0;
	for(;;) {
		if(r_lexer.n_PeekOperator2("&&") >= 0 || !r_lexer.n_LookForOperator("&"))
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Lh(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = &EvalBitAnd;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lh(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Li(r_lexer)))
		return 0;
	for(;;) {
		int n_op = r_lexer.n_LookForOperator2("==!=");
		if(n_op < 0)
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Li(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = (n_op)? &EvalIsNotEqual : &EvalIsEqual;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Li(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Lj(r_lexer)))
		return 0;
	for(;;) {
		int n_op = r_lexer.n_LookForOperator2("<=>=");
		if(n_op < 0) {
			if(r_lexer.n_PeekOperator2("<<>>") >= 0 || !(n_op = r_lexer.n_LookForOperator("<>")))
				return p_left;
		}
		TNode *p_right;
		if(!(p_right = p_Parse_Lj(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		if(n_op == '<')
			p_new->p_eval = &EvalIsLess;
		else if(n_op == '>')
			p_new->p_eval = &EvalIsGreater;
		else if(n_op == 0)
			p_new->p_eval = &EvalIsLessEqual;
		else if(n_op == 1)
			p_new->p_eval = &EvalIsGreaterEqual;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lj(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Lk(r_lexer)))
		return 0;
	for(;;) {
		int n_op = r_lexer.n_LookForOperator2("<<>>");
		if(n_op < 0)
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Lk(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		p_new->p_eval = (n_op)? &EvalShR : &EvalShL;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lk(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Ll(r_lexer)))
		return 0;
	for(;;) {
		int n_op;
		if(r_lexer.n_PeekOperator2("++--") >= 0 || !(n_op = r_lexer.n_LookForOperator("+-")))
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Ll(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		if(n_op == '+')
			p_new->p_eval = &EvalAdd;
		else if(n_op == '-')
			p_new->p_eval = &EvalSub;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Ll(CExpEvalLexer &r_lexer)
{
	TNode *p_left;
	if(!(p_left = p_Parse_Lm(r_lexer)))
		return 0;
	for(;;) {
		int n_op = r_lexer.n_LookForOperator("*/%");
		if(!n_op)
			return p_left;
		TNode *p_right;
		if(!(p_right = p_Parse_Lm(r_lexer))) {
			delete p_left;
			return 0;
		}
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode(p_left, p_right))) {
			delete p_left;
			delete p_right;
			return 0;
		}
		if(n_op == '*')
			p_new->p_eval = &EvalMul;
		else if(n_op == '/')
			p_new->p_eval = &EvalDiv;
		else if(n_op == '%')
			p_new->p_eval = &EvalMod;
		p_left = p_new;
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
typename CExpression<_Ty, _TyInt, b_integer_exception_handling>::TNode *CExpression<_Ty, _TyInt, b_integer_exception_handling>::p_Parse_Lm(CExpEvalLexer &r_lexer)
{
	int n_op;
	_Ty t_value;
	if(r_lexer.LookForNumberLiteral(t_value)) {
		TNode *p_new;
		if(!(p_new = new(std::nothrow) TNode))
			return 0;
		p_new->t_value = t_value;
		p_new->p_eval = &EvalConst;
		return p_new;
	} else if((n_op = r_lexer.n_LookForOperator("(+-~!")) == '(') { // parentheses
		TNode *p_inner = p_Parse_La(r_lexer);
		if(r_lexer.n_LookForOperator(")") != ')') {
			if(p_inner)
				delete p_inner;
			return 0;
		}
		return p_inner;
	} else if(n_op == '+' || n_op == '-' || n_op == '~' || n_op == '!') { // unary +, -, ~ and !
		TNode *p_inner = p_Parse_Lm(r_lexer);
		if(n_op == '+')
			return p_inner; // unary + (no op)
		else {
			TNode *p_new;
			if(!(p_new = new(std::nothrow) TNode(p_inner, 0))) {
				delete p_inner;
				return 0;
			}
			if(n_op == '-')
				p_new->p_eval = &EvalNeg; // unary -
			else if(n_op == '~')
				p_new->p_eval = &EvalBitNeg; // unary ~
			else /*if(n_op == '!')*/ {
				_ASSERTE(n_op == '!');
				p_new->p_eval = &EvalBoolNeg; // unary !
			}
			return p_new;
		}
	} else /*if(!n_op)*/ { // identifiers, functions
		_ASSERTE(!n_op);

		size_t n_length;
		const char *p_ident;
		if(!r_lexer.LookForIdentifier(p_ident, n_length))
			return 0;
		// get an identifier

		static const TFuncEntry p_default_func_list[] = {
			{2, "pi", &EvalPi, 0}, // constant functions
			{3, "exp", &EvalExp, 1}, {2, "ln", &EvalLn, 1}, {3, "log", &EvalLog, 1}, // exp/log functions
			{3, "sqr", &EvalSqr, 1}, {3, "pow", &EvalPow, 2}, {4, "sqrt", &EvalSqrt, 1}, // power functions
			{3, "abs", &EvalAbs, 1}, {4, "sign", &EvalSign, 1}, // sign functions
			{3, "sin", &EvalSin, 1}, {3, "cos", &EvalCos, 1}, {3, "tan", &EvalTan, 1},
			{4, "asin", &EvalASin, 1}, {4, "acos", &EvalACos, 1}, {4, "atan", &EvalATan, 1},
			{5, "atan2", &EvalATan2, 2}, // goniometric functions
			{5, "floor", &EvalFloor, 1}, {4, "ceil", &EvalCeil, 1}, {5, "fract", &EvalFract, 1},
			{5, "round", &EvalRound, 1}, {3, "mod", &EvalMod, 2}, // rounding functions
			{3, "min", &EvalMin, 2}, {3, "max", &EvalMax, 2}, {5, "clamp", &EvalClamp, 3},
			{4, "step", &EvalStep, 2}, {10, "smoothstep", &EvalSmoothStep, 3}, {3, "mix", &EvalMix, 3} // min/max
		};
		size_t n_default_func_num = sizeof(p_default_func_list) / sizeof(p_default_func_list[0]);
		// function table

		for(int n_table = 0; n_table < 2; ++ n_table) { // choose table - user specified, then default
			size_t n_table_size = (n_table)? n_default_func_num : m_function_table.size();
			if(!n_table_size)
				continue; // avoid dereferencing an empty vector on the next line
			const TFuncEntry *p_table = (n_table)? p_default_func_list : &m_function_table.front();
			// get table

			for(size_t i = 0; i < n_table_size; ++ i) {
				_ASSERTE(strlen(p_table[i].p_s_name) == p_table[i].n_length);
				if(n_length == p_table[i].n_length &&
				   !memcmp(p_ident, p_table[i].p_s_name, n_length * sizeof(char))) {
					if(r_lexer.n_LookForOperator("(") != '(')
						return 0;
					// skip '('

					_ASSERTE(p_table[i].n_arg_num <= max_FunctionParam_Num);
					TNode *p_arg[max_FunctionParam_Num];
					for(size_t j = 0, m = p_table[i].n_arg_num; j < m; ++ j) {
						if((j && r_lexer.n_LookForOperator(",") != ',') || // can't skip '(' in here because of functions with no operators
						   !(p_arg[j] = p_Parse_La(r_lexer))) {
							while(j > 0)
								delete p_arg[-- j];
							return 0;
						}
						// skip ',' and parse the next
					}
					// read arguments

					if(!type_IsSignedDataType && p_table[i].p_func == &EvalAbs) {
						_ASSERTE(p_table[i].n_arg_num == 1);
						if(r_lexer.n_LookForOperator(")") != ')') {
							for(size_t j = 0, m = p_table[i].n_arg_num; j < m; ++ j)
								delete p_arg[j];
							return 0;
						}
						// skip ')'

						return p_arg[0];
						// just the argument
					}
					// optimize away abs() on unsigned types (kind of dirty hack, this should be moved to Optimize())

					TNode *p_new;
					if(r_lexer.n_LookForOperator(")") != ')' ||
					   !(p_new = new(std::nothrow) TNode())) {
						for(size_t j = 0, m = p_table[i].n_arg_num; j < m; ++ j)
							delete p_arg[j];
						return 0;
					}
					// skip ')', alloc a new node

					p_new->p_eval = p_table[i].p_func;
					memcpy(p_new->p_node, p_arg, p_table[i].n_arg_num * sizeof(TNode*)); // !!
					// set the eval function

					return p_new;
				}
			}
		}
		// parse (math) function invokation
		
		{
			_Ty *p_data;
			try {
				std::string s_variable_name;
				s_variable_name.insert(s_variable_name.begin(), p_ident, p_ident + n_length);
				p_data = &m_symbol_table[s_variable_name];
			} catch(std::bad_alloc&) {
				return 0; // not enough memory
			}
			// get address of / create entry in the symbol table (case sensitive)

			TNode *p_new;
			if(!(p_new = new(std::nothrow) TNode))
				return 0;
			p_new->p_value = p_data;
			p_new->p_eval = &EvalVar;
			// create a node

			return p_new;
		}
		// it's a variable
	}
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalDiv(const TNode &r_node) // throw(std::runtime_error)
{
	_ASSERTE(!type_IsIntegerDataType || sizeof(_TyInt) == sizeof(_Ty));
	if(integer_ExceltionHandling) {
		if(type_IsIntegerDataType) {
			_Ty r = r_node.t_EvalChild(1);
			if(!r)
				throw std::runtime_error("integer division by zero");
			if(type_IsSignedDataType && r == _Ty(-1)) { // is signed and r == -1?
				_Ty l = r_node.t_EvalChild(0);
				if(l == (_TyInt(1) << (sizeof(_TyInt) * 8 - 1))) // is it minimum value? // sizeof(_TyInt) instead of sizeof(_Ty) to avoid "warning C4293: '<<' : shift count negative or too big, undefined behavior"
					throw std::runtime_error("integer overflow");
				return l / r; // reuse calculated
			}
			return r_node.t_EvalChild(0) / r; // reuse calculated
		}
	}
	return r_node.t_EvalChild(0) / r_node.t_EvalChild(1);
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalMod(const TNode &r_node) // throw(std::runtime_error)
{
	_ASSERTE(!type_IsIntegerDataType || sizeof(_TyInt) == sizeof(_Ty));
	if(type_IsIntegerDataType) {
		if(integer_ExceltionHandling) {
			_Ty r = r_node.t_EvalChild(1);
			if(!r)
				throw std::runtime_error("integer division by zero");
			if(type_IsSignedDataType && r == _Ty(-1)) { // is signed and r == -1?
				_Ty l = r_node.t_EvalChild(0);
				if(l == (_TyInt(1) << (sizeof(_TyInt) * 8 - 1))) // is it minimum value? // sizeof(_TyInt) instead of sizeof(_Ty) to avoid "warning C4293: '<<' : shift count negative or too big, undefined behavior"
					throw std::runtime_error("integer overflow");
				return _TyInt(l) % _TyInt(r); // reuse calculated
			}
			return _TyInt(r_node.t_EvalChild(0)) % _TyInt(r); // reuse calculated
		} else
			return _TyInt(r_node.t_EvalChild(0)) % _TyInt(r_node.t_EvalChild(1)); // unsafe
	}
	return _Ty(fmod(double(r_node.t_EvalChild(0)), double(r_node.t_EvalChild(1))));
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalVar(const TNode &r_node)
{
	_ASSERTE(r_node.p_value);
	return *r_node.p_value;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalSqr(const TNode &r_node)
{
	_Ty t_tmp = r_node.t_EvalChild(0);
	return t_tmp * t_tmp;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalAbs(const TNode &r_node)
{
	_ASSERTE(type_IsSignedDataType); // no need to call abs on unsigned, should be automatically optimized away
	if(type_IsIntegerDataType) { // is integer?
		_Ty t_tmp = r_node.t_EvalChild(0);
		return (t_tmp >= 0)? t_tmp : -t_tmp; // works well for integers (there is an overflow, abs(INT_MIN) = INT_MIN, but we ignore it)
	} else
		return _Ty(fabs(double(r_node.t_EvalChild(0)))); // the above would not work for negative zero
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalFract(const TNode &r_node)
{
	_Ty x = r_node.t_EvalChild(0);
	return _Ty(x - floor(double(x)));
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalMin(const TNode &r_node)
{
	_Ty a = r_node.t_EvalChild(0), b = r_node.t_EvalChild(1);
	return (a < b)? a : b;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalMax(const TNode &r_node)
{
	_Ty a = r_node.t_EvalChild(0), b = r_node.t_EvalChild(1);
	return (a < b)? b : a;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalMix(const TNode &r_node)
{
	_Ty a = r_node.t_EvalChild(0), b = r_node.t_EvalChild(1);
	_Ty x = r_node.t_EvalChild(2);
	return a + (b - a) * x;
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalSmoothStep(const TNode &r_node)
{
	_Ty a = r_node.t_EvalChild(0), b = r_node.t_EvalChild(1);
	_Ty x = r_node.t_EvalChild(2);
	return (x >= a)? ((x <= b)? (x - a) / (b - a) : 1) : 1; // note this is simple lerp // @todo - test it
}

template <class _Ty, class _TyInt, const bool b_integer_exception_handling>
_Ty CExpression<_Ty, _TyInt, b_integer_exception_handling>::EvalClamp(const TNode &r_node)
{
	_Ty x = r_node.t_EvalChild(0);
	_Ty a = r_node.t_EvalChild(1), b = r_node.t_EvalChild(2);
	return (x >= a)? ((x <= b)? x : b) : a;
}

/*
 *								=== ~CExpression ===
 */

#ifdef __EXPRESSION_EVALUATOR_COMPILE_TESTS

#define __EE_TEST(eval,exp) Test((eval), #exp, (exp), b_verbose)

/**
 *	@brief expression evaluation tester
 *	@param[in] _EEvalClass is instance of CExpression
 */
template <class _EEvalClass = CIntExpression>
class CExpEvalTester {
protected:
	typedef typename _EEvalClass::TDataType _Ty; /**< @brief expression evaluator data type */
	size_t n_failure_num; /**< @brief number of errors encountered in tests */

public:
	/**
	 *	@brief default constructor; runs the tests
	 *	@param[in] b_verbose is verbosity flag (true enables verbose output to stdout)
	 */
	CExpEvalTester(bool b_verbose = true)
		:n_failure_num(0)
	{
		_EEvalClass eeval; // use a single object for all tests

		if(!__EE_TEST(eeval, 1 + 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 + 1 + 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 + 1 * 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 2 * 1 + 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 + 1) * 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 * 2 / 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 / 4 * 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (3 * 2) / 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 * (2 / 4))) ++ n_failure_num;
		// simple expressions

		if(!__EE_TEST(eeval, 1 << 8)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 256 >> 8)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 << 8 | 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 << 8 - 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 << 8) - 1)) ++ n_failure_num;
		// bit shifts

		if(!__EE_TEST(eeval, (0)? 10 : 20)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? 10 : 20)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 + (0)? 10 : 20)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? ((1)? 10 : 20) : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? ((0)? 10 : 20) : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? ((1)? 10 : 20) : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? ((0)? 10 : 20) : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (1)? 10 : 20 : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (0)? 10 : 20 : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (1)? 10 : 20 : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (0)? 10 : 20 : 30)) ++ n_failure_num;
		// simple ternary operator

		if(!__EE_TEST(eeval, (1)? 1 : (0)? 2 : 3)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? 1 : (1)? 2 : 3)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? 1 : (0)? 2 : 3)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? 1 : (0)? 2 : (0)? 3 : (0)? 4 : (0)? 5 : 6)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? 1 : (1)? 2 : (0)? 3 : (0)? 4 : (0)? 5 : 6)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? 1 : (0)? 2 : (1)? 3 : (0)? 4 : (0)? 5 : 6)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? 1 : (0)? 2 : (0)? 3 : (1)? 4 : (0)? 5 : 6)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? 1 : (0)? 2 : (0)? 3 : (0)? 4 : (1)? 5 : 6)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? 1 : (0)? 2 : (0)? 3 : (0)? 4 : (0)? 5 : 6)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (0)? 10 : 20 : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (1)? 10 : 20 : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (0)? 10 : 20 : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (1)? 10 : 20 : 30)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (0)? (0)? (0)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (0)? (0)? (0)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (1)? (0)? (0)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (0)? (1)? (0)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (0)? (0)? (1)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (0)? (0)? (0)? (0)? (1)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (0)? (0)? (0)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (1)? (0)? (0)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (1)? (1)? (0)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (1)? (1)? (1)? (0)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1)? (1)? (1)? (1)? (1)? 10 : 20 : 30 : 40 : 50 : 60)) ++ n_failure_num;
		// ternary operator associativity

		if(!__EE_TEST(eeval, 0 == 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 != 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 != 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 > 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 > 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 >= 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 < 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 < 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 <= 1)) ++ n_failure_num;
		// simple boolean expressions

		if(!__EE_TEST(eeval, 0 == 1 || 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 == 1 || 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 || 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 || 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && 0 == 1 || 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && 0 == 1 || 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && 1 == 1 || 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && 1 == 1 || 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && 0 == 1 || 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && 0 == 1 || 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && 1 == 1 || 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && 1 == 1 || 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 == 1 || 2 != 2 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 == 1 || 0 != 2 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 || 2 != 2 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 || 0 != 2 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 == 1 || 2 != 2 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 == 1 || 0 != 2 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 || 2 != 2 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 == 1 || 0 != 2 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 2 != 2 || 0 == 1 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 != 2 || 0 == 1 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 2 != 2 || 1 == 1 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 != 2 || 1 == 1 && 3 >= 5)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 2 != 2 || 0 == 1 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 != 2 || 0 == 1 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 2 != 2 || 1 == 1 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 0 != 2 || 1 == 1 && 3 >= 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && (0 == 1 || 2 != 2))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && (0 == 1 || 0 != 2))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && (1 == 1 || 2 != 2))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 5 && (1 == 1 || 0 != 2))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && (0 == 1 || 2 != 2))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && (0 == 1 || 0 != 2))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && (1 == 1 || 2 != 2))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 && (1 == 1 || 0 != 2))) ++ n_failure_num;
		// advanced boolean expressions

		if(!__EE_TEST(eeval, 3 >= 1 | 1 || 0 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 | 1 || 0 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 | 1 || 1 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 | 1 || 1 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 | 1 || 0 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 | 1 || 0 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 | 1 || 1 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 | 1 || 1 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 | 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 | 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 | 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 | 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 | 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 | 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 | 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 | 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 | 0 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 | 0 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 | 1 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 | 1 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 | 0 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 | 0 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 | 1 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 | 1 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 && 1 | 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 && 1 | 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 && 1 | 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 && 1 | 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 && 1 | 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 && 1 | 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 && 1 | 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 && 1 | 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 & 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 & 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 & 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 & 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 & 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 & 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 & 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 & 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 & 0 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 & 0 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 & 1 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 & 1 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 & 0 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 & 0 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 & 1 == 1 && 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 & 1 == 1 && 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 && 1 & 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 0 == 1 && 1 & 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 && 1 & 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 1 || 1 == 1 && 1 & 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 && 1 & 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 0 == 1 && 1 & 0 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 && 1 & 2 != 2)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 3 >= 2 || 1 == 1 && 1 & 0 != 2)) ++ n_failure_num;
		// mixtures of boolean and bitwise expressions

		if(!__EE_TEST(eeval, 1 & 2 | 7)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | 2 & 7)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & 2) | 7)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & (2 | 7))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | 2) & 7)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | (2 & 7))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | 2 & 7 ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | 2) & 7 ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | (2 & 7) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | 2 & (7 ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | 2) & (7 ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | 2 & 7) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ((1 | 2) & 7) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | (2 & 7)) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | (2 & 7 ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | ((2 & 7) ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | (2 & (7 ^ 4)))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & 2 | 7 ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & 2) | 7 ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & (2 | 7) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & 2 | (7 ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & 2) | (7 ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & 2 | 7) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ((1 & 2) | 7) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & (2 | 7)) ^ 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & (2 | 7 ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & ((2 | 7) ^ 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & (2 | (7 ^ 4)))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & 2 ^ 7 | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & 2) ^ 7 | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & (2 ^ 7) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & 2 ^ (7 | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & 2) ^ (7 | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & 2 ^ 7) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ((1 & 2) ^ 7) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 & (2 ^ 7)) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & (2 ^ 7 | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & ((2 ^ 7) | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 & (2 ^ (7 | 4)))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ 2 & 7 | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ 2) & 7 | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ (2 & 7) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ 2 & (7 | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ 2) & (7 | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ 2 & 7) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ((1 ^ 2) & 7) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ (2 & 7)) | 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ (2 & 7 | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ ((2 & 7) | 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ (2 & (7 | 4)))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | 2 ^ 7 & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | 2) ^ 7 & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | (2 ^ 7) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | 2 ^ (7 & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | 2) ^ (7 & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | 2 ^ 7) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ((1 | 2) ^ 7) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 | (2 ^ 7)) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | (2 ^ 7 & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | ((2 ^ 7) & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 | (2 ^ (7 & 4)))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ 2 | 7 & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ 2) | 7 & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ (2 | 7) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ 2 | (7 & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ 2) | (7 & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ 2 | 7) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ((1 ^ 2) | 7) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, (1 ^ (2 | 7)) & 4)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ (2 | 7 & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ ((2 | 7) & 4))) ++ n_failure_num;
		if(!__EE_TEST(eeval, 1 ^ (2 | (7 & 4)))) ++ n_failure_num;
		// bitwise expression precedence

		if(!__EE_TEST(eeval, 255 & 15)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 256 & 15)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 40 & 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 41 & 1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, !0)) ++ n_failure_num;
		if(!__EE_TEST(eeval, !1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ~0)) ++ n_failure_num;
		if(!__EE_TEST(eeval, ~1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 255 & !0)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 255 & !1)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 255 & ~0)) ++ n_failure_num;
		if(!__EE_TEST(eeval, 255 & ~1)) ++ n_failure_num;
		// bitwise expressions and boolean negation

		if(!n_failure_num) {
			if(b_verbose)
				printf("all tests passed, there were no errors\n");
		} else
			fprintf(stderr, "error: there were %d errors\n", n_failure_num);
		// verbose
	}

	/**
	 *	@brief gets number of errors
	 *	@return Returns number of errors (0 means the expression evaluation is working correctly).
	 */
	operator size_t() const
	{
		return n_failure_num;
	}

protected:
	bool Test(_EEvalClass &eval, const char *p_s_expression,
		_Ty n_expected_result, bool b_verbose)
	{
		_EEvalClass newEval; // create new object as well
		if(!eval.Parse(p_s_expression) || !newEval.Parse(p_s_expression)) {
			fprintf(stderr, "error: parse error in \'%s\'\n", p_s_expression);
			return false;
		}
		_Ty n_result = newEval.t_Evaluate();
		_Ty n_result2 = eval.t_Evaluate();
		if(n_result != n_expected_result || n_result2 != n_expected_result) {
			fprintf(stderr, "error: evaluation error in \'%s\' (%g, expected to get %g)\n",
				p_s_expression, double(n_result), double(n_expected_result));
			return false;
		} else {
			if(b_verbose)
				printf("test: \'%s\' = %g : ok\n", p_s_expression, double(n_result));
			return true;
		}
	}
};

#undef __EE_TEST

#endif // __EXPRESSION_EVALUATOR_COMPILE_TESTS

#endif // !__EXPRESSION_EVALUATOR_INLINES_INCLUDED
