Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.tsl2.nano.logictable.EquationSolver Maven / Gradle / Ivy
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Thomas Schneider, Thomas Schneider
* created on: Dec 8, 2012
*
* Copyright: (c) Thomas Schneider 2012, all rights reserved
*/
package de.tsl2.nano.logictable;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;
import de.tsl2.nano.execution.IPRunnable;
import de.tsl2.nano.specification.AbstractRunnable;
import de.tsl2.nano.specification.Pool;
import de.tsl2.nano.util.operation.NumericOperator;
/**
* Tries to solve a given string expression as action or function (specification rule, or query) in a cell of a logictable.
* to have a rule, the expression starts with '=', actions are surrounded with '<<' and '>>'
*
* @author Thomas Schneider
* @version $Revision$
*/
public class EquationSolver extends NumericOperator {
static final String B_OPEN = "\\(";
static final String B_CLOSE = "\\)";
static final String BETWEEN = ":";
static final String SEPARATOR = ",";
static final String CONCAT = "\\s*[" + SEPARATOR + BETWEEN + "]\\s*";
static final String OPERATION = "([+*/%-])";
static final String EQUATION = "=";
static final String ACTION = "<<";
static final String TERM = B_OPEN + "[^)(]*" + B_CLOSE;
static final String OPERAND = (ENV.get(Pool.class).getExpressionPattern().length() > 2
? ENV.get(Pool.class).getExpressionPattern() + "?" : "") + "([a-zA-Z0-9_]+)";
static final String SET = "(" + OPERAND + "(" + CONCAT + OPERAND + ")*)*";
static final String FUNC = OPERAND + B_OPEN + SET + B_CLOSE;
/** a map containing any values. values found by this solver must be of right type */
Map values;
NumberFormat formatter;
/**
* constructor
*/
public EquationSolver() {
this(null, null);
}
/**
* constructor
*
* @param values
*/
public EquationSolver(NumberFormat formatter, Map values) {
super();
if (formatter == null) {
this.formatter = NumberFormat.getInstance();
} else {
this.formatter = formatter;
}
if (values == null) {
this.values = new Hashtable();
} else {
this.values = values;
}
}
public Object eval(String expression) {
return eval(new StringBuilder(expression));
}
public Object eval(StringBuilder expression) {
//extract all functions
evalActions(expression);
evalFunctions(expression);
//extract all terms
String term;
StringBuilder t;
while (true) {
term = extract(expression, TERM);
if (isEmpty(term)) {
break;
}
t = new StringBuilder(term.substring(1, term.length() - 1));
StringUtil.replace(expression, term, String.valueOf(operate(t, values)));
}
return !isEmpty(expression) && expression.toString().matches(".*" + OPERATION + ".*")
? operate(expression, values) : expression.toString();
}
/**
* isEmpty
* @param term
* @return
*/
boolean isEmpty(String term) {
return term.isEmpty() || term.equals("()");
}
protected void evalActions(StringBuilder expression) {
eval(expression, "\\<\\<" + FUNC + "\\>\\>", true);
}
/**
* replaces all found functions/rule inside the expression with their calculation results.
*
* @param expression
*/
protected void evalFunctions(StringBuilder expression) {
eval(expression, FUNC, true);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void eval(StringBuilder expression, String regex, boolean replace) {
String expr = expression.toString();
Object result;
Map args; //TODO: use extra args?
StringBuilder func, tempFunc;
String sfunc, fname, sresult;
int i = 0;
do {
sfunc = StringUtil.findRegExp(expr, regex, i);
if (Util.isEmpty(sfunc)) {
break;
}
func = new StringBuilder(sfunc);
fname = StringUtil.extract(tempFunc=new StringBuilder(func), OPERAND, "");
args = getFunctionArgs(tempFunc, values);
IPRunnable runner = ENV.get(Pool.class).get(fname);
result = runner.run(args);
if (replace) {
sresult = StringUtil.toString(result, 255);
StringUtil.replace(expression, func.toString(), sresult, i);
}
i += func.length();
} while (true);
}
private Map getFunctionArgs(StringBuilder func, Map values) {
Map args = new LinkedHashMap();
AbstractRunnable.markArgumentsAsSequence(args);
String a;
do {
a = StringUtil.extract(func, OPERAND, "");
if (Util.isEmpty(a))
break;
args.put(a, values.get(a));
} while (true);
return args;
}
private BigDecimal operate(StringBuilder term, Map values) {
String o1 = extract(term, OPERAND, "");
String op = extract(term, OPERATION, "");
String o2 = extract(term, OPERAND, "");
String rest = StringUtil.substring(term, o2, null).trim();
/*
* the value map may contain any values - but the found value must have the right type!
*/
Object v1 = values.containsKey(o1) ? values.get(o1) : o1;
BigDecimal n1 = (BigDecimal) (v1 instanceof BigDecimal ? v1 : new BigDecimal(String.valueOf(v1)));
Object v2 = values.containsKey(o2) ? values.get(o2) : o2;
BigDecimal n2 = (BigDecimal) (v2 instanceof BigDecimal ? v2 : new BigDecimal(String.valueOf(v2)));
BigDecimal result;
switch (op.charAt(0)) {
case '+':
result = n1.add(n2);
break;
case '-':
result = n1.subtract(n2);
break;
case '*':
result = n1.multiply(n2);
break;
case '/':
result = n1.divide(n2);
break;
case '%':
result = new BigDecimal(n1.intValueExact() % n2.intValueExact());
break;
case '^':
result = n1.pow(n2.intValueExact());
break;
default:
throw new IllegalArgumentException(term.toString());
}
if (!rest.isEmpty()) {
term = new StringBuilder(result + rest);
result = operate(term, values);
}
return result;
}
private String extract(CharSequence source, String regexp) {
return extract(source, regexp, null);
}
private String extract(CharSequence source, String regexp, String replacement) {
return StringUtil.extract(source, regexp, replacement);
}
private Object func(String name, Number... values) {
return null;
}
}