/* * 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; } }