private Expression simplify(AryExpression ary) {
   Expression expr1 = ary.getExpression1();
   expr1 = simplify(expr1);
   Expression expr2 = ary.getExpression2();
   expr2 = simplify(expr2);
   if (ary instanceof ExprADD) {
     if (isScalarConstants(expr1, expr2)) {
       if ((expr2.getResultType() == Expression.TYPE_INTEGER)
           && (expr1.getResultType() == Expression.TYPE_INTEGER)) {
         Constant constant = new Constant(Expression.TYPE_INTEGER, Expression.STRUCT_SCALAR);
         constant.setValue(expr1.evalAsInt() + expr2.evalAsInt());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_FLOAT)
           || (expr1.getResultType() == Expression.TYPE_FLOAT)) {
         Constant constant = new Constant(Expression.TYPE_FLOAT, Expression.STRUCT_SCALAR);
         constant.setValue(expr1.evalAsFloat() + expr2.evalAsFloat());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_NUMERIC)
           || (expr1.getResultType() == Expression.TYPE_NUMERIC)) {
         Constant constant = new Constant(Expression.TYPE_NUMERIC, Expression.STRUCT_SCALAR);
         constant.setValue(expr1.evalAsFloat() + expr2.evalAsFloat());
         return constant;
       } else {
         return ary;
       }
     } else if ((expr1 instanceof Constant) || (expr2 instanceof Constant)) {
       if (expr1.equals(Constant.ZERO_F)) {
         return expr2;
       } else if (expr1.equals(Constant.ZERO_I)) {
         return expr2;
       } else if (expr1.equals(Constant.EMPTY_STRING)) {
         return expr2;
       } else if (expr2.equals(Constant.ZERO_F)) {
         return expr1;
       } else if (expr2.equals(Constant.ZERO_I)) {
         return expr1;
       } else if (expr2.equals(Constant.EMPTY_STRING)) {
         return expr1;
       }
     }
   } else if (ary instanceof ExprSUB) {
     if (isScalarConstants(expr1, expr2)) {
       if ((expr2.getResultType() == Expression.TYPE_INTEGER)
           && (expr1.getResultType() == Expression.TYPE_INTEGER)) {
         Constant constant = new Constant(Expression.TYPE_INTEGER, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsInt() - expr1.evalAsInt());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_FLOAT)
           || (expr1.getResultType() == Expression.TYPE_FLOAT)) {
         Constant constant = new Constant(Expression.TYPE_FLOAT, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsFloat() - expr1.evalAsFloat());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_NUMERIC)
           || (expr1.getResultType() == Expression.TYPE_NUMERIC)) {
         Constant constant = new Constant(Expression.TYPE_NUMERIC, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsFloat() - expr1.evalAsFloat());
         return constant;
       } else {
         return ary;
       }
     } else if (expr1 instanceof Constant) {
       if (expr1.equals(Constant.ZERO_F)) {
         return expr2;
       } else if (expr1.equals(Constant.ZERO_I)) {
         return expr2;
       }
     }
   } else if (ary instanceof ExprMULT) {
     if (isScalarConstants(expr1, expr2)) {
       if ((expr2.getResultType() == Expression.TYPE_INTEGER)
           && (expr1.getResultType() == Expression.TYPE_INTEGER)) {
         Constant constant = new Constant(Expression.TYPE_INTEGER, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsInt() * expr1.evalAsInt());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_FLOAT)
           || (expr1.getResultType() == Expression.TYPE_FLOAT)) {
         Constant constant = new Constant(Expression.TYPE_FLOAT, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsFloat() * expr1.evalAsFloat());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_NUMERIC)
           || (expr1.getResultType() == Expression.TYPE_NUMERIC)) {
         Constant constant = new Constant(Expression.TYPE_NUMERIC, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsFloat() * expr1.evalAsFloat());
         return constant;
       } else {
         return ary;
       }
     } else if ((expr1 instanceof Constant) || (expr2 instanceof Constant)) {
       if (expr1.equals(Constant.ZERO_F)) {
         return Constant.ZERO_F;
       } else if (expr1.equals(Constant.ZERO_I)) {
         return Constant.ZERO_I;
       } else if (expr2.equals(Constant.ZERO_F)) {
         return Constant.ZERO_F;
       } else if (expr2.equals(Constant.ZERO_I)) {
         return Constant.ZERO_I;
       } else if (expr1.equals(Constant.ONE_F)) {
         return expr2;
       } else if (expr1.equals(Constant.ONE_I)) {
         return expr2;
       } else if (expr2.equals(Constant.ONE_F)) {
         return expr1;
       } else if (expr2.equals(Constant.ONE_I)) {
         return expr1;
       }
     }
   } else if (ary instanceof ExprDIV) {
     if (isScalarConstants(expr1, expr2)) {
       if ((expr2.getResultType() == Expression.TYPE_INTEGER)
           && (expr1.getResultType() == Expression.TYPE_INTEGER)) {
         Constant constant = new Constant(Expression.TYPE_FLOAT, Expression.STRUCT_SCALAR);
         constant.setValue((float) expr2.evalAsInt() / (float) expr1.evalAsInt());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_FLOAT)
           || (expr1.getResultType() == Expression.TYPE_FLOAT)) {
         Constant constant = new Constant(Expression.TYPE_FLOAT, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsFloat() / expr1.evalAsFloat());
         return constant;
       } else if ((expr2.getResultType() == Expression.TYPE_NUMERIC)
           || (expr1.getResultType() == Expression.TYPE_NUMERIC)) {
         Constant constant = new Constant(Expression.TYPE_NUMERIC, Expression.STRUCT_SCALAR);
         constant.setValue(expr2.evalAsFloat() / expr1.evalAsFloat());
         return constant;
       } else {
         return ary;
       }
     } else if ((expr1 instanceof Constant) || (expr2 instanceof Constant)) {
       if (expr1.equals(Constant.ONE_F)) {
         return expr2;
       } else if (expr1.equals(Constant.ONE_I)) {
         return expr2;
       } else if (expr2.equals(Constant.ZERO_F)) {
         return Constant.ZERO_F;
       } else if (expr2.equals(Constant.ZERO_I)) {
         return Constant.ZERO_I;
       }
     }
   } else if (ary instanceof ExprAND) {
     if ((expr1 instanceof Constant) && (expr2 instanceof Constant)) {
       ((Constant) expr1).setValue(expr2.evalAsBoolean() && expr1.evalAsBoolean());
       return expr1;
     } else if ((expr1 instanceof Constant) || (expr2 instanceof Constant)) {
       if (expr1.equals(Constant.TRUE)) {
         return expr2;
       } else if (expr1.equals(Constant.FALSE)) {
         return Constant.FALSE;
       } else if (expr2.equals(Constant.TRUE)) {
         return expr1;
       } else if (expr2.equals(Constant.FALSE)) {
         return Constant.FALSE;
       }
     }
   } else if (ary instanceof ExprOR) {
     if (isScalarConstants(expr1, expr2)) {
       Constant constant = new Constant(Expression.TYPE_BOOL, Expression.STRUCT_SCALAR);
       constant.setValue(expr2.evalAsBoolean() || expr1.evalAsBoolean());
       return constant;
     } else if ((expr1 instanceof Constant) || (expr2 instanceof Constant)) {
       if (expr1.equals(Constant.TRUE)) {
         return Constant.TRUE;
       } else if (expr1.equals(Constant.FALSE)) {
         return Constant.FALSE;
       } else if (expr2.equals(Constant.TRUE)) {
         return Constant.TRUE;
       } else if (expr2.equals(Constant.FALSE)) {
         return Constant.FALSE;
       }
     }
   }
   ary.setExpression1(expr1);
   ary.setExpression2(expr2);
   return ary;
 }
 @Override
 public final Object eval() throws ArithmeticException {
   FunctionsDefinitions def = FunctionsDefinitions.getInstance();
   Function function = def.getFunction(key);
   if (function == null) {
     throw new ArithmeticException(
         "Function " + key.getName() + "( " + key.getParamsSize() + " parameters) does not exist");
   } else {
     Method method = function.getMethod();
     if (fields == null) {
       initFieldsArray(function, method);
     }
     Object obj = function.getObject();
     if (varargs) {
       Class clazz = method.getParameterTypes()[fields.length - 1];
       Class ctype = getVarargType(clazz);
       if (ctype == Integer.TYPE) {
         fillIntField(fields);
       } else if (ctype == Float.TYPE) {
         fillFloatField(fields);
       } else {
         fillObjectField(fields);
       }
     } else {
       for (int i = 0; i < exprs.size(); i++) {
         Expression expr = exprs.get(i);
         Object _f = null;
         if (expr.getResultType() == TYPE_INTEGER) {
           _f = getField(expr.evalAsInt(), method.getParameterTypes()[i]);
         } else if (expr.getResultType() == TYPE_FLOAT) {
           _f = getField(expr.evalAsFloat(), method.getParameterTypes()[i]);
         } else if (expr.getResultType() == TYPE_BOOL) {
           _f = getField(expr.evalAsBoolean(), method.getParameterTypes()[i]);
         } else {
           _f = getField(expr.eval(), method.getParameterTypes()[i]);
         }
         fields[i] = _f;
       }
     }
     try {
       Object result = null;
       if (varargs) {
         result = method.invoke(obj, fields);
       } else {
         result = method.invoke(obj, fields);
       }
       if (method.getReturnType() == Integer.TYPE) {
         return result;
       } else if (method.getReturnType() == Short.TYPE) {
         return ((Short) result).intValue();
       } else if (method.getReturnType() == Double.TYPE) {
         return ((Double) result).floatValue();
       } else if (method.getReturnType() == Float.TYPE) {
         Float f = (Float) result;
         if (f.isInfinite()) {
           throw newArithmeticException(function);
         }
         return f;
       } else if (method.getReturnType() == Boolean.TYPE) {
         return ((Boolean) result).booleanValue();
       } else {
         return result;
       }
     } catch (IllegalAccessException ex) {
       ArithmeticException e =
           new ArithmeticException("IllegalAccessException for function " + function.getName());
       throw e;
     } catch (IllegalArgumentException ex) {
       ArithmeticException e =
           new ArithmeticException("Illegal argument for function " + function.getName());
       throw e;
     } catch (InvocationTargetException ex) {
       Throwable _ex = ex.getCause();
       String message = null;
       if (_ex != null) {
         message = "Exception in function " + function.getName() + " (" + _ex.getMessage() + ")";
       } else {
         message = "Exception in function " + function.getName();
       }
       ArithmeticException e = new ArithmeticException(message);
       e.initCause(_ex);
       e.setStackTrace(_ex.getStackTrace());
       throw e;
     }
   }
 }