Subversion Repositories public iLand

Rev

Rev 1104 | Rev 1218 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/********************************************************************************************
**    iLand - an individual based forest landscape and disturbance model
**    http://iland.boku.ac.at
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
**
**    This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
**
**    This program is distributed in the hope that it will be useful,
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**    GNU General Public License for more details.
**
**    You should have received a copy of the GNU General Public License
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
********************************************************************************************/


#ifndef LOGICEXPRESSION_H
#define LOGICEXPRESSION_H
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QMutexLocker>
#include <QtCore/QVector>

class ExpressionWrapper;
class Expression
{
public:
        ~Expression();
        Expression();
        Expression(const QString &aExpression) { m_expr=0; m_execList=0; setExpression(aExpression); }
        Expression(const QString &expression, ExpressionWrapper *wrapper) { m_expr=0; m_execList=0; setExpression(expression); mModelObject = wrapper;  }
        // intialization
        void setExpression(const QString &aExpression); ///< set expression
        void setAndParse(const QString &expr); ///< set expression and parse instantly
        void setModelObject(ExpressionWrapper *wrapper) { mModelObject = wrapper; }
        const QString &expression() const { return m_expression; }
        void  parse(ExpressionWrapper *wrapper=0); ///< force a parsing of the expression

        /// call linearize() to 'linarize' an expression, i.e. approximate the function by linear interpolation.
        void linearize(const double low_value, const double high_value, const int steps=1000);
        /// lineraize2d works with two variables
        void linearize2d(const double low_x, const double high_x, const double low_y, const double high_y, const int stepsx=50, const int stepsy=50);

        /// global switch for linerization. If set to false, subsequent calls to linearize are ignored.
        static void setLinearizationEnabled(const bool enable) {mLinearizationAllowed = enable; }
        // calculations
        double execute(double *varlist=0, ExpressionWrapper *object=0) const; ///< calculate formula and return result. variable values need to be set using "setVar()"
        double executeLocked() { QMutexLocker m(&m_execMutex); return execute();  } ///< thread safe version
        /** calculate formula. the first two variables are assigned the values Val1 and Val2. This function is for convenience.
           the return is the result of the calculation.
           e.g.: x+3*y --> Val1->x, Val2->y
           forceExecution: do not apply linearization */

        double calculate(const double Val1=0., const double Val2=0., const bool forceExecution=false) const;
        /// calculate formula with object
        ///
        double calculate(ExpressionWrapper &object, const double variable_value1=0., const double variable_value2=0.) const;

        //variables
        /// set the value of the variable named "Var". Note: using addVar to obtain a pointer may be more efficient for multiple executions.
        void  setVar(const QString& Var, double Value);
        /// adds variable "VarName" and returns a double pointer to the variable. Use *ptr to set the value (before calling execute())
        double *addVar(const QString& VarName);
        /// retrieve again the value pointer of a variable.
        double *  getVarAdress(const QString& VarName);


        bool isConstExpression() const { return m_constExpression; } ///< returns true if current expression is a constant.
        bool isEmpty() const { return m_empty; } ///< returns true if expression is empty
        const QString &lastError() const { return m_errorMsg; }
        /** strict property: if true, variables must be named before execution.
          When strict=true, all variables in the expression must be added by setVar or addVar.
          if false, variable values are assigned depending on occurence. strict is false by default for calls to "calculate()".
        */

        bool isStrict() { return m_strict;}
        void setStrict(bool str) { m_strict=str; }
        void setCatchExceptions(bool docatch=true) { m_catchExceptions = docatch; }
        void   setExternalVarSpace(const QStringList& ExternSpaceNames, double* ExternSpace);
        void enableIncSum();
        // other maintenance
        static void addConstant(const QString const_name, const double const_value);
private:
        enum ETokType {etNumber, etOperator, etVariable, etFunction, etLogical, etCompare, etStop, etUnknown, etDelimeter};
        enum EValueClasses {evcBHD, evcHoehe, evcAlter};
        struct ExtExecListItem {
            ETokType Type;
            double  Value;
            int     Index;
        };
        enum EDatatype {edtInfo, edtNumber, edtString, edtObject, edtVoid, edtObjVar, edtReference, edtObjectReference};
        bool m_catchExceptions;
        QString m_errorMsg;

        bool m_parsed;
        mutable bool m_strict;
        bool m_empty; // empty expression
        bool m_constExpression;
        QString m_tokString;
        QString m_expression;
        Expression::ExtExecListItem *m_execList;
        int m_execListSize; // size of buffer
        int m_execIndex;
        double m_varSpace[10];
        QStringList m_varList;
        QStringList m_externVarNames;
        double *m_externVarSpace;
        Expression::ETokType m_state;
        Expression::ETokType m_lastState;
        char *m_pos;
        char *m_expr;
        QString m_token;
        QString m_prepStr;
        int   m_tokCount;
        Expression::ETokType  next_token();
        void  atom();
        void  parse_levelL0();
        void  parse_levelL1();
        void  parse_level0();
        void  parse_level1();
        void  parse_level2();
        void  parse_level3();
        void  parse_level4();
        int  getFuncIndex(const QString& functionName);
        int  getVarIndex(const QString& variableName);
        inline double getModelVar(const int varIdx, ExpressionWrapper *object=0) const ;

        // link to external model variable
        ExpressionWrapper *mModelObject;

        double getExternVar(const int Index) const;
        // inc-sum
        mutable double m_incSumVar;
        bool   m_incSumEnabled;
        double  udfPolygon(double Value, double* Stack, int ArgCount) const; ///< special function polygon()
        double  udfInList(double value, double* stack, int argCount) const; ///< special function in()
        double udfSigmoid(double Value, double sType, double p1, double p2) const; ///< special function sigmoid()
        double udfRandom(int type, double p1, double p2) const; ///< user defined function rnd() (normal distribution does not work now!)

        void checkBuffer(int Index);
        QMutex m_execMutex;
        // linearization
        inline double linearizedValue(const double x) const;
        inline double linearizedValue2d(const double x, const double y) const;
        int mLinearizeMode;
        QVector<double> mLinearized;
        double mLinearLow, mLinearHigh;
        double mLinearStep;
        double mLinearLowY, mLinearHighY;
        double mLinearStepY;
        int mLinearStepCountY;
        static bool mLinearizationAllowed;
};

#endif // LOGICEXPRESSION_H