private double doFunction(int beg, int end) {
    int argbeg;

    for (argbeg = beg; argbeg <= end && expression.charAt(argbeg) != '('; argbeg++) {;
    }

    String fncnam = expression.substring(beg, argbeg).trim();
    ArgParser fncargs = new ArgParser(argbeg, end);
    FunctionHandler fnchdl = null;

    try {
      if ((fnchdl = pureFunctions.get(fncnam)) != null) {
        return fnchdl.evaluateFunction(fncnam, fncargs);
      } else if ((fnchdl = impureFunctions.get(fncnam)) != null) {
        isConstant = false; // impure functions cannot be guaranteed to be constant
        return fnchdl.evaluateFunction(fncnam, fncargs);
      }
      fncargs = null; // suppress check for too many fncargs
    } catch (ArithmeticException thr) {
      fncargs = null;
      throw thr;
    } catch (NoSuchMethodError thr) {
      fncargs = null;
      throw exception(beg, "Function not supported in this JVM: \"" + fncnam + "\"");
    } catch (UnsupportedOperationException thr) {
      fncargs = null;
      throw exception(beg, thr.getMessage());
    } catch (Throwable thr) {
      fncargs = null;
      throw exception(beg, "Unexpected exception parsing function arguments", thr);
    } finally {
      if (fncargs != null) {
        if (fncargs.hasNext()) {
          throw exception(fncargs.getIndex(), "Function has too many arguments");
        }
        offset = fncargs.getIndex();
      }
    }
    throw exception(beg, "Function \"" + fncnam + "\" not recognized");
  }
 // To add/remove functions change evaluateOperator() and registration
 public double evaluateFunction(String fncnam, ArgParser fncargs) throws ArithmeticException {
   switch (Character.toLowerCase(fncnam.charAt(0))) {
     case 'a':
       {
         if (fncnam.equalsIgnoreCase("abs")) {
           return Math.abs(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("acos")) {
           return Math.acos(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("asin")) {
           return Math.asin(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("atan")) {
           return Math.atan(fncargs.next());
         }
       }
       break;
     case 'c':
       {
         if (fncnam.equalsIgnoreCase("cbrt")) {
           return Math.cbrt(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("ceil")) {
           return Math.ceil(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("cos")) {
           return Math.cos(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("cosh")) {
           return Math.cosh(fncargs.next());
         }
       }
       break;
     case 'e':
       {
         if (fncnam.equalsIgnoreCase("exp")) {
           return Math.exp(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("expm1")) {
           return Math.expm1(fncargs.next());
         }
       }
       break;
     case 'f':
       {
         if (fncnam.equalsIgnoreCase("floor")) {
           return Math.floor(fncargs.next());
         }
       }
       break;
     case 'g':
       {
         //              if(fncnam.equalsIgnoreCase("getExponent"   )) { return
         // Math.getExponent(fncargs.next());                } needs Java 6
       }
       break;
     case 'l':
       {
         if (fncnam.equalsIgnoreCase("log")) {
           return Math.log(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("log10")) {
           return Math.log10(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("log1p")) {
           return Math.log1p(fncargs.next());
         }
       }
       break;
     case 'm':
       {
         if (fncnam.equalsIgnoreCase("max")) {
           return Math.max(fncargs.next(), fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("min")) {
           return Math.min(fncargs.next(), fncargs.next());
         }
       }
       break;
     case 'n':
       {
         //              if(fncnam.equalsIgnoreCase("nextUp"        )) { return Math.nextUp
         // (fncargs.next());                } needs Java 6
       }
       break;
     case 'r':
       {
         if (fncnam.equalsIgnoreCase("random")) {
           return Math.random();
         } // impure
         if (fncnam.equalsIgnoreCase("round")) {
           return Math.round(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("roundHE")) {
           return Math.rint(fncargs.next());
         } // round half-even
       }
       break;
     case 's':
       {
         if (fncnam.equalsIgnoreCase("signum")) {
           return Math.signum(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("sin")) {
           return Math.sin(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("sinh")) {
           return Math.sinh(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("sqrt")) {
           return Math.sqrt(fncargs.next());
         }
       }
       break;
     case 't':
       {
         if (fncnam.equalsIgnoreCase("tan")) {
           return Math.tan(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("tanh")) {
           return Math.tanh(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("toDegrees")) {
           return Math.toDegrees(fncargs.next());
         }
         if (fncnam.equalsIgnoreCase("toRadians")) {
           return Math.toRadians(fncargs.next());
         }
       }
       break;
     case 'u':
       {
         if (fncnam.equalsIgnoreCase("ulp")) {
           return Math.ulp(fncargs.next());
         }
       }
       break;
       // no default
   }
   throw new UnsupportedOperationException(
       "MathEval internal function setup is incorrect - internal function \""
           + fncnam
           + "\" not handled");
 }