public class Calculator
{
public Calculator(){
}
/*
public Calculator(String data)
{
bits = data.toCharArray();
}
*/
/**
* 计算
* @return double类型的计算结果
* @throws ArithmeticException 计算出现错误
* @throws NumberFormatException 数据格式错误
*/
public double evaluate(String data)
throws ArithmeticException, NumberFormatException
{
if(data.toLowerCase().indexOf("if")>=0)
data = evalIfFunction(data);
bits = data.toCharArray();
pos = 0;
double ret = evalPlusMinus();
if (pos != bits.length) {
throw new NumberFormatException("Garbage at end of equation");
}
return ret;
}
public double evaluate(double x, String data)
throws ArithmeticException, NumberFormatException
{
this.x = x;
return evaluate(data);
}
private void skipWS()
{
while ((pos < bits.length) && Character.isWhitespace(bits[pos])) {
pos++;
}
}
private double evalReal() throws NumberFormatException
{
skipWS();
int save = pos;
while ((pos < bits.length) && Character.isDigit(bits[pos])) {
pos++;
}
if ((pos == save) && (bits[pos] != '.')) {
throw new NumberFormatException("pos != save");
}
if ((pos < bits.length) && bits[pos] == '.') {
int saved = ++pos;
while ((pos < bits.length) && Character.isDigit(bits[pos])) {
pos++;
}
if (pos == saved) {
throw new NumberFormatException("Invalid numeric literal");
}
}
return Double.valueOf(new String(bits, save, pos-save)).doubleValue();
}
private double evalSymbol()
throws ArithmeticException, NumberFormatException
{
double lhs = 0.0;
int skip = pos;
while ((skip < bits.length)&& Character.isLetterOrDigit(bits[skip])) {
skip++;
}
if (skip > pos) {
String symbol = new String(bits, pos, skip-pos);
if (symbol.equals("pi")) {
lhs = Math.PI;
} else if (symbol.equals("e")) {
lhs = Math.E;
} else if (symbol.equals("x")) {
lhs = x;
} else {
throw new NumberFormatException("Unknown symbol: " + symbol);
}
pos = skip;
}
return lhs;
}
private double evalFunction()
throws ArithmeticException, NumberFormatException,
SymbolNotFoundException
{
double lhs = 0.0;
int skip = pos;
while ((skip < bits.length)&& Character.isLetterOrDigit(bits[skip])) {
skip++;
}
if (skip > pos) {
String symbol = new String(bits, pos, skip-pos);
int saved = pos; pos = skip;
if (symbol.equals("sin")) {
lhs = Math.sin(evalTerm());
} else if (symbol.equals("cos")) {
lhs = Math.cos(evalTerm());
} else if (symbol.equals("tan")) {
lhs = Math.tan(evalTerm());
} else if (symbol.equals("asin")) {
lhs = Math.asin(evalTerm());
} else if (symbol.equals("acos")) {
lhs = Math.acos(evalTerm());
} else if (symbol.equals("atan")) {
lhs = Math.atan(evalTerm());
} else if (symbol.equals("ln")) {
lhs = Math.log(evalTerm());
} else if (symbol.equals("deg")) {
lhs = Math.toDegrees(evalTerm());
} else if (symbol.equals("rad")) {
lhs = Math.toRadians(evalTerm());
} else{
pos = saved;
throw new SymbolNotFoundException();
}
if (Double.isNaN(lhs)) {
throw new ArithmeticException(symbol + ": Invalid Domain");
}
}
return lhs;
}
private double evalTerm()
throws ArithmeticException, NumberFormatException
{
double lhs = 0.0;
skipWS();
switch (bits[pos]) {
case '(':
pos++;
lhs = evalPlusMinus();
skipWS();
if ((pos < bits.length) && (bits[pos] == ')')) {
pos++;
} else {
throw new NumberFormatException("Missing ')' in expression");
}
break;
case '[':
pos++;
lhs = evalPlusMinus();
skipWS();
if ((pos < bits.length) && (bits[pos] == ']')) {
pos++;
} else {
throw new NumberFormatException("Missing ']' in expression");
}
break;
default:
if (Character.isDigit(bits[pos]) || (bits[pos] == '.')) {
lhs = evalReal();
} else if (Character.isLetter(bits[pos])) {
try {
lhs = evalFunction();
} catch (SymbolNotFoundException sym) {
lhs = evalSymbol();
}
} else {
throw new NumberFormatException("Expecting Term");
}
break;
}
return lhs;
}
private double evalUnary()
throws ArithmeticException, NumberFormatException
{
double lhs = 0.0;
skipWS();
if (pos >= bits.length) {
throw new NumberFormatException("Premature expression end");
}
switch (bits[pos]) {
case '-':
pos++;
lhs = -evalUnary();
break;
case '+':
pos++;
lhs = evalUnary();
break;
default:
lhs = evalTerm();
break;
}
return lhs;
}
private double evalExponent()
throws ArithmeticException, NumberFormatException
{
double lhs = evalUnary();
boolean need_more = true;
skipWS();
while ((pos < bits.length) && need_more) {
switch (bits[pos]) {
case '^':
pos++;
lhs = Math.pow(lhs, evalUnary());
break;
case '!':
int lhs_int = (int)lhs;
pos++;
if (lhs < 0 || lhs_int != lhs) {
throw new ArithmeticException("Invalid domain");
}
lhs = 1.0;
for(int i = 2; i <= lhs_int; i++) {
lhs *= i;
}
break;
case '%':
pos++;
lhs /= 100;
break;
default:
need_more = false;
break;
}
}
return lhs;
}
private double evalMultDivide()
throws ArithmeticException, NumberFormatException
{
double lhs = evalExponent();
boolean need_more = true;
skipWS();
while ((pos < bits.length) && need_more) {
switch (bits[pos]) {
case '*':
pos++;
lhs *= evalExponent();
break;
case '/':
pos++;
lhs /= evalExponent();
break;
default:
need_more = false;
break;
}
}
return lhs;
}
private double evalPlusMinus()
throws ArithmeticException, NumberFormatException
{
double lhs = evalMultDivide();
boolean need_more = true;
skipWS();
while ((pos < bits.length) && need_more) {
switch (bits[pos]) {
case '-':
pos++;
lhs -= evalMultDivide();
break;
case '+':
pos++;
lhs += evalMultDivide();
break;
default:
need_more = false;
break;
}
}
return lhs;
}
private char bits[];
private int pos = 0;
private double x = 0.0;
private int ifPos = 0;
private String evalIfFunction(String express) throws ArithmeticException, NumberFormatException{
int lastIfPos = express.lastIndexOf("if");
if(lastIfPos<0) return express;
ifPos = lastIfPos;
String beforeIf = express.substring(0,lastIfPos);
String afterIf = express.substring(ifPos);
bits = afterIf.toCharArray();
double ifValue = evalLastIf();
String nonIfString = beforeIf + String.valueOf(ifValue) + afterIf.substring(ifPos);
express = evalIfFunction(nonIfString);
return express;
}
private double evalLastIf() throws ArithmeticException, NumberFormatException{
double x = 0.0;
skipWS();
int skip = 0;
String compareExpress=null;
String trueTerm=null;
String falseTerm=null;
while((skip<bits.length) && bits[skip]!=','){
skip++;
}
if(skip>0){
compareExpress = new String(bits, 3, skip-3);
}
//get the true expression
skip++;
ifPos = skip;
while( (skip<bits.length)&& bits[skip]!=','){
skip++;
}
if(skip>pos){
trueTerm = new String(bits, ifPos, skip-ifPos);
ifPos = skip;
}
//get the false expression
skip++;
ifPos = skip;
int leftBracket = 0;
int rightBracket = 0;
while((skip<bits.length) && bits[skip]!=')'){
if(bits[skip]=='('){
leftBracket ++;
}
skip++;
}
while(rightBracket<leftBracket+1){
if(bits[skip]==')'){
rightBracket++;
}
skip++;
}
if(skip>ifPos){
falseTerm = new String(bits, ifPos, skip-ifPos-1);
ifPos = skip;
}
boolean compare = evalCompare(compareExpress);
if(compare){
x = this.evaluate(trueTerm);
}else{
x = this.evaluate(falseTerm);
}
return x;
}
private boolean evalCompare(String express) throws ArithmeticException, NumberFormatException {
boolean isTrue = false;
char[] ifBits = express.toCharArray();
String partA = null;
String partB = null;
String compareType = null;
int i = 0;
int skip = 0;
while(skip<ifBits.length && !isCompareFlag(ifBits[skip])){
skip++;
}
partA = new String(ifBits, i, skip);
i = skip;
if(ifBits[skip+1]!='='){
compareType = new String(ifBits,skip,1);
partB = new String(ifBits,++skip,ifBits.length - skip);
}else{
compareType = new String(ifBits, skip, 2);
partB = new String(ifBits, skip+2, ifBits.length - skip-2);
}
double a = this.evaluate(partA);
double b = this.evaluate(partB);
if(compareType.length()==1){
char[] compareChar = compareType.toCharArray();
switch(compareChar[0]){
case '>':
return (a>b);
case '<':
return (a<b);
case '=':
return (a==b);
default:
return false;
}
}else{
if(compareType.equals(">=")){
return (a>b || a==b);
}else if(compareType.equals("<=")){
return (a<b || a==b);
}
}
return isTrue;
}
private boolean isCompareFlag(char c){
boolean isCompare = false;
switch(c){
case '>':
return true;
case '<':
return true;
case '=':
return true;
}
return false;
}
}