/*
* RPNexpressionCalculator.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.inference.model;
import java.util.Stack;
/**
* Simple RPN expression evaluator.
*
* Limitations:
* - variables are statistics of 1 dimension.
* - Four basic operations (easy to extend, though)
*
* @author Joseph Heled
* Date: 10/05/2008
*/
public class RPNexpressionCalculator {
/**
* Interfave for variable access by name
*/
public interface GetVariable {
/**
*
* @param name
* @return variable value
*/
double get(String name);
}
private enum OP { OP_ADD, OP_SUB, OP_MULT, OP_DIV, OP_LOG, OP_EXP, OP_CHS, OP_CONST, OP_REF }
private class Eelement {
OP op;
String name;
private double value;
Eelement(OP op) {
this.op = op;
name = null;
}
Eelement(String name) {
this.op = OP.OP_REF;
this.name = name;
}
Eelement(double val) {
this.op = OP.OP_CONST;
this.value = val;
}
}
Eelement[] expression;
public RPNexpressionCalculator(String expressionString) {
String[] tokens = expressionString.trim().split("\\s+");
expression = new Eelement[tokens.length];
for(int k = 0; k < tokens.length; ++k) {
String tok = tokens[k];
Eelement element;
if( tok.equals("+") ) {
element = new Eelement(OP.OP_ADD);
} else if( tok.equals("-") ) {
element = new Eelement(OP.OP_SUB);
} else if( tok.equals("*") ) {
element = new Eelement(OP.OP_MULT);
} else if( tok.equals("/") ) {
element = new Eelement(OP.OP_DIV);
} else if( tok.equals("log") ) {
element = new Eelement(OP.OP_LOG);
} else if( tok.equals("exp") ) {
element = new Eelement(OP.OP_EXP);
} else if( tok.equals("chs") ) {
element = new Eelement(OP.OP_CHS);
} else {
try {
double val = Double.parseDouble(tok);
element = new Eelement(val);
} catch(java.lang.NumberFormatException ex) {
element = new Eelement(tok);
}
}
expression[k] = element;
}
}
/**
*
* @param variables
* @return evaluate expression given context (i.e. variables)
*/
public double evaluate(GetVariable variables) {
Stack<Double> stack = new Stack<Double>();
for( Eelement elem : expression ) {
switch( elem.op ) {
case OP_ADD: {
final Double y = stack.pop();
final Double x = stack.pop();
stack.push(x+y);
break;
}
case OP_SUB: {
final Double y = stack.pop();
final Double x = stack.pop();
stack.push(x-y);
break;
}
case OP_MULT : {
final Double y = stack.pop();
final Double x = stack.pop();
stack.push(x*y);
break;
}
case OP_DIV : {
final Double y = stack.pop();
final Double x = stack.pop();
stack.push(x/y);
break;
}
case OP_CHS: {
final Double x = stack.pop();
stack.push(-x);
break;
}
case OP_LOG: {
final Double x = stack.pop();
if( x <= 0.0 ) {
return Double.NaN;
}
stack.push(Math.log(x));
break;
}
case OP_EXP: {
final Double x = stack.pop();
stack.push(Math.exp(x));
break;
}
case OP_CONST: {
stack.push(elem.value);
break;
}
case OP_REF: {
stack.push(variables.get(elem.name) );
break;
}
}
}
return stack.pop();
}
/**
* @return null if all ok, error message otherwise
**/
public String validate() {
int stackSize = 0;
for(Eelement elem : expression) {
switch( elem.op ) {
case OP_ADD:
case OP_SUB:
case OP_MULT:
case OP_DIV: {
if( stackSize < 2 ) {
return "Binary operator underflow";
}
stackSize -= 1;
break;
}
case OP_CHS:
case OP_LOG:
case OP_EXP: {
if( stackSize == 0 ) {
return "Unary operator underflow";
}
break;
}
case OP_CONST: {
stackSize += 1;
break;
}
case OP_REF: {
stackSize += 1;
break;
}
}
}
if( stackSize != 1 ) {
return "Stack size " + stackSize + " ( != 1 ) at end of expression evaluation";
}
return null;
}
}