diff --git a/symja-parser/src/main/java/org/matheclipse/parser/client/eval/api/AbstractASTVisitor.java b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/api/AbstractASTVisitor.java index f2b1f99..c9f9731 100644 --- a/symja-parser/src/main/java/org/matheclipse/parser/client/eval/api/AbstractASTVisitor.java +++ b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/api/AbstractASTVisitor.java @@ -19,7 +19,6 @@ import java.util.Map; import org.apache.commons.math3.FieldElement; -import org.apache.commons.math3.dfp.Dfp; import org.matheclipse.parser.client.ast.ASTNode; import org.matheclipse.parser.client.ast.FloatNode; import org.matheclipse.parser.client.ast.FractionNode; diff --git a/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionEvalVisitor.java b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionEvalVisitor.java new file mode 100644 index 0000000..ab7bf6c --- /dev/null +++ b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionEvalVisitor.java @@ -0,0 +1,1009 @@ +/* + * Copyright 2005-2014 Axel Kramer (axelclk@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.matheclipse.parser.client.eval.bigfraction; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.math3.Field; +import org.apache.commons.math3.fraction.BigFraction; +import org.apache.commons.math3.fraction.BigFractionField; +import org.matheclipse.parser.client.Parser; +import org.matheclipse.parser.client.SyntaxError; +import org.matheclipse.parser.client.ast.ASTNode; +import org.matheclipse.parser.client.ast.FloatNode; +import org.matheclipse.parser.client.ast.FractionNode; +import org.matheclipse.parser.client.ast.FunctionNode; +import org.matheclipse.parser.client.ast.IntegerNode; +import org.matheclipse.parser.client.ast.NumberNode; +import org.matheclipse.parser.client.ast.PatternNode; +import org.matheclipse.parser.client.ast.StringNode; +import org.matheclipse.parser.client.ast.SymbolNode; +import org.matheclipse.parser.client.eval.BooleanVariable; +import org.matheclipse.parser.client.eval.ComplexNode; +import org.matheclipse.parser.client.eval.DoubleNode; +import org.matheclipse.parser.client.eval.api.AbstractASTVisitor; +import org.matheclipse.parser.client.eval.api.FieldElementVariable; +import org.matheclipse.parser.client.eval.api.IBooleanBoolean1Function; +import org.matheclipse.parser.client.eval.api.IBooleanBoolean2Function; +import org.matheclipse.parser.client.eval.api.IBooleanFieldElement2Function; +import org.matheclipse.parser.client.eval.api.IBooleanFunction; +import org.matheclipse.parser.client.eval.api.IEvaluator; +import org.matheclipse.parser.client.eval.api.IFieldElement0Function; +import org.matheclipse.parser.client.eval.api.IFieldElement1Function; +import org.matheclipse.parser.client.eval.api.IFieldElement2Function; +import org.matheclipse.parser.client.eval.api.IFieldElementFunction; +import org.matheclipse.parser.client.eval.api.IFieldElementFunctionNode; +import org.matheclipse.parser.client.eval.api.IFieldElementInt2Function; +import org.matheclipse.parser.client.eval.api.function.CompoundExpressionFunction; +import org.matheclipse.parser.client.eval.api.function.PlusFunction; +import org.matheclipse.parser.client.eval.api.function.SetFunction; +import org.matheclipse.parser.client.eval.api.function.TimesFunction; +import org.matheclipse.parser.client.math.ArithmeticMathException; +import org.matheclipse.parser.client.operator.ASTNodeFactory; + +/** + * Evaluate math expressions to BigFraction numbers. + * + * @see org.apache.commons.math3.fraction.BigFraction + */ +public class BigFractionEvalVisitor extends AbstractASTVisitor { + + static class MaxFunction implements IFieldElementFunctionNode, IFieldElement2Function { + @Override + public BigFraction evaluate(BigFraction arg1, BigFraction arg2) { + return (arg1.compareTo(arg2)) > 0 ? arg1 : arg2; + } + + @Override + public BigFraction evaluate(IEvaluator engine, FunctionNode function) { + BigFraction result = null; + int end = function.size(); + if (end > 1) { + result = engine.evaluateNode(function.getNode(1)); + for (int i = 2; i < end; i++) { + result = evaluate(result, engine.evaluateNode(function.getNode(i))); + } + } + return result; + } + } + + static class MinFunction implements IFieldElementFunctionNode, IFieldElement2Function { + @Override + public BigFraction evaluate(BigFraction arg1, BigFraction arg2) { + return (arg1.compareTo(arg2)) < 0 ? arg1 : arg2; + } + + @Override + public BigFraction evaluate(IEvaluator engine, FunctionNode function) { + BigFraction result = null; + int end = function.size(); + if (end > 1) { + result = engine.evaluateNode(function.getNode(1)); + for (int i = 2; i < end; i++) { + result = evaluate(result, engine.evaluateNode(function.getNode(i))); + } + } + return result; + } + } + + private static Map SYMBOL_MAP; + + private static Map SYMBOL_BOOLEAN_MAP; + + private static Map FUNCTION_MAP; + + private static Map FUNCTION_BOOLEAN_MAP; + + static { + // TODO: get better precision for constants + SYMBOL_MAP = new ConcurrentHashMap(); + + SYMBOL_BOOLEAN_MAP = new ConcurrentHashMap(); + SYMBOL_BOOLEAN_MAP.put("False", Boolean.FALSE); + SYMBOL_BOOLEAN_MAP.put("True", Boolean.TRUE); + + FUNCTION_BOOLEAN_MAP = new ConcurrentHashMap(); + + FUNCTION_BOOLEAN_MAP.put("And", new IBooleanBoolean2Function() { + @Override + public boolean evaluate(boolean arg1, boolean arg2) { + return arg1 && arg2; + } + }); + FUNCTION_BOOLEAN_MAP.put("Not", new IBooleanBoolean1Function() { + @Override + public boolean evaluate(boolean arg1) { + return !arg1; + } + }); + FUNCTION_BOOLEAN_MAP.put("Or", new IBooleanBoolean2Function() { + @Override + public boolean evaluate(boolean arg1, boolean arg2) { + return arg1 || arg2; + } + }); + + FUNCTION_BOOLEAN_MAP.put("Equal", new IBooleanFieldElement2Function() { + @Override + public boolean evaluate(BigFraction arg1, BigFraction arg2) { + return arg1.equals(arg2); + } + }); + FUNCTION_BOOLEAN_MAP.put("Greater", new IBooleanFieldElement2Function() { + @Override + public boolean evaluate(BigFraction arg1, BigFraction arg2) { + return (arg1.compareTo(arg2)) > 0; + } + }); + FUNCTION_BOOLEAN_MAP.put("GreaterEqual", new IBooleanFieldElement2Function() { + @Override + public boolean evaluate(BigFraction arg1, BigFraction arg2) { + return (arg1.compareTo(arg2)) >= 0; + } + }); + FUNCTION_BOOLEAN_MAP.put("Less", new IBooleanFieldElement2Function() { + @Override + public boolean evaluate(BigFraction arg1, BigFraction arg2) { + return (arg1.compareTo(arg2)) < 0; + } + }); + FUNCTION_BOOLEAN_MAP.put("LessEqual", new IBooleanFieldElement2Function() { + @Override + public boolean evaluate(BigFraction arg1, BigFraction arg2) { + return (arg1.compareTo(arg2)) <= 0; + } + }); + FUNCTION_BOOLEAN_MAP.put("Unequal", new IBooleanFieldElement2Function() { + @Override + public boolean evaluate(BigFraction arg1, BigFraction arg2) { + return !arg1.equals(arg2); + } + }); + + FUNCTION_MAP = new ConcurrentHashMap(); + FUNCTION_MAP.put("CompoundExpression", new CompoundExpressionFunction()); + FUNCTION_MAP.put("Set", new SetFunction()); + FUNCTION_MAP.put("Max", new MaxFunction()); + FUNCTION_MAP.put("Min", new MinFunction()); + FUNCTION_MAP.put("Plus", new PlusFunction()); + FUNCTION_MAP.put("Times", new TimesFunction()); + // + // Functions with 0 argument + // + // FUNCTION_DFP_MAP.put("Random", new IBigFraction0Function() { + // public BigFraction evaluate() { + // return BigFractionMath.random(); + // } + // }); + // + // Functions with 1 argument + // + + // FUNCTION_MAP.put("Ceiling", new IFieldElement1Function() + // { + // @Override + // public BigFraction evaluate(BigFraction arg1) { + // return arg1.ceil(); + // } + // }); + // FUNCTION_MAP.put("Floor", new IFieldElement1Function() { + // @Override + // public BigFraction evaluate(BigFraction arg1) { + // return arg1.floor(); + // } + // }); + FUNCTION_MAP.put("Sign", new IFieldElement1Function() { + @Override + public BigFraction evaluate(BigFraction arg1) { + return (arg1.equals(BigFraction.ZERO)) ? BigFraction.ZERO + : (arg1.compareTo(BigFraction.ZERO) > 0) ? BigFraction.ONE : BigFraction.MINUS_ONE; + } + }); + + // + // Functions with 2 arguments + // + FUNCTION_MAP.put("Power", new IFieldElementInt2Function() { + @Override + public BigFraction evaluate(BigFraction arg1, BigFraction arg2) { + if (arg2.getDenominator().equals(BigInteger.ONE)) { + return arg1.pow(arg2.getNumerator()); + } + throw new ArithmeticMathException( + "Power#evaluate(BigFraction, BigFraction) not possible for argument 2: " + arg2.toString()); + } + + @Override + public BigFraction evaluate(BigFraction arg1, int n) { + return arg1.pow(n); + } + }); + + } + + /** + * Parse the given expression String and return the resulting + * ASTNode + * + * @param expression + * @return + * @throws SyntaxError + */ + public static ASTNode parseNode(final int decimalDigits, String expression, boolean relaxedSyntax) { + BigFractionEvalVisitor dfpEvaluator = new BigFractionEvalVisitor(relaxedSyntax); + return dfpEvaluator.parse(expression); + } + + private final BigFractionField fDfpField; + + private final BigFractionNode fZERO; + + private ASTNode fNode; + + private final ASTNodeFactory fASTFactory; + + public BigFractionEvalVisitor() { + this(null, false); + } + + public BigFractionEvalVisitor(ASTNode node, boolean relaxedSyntax) { + super(relaxedSyntax); + fASTFactory = new ASTNodeFactory(relaxedSyntax); + fVariableMap = new HashMap>(); + fBooleanVariables = new HashMap(); + fNode = node; + fDfpField = BigFractionField.getInstance(); + fZERO = new BigFractionNode(fDfpField.getZero()); + init(); + if (fRelaxedSyntax) { + if (SYMBOL_MAP.get("pi") == null) { + // init tables for relaxed mode + for (String key : SYMBOL_MAP.keySet()) { + SYMBOL_MAP.put(key.toLowerCase(), SYMBOL_MAP.get(key)); + } + for (String key : SYMBOL_BOOLEAN_MAP.keySet()) { + SYMBOL_BOOLEAN_MAP.put(key.toLowerCase(), SYMBOL_BOOLEAN_MAP.get(key)); + } + for (String key : FUNCTION_MAP.keySet()) { + FUNCTION_MAP.put(key.toLowerCase(), FUNCTION_MAP.get(key)); + } + for (String key : FUNCTION_BOOLEAN_MAP.keySet()) { + FUNCTION_BOOLEAN_MAP.put(key.toLowerCase(), FUNCTION_BOOLEAN_MAP.get(key)); + } + } + } + } + + public BigFractionEvalVisitor(boolean relaxedSyntax) { + this(null, relaxedSyntax); + } + + /** + * Clear all defined variables for this evaluator. + */ + @Override + public void clearVariables() { + fVariableMap.clear(); + fBooleanVariables.clear(); + } + + @Override + public FieldElementVariable createVariable(BigFraction value) { + return new BigFractionVariable(value); + } + + /** + * Define a value for a given variable name. + * + * @param variableName + * @param value + */ + public void defineVariable(String variableName) { + if (fRelaxedSyntax) { + fVariableMap.put(variableName.toLowerCase(), new BigFractionVariable(fDfpField.getZero())); + } else { + fVariableMap.put(variableName, new BigFractionVariable(fDfpField.getZero())); + } + } + + /** + * Define a boolean value for a given variable name. + * + * @param variableName + * @param value + */ + @Override + public void defineVariable(String variableName, BooleanVariable value) { + if (fRelaxedSyntax) { + fBooleanVariables.put(variableName.toLowerCase(), value); + } else { + fBooleanVariables.put(variableName, value); + } + + } + + /** + * Define a value for a given variable name. + * + * @param variableName + * @param value + */ + public void defineVariable(String variableName, BigFraction value) { + if (fRelaxedSyntax) { + fVariableMap.put(variableName.toLowerCase(), new BigFractionVariable(value)); + } else { + fVariableMap.put(variableName, new BigFractionVariable(value)); + } + } + + // public FieldElementVariable defineVariable(String + // variableName, double value) { + // FieldElementVariable val = new + // BigFractionVariable(fDfpField.newDfp(value)); + // if (fRelaxedSyntax) { + // fVariableMap.put(variableName.toLowerCase(), val); + // } else { + // fVariableMap.put(variableName, val); + // } + // return val; + // } + + /** + * Define a value for a given variable name. + * + * @param variableName + * @param value + */ + @Override + public void defineVariable(String variableName, FieldElementVariable value) { + if (fRelaxedSyntax) { + fVariableMap.put(variableName.toLowerCase(), value); + } else { + fVariableMap.put(variableName, value); + } + } + + /** + * + * @param node + * @param var + * @return + */ + public ASTNode derivative(final ASTNode node, String var) { + SymbolNode sym = fASTFactory.createSymbol(var); + return derivative(node, sym); + } + + /** + * + * TODO: add more derivation rules + * + * @param node + * @param var + * @return + */ + public ASTNode derivative(final ASTNode node, SymbolNode var) { + if (node.isFree(var)) { + return new BigFractionNode(fDfpField.getZero()); + } + if (node instanceof FunctionNode) { + FunctionNode f = (FunctionNode) node; + if (f.size() > 1 && f.getNode(0) instanceof SymbolNode) { + SymbolNode head = (SymbolNode) f.getNode(0); + if (f.size() == 2) { + ASTNode arg1Derived = derivative(f.getNode(1), var); + if (isSymbol(head, "Exp")) { + FunctionNode fun = new FunctionNode(fASTFactory.createSymbol("Exp")); + fun.add(f.getNode(1)); + return getDerivativeResult(arg1Derived, fun); + } + if (isSymbol(head, "Cos")) { + FunctionNode fun = new FunctionNode(fASTFactory.createSymbol("Times")); + fun.add(new BigFractionNode(BigFraction.MINUS_ONE)); + fun.add(new FunctionNode(fASTFactory.createSymbol("Cos"), f.getNode(1))); + return getDerivativeResult(arg1Derived, fun); + } + if (isSymbol(head, "Sin")) { + FunctionNode fun = new FunctionNode(fASTFactory.createSymbol("Cos")); + fun.add(f.getNode(1)); + return getDerivativeResult(arg1Derived, fun); + } + } else if (f.size() == 3 && isSymbol(head, "Power")) { + if (f.get(2).isFree(var)) {// derive x^r + ASTNode arg1Derived = derivative(f.getNode(1), var); + // (r-1) + FunctionNode exponent = fASTFactory.createFunction(fASTFactory.createSymbol("Plus"), + new BigFractionNode(BigFraction.MINUS_ONE), f.get(2)); + // r*x^(r-1) + FunctionNode fun = fASTFactory.createFunction(fASTFactory.createSymbol("Times"), f.get(2), + fASTFactory.createFunction(fASTFactory.createSymbol("Power"), f.get(1), exponent)); + return getDerivativeResult(arg1Derived, fun); + } + if (f.get(1).isFree(var)) {// derive a^x + ASTNode arg2Derived = derivative(f.getNode(2), var); + // log(a) * a^x + FunctionNode fun = fASTFactory.createFunction(fASTFactory.createSymbol("Times"), + fASTFactory.createFunction(fASTFactory.createSymbol("Log"), f.get(1)), f); + return getDerivativeResult(arg2Derived, fun); + } + } else { + if (isSymbol(head, "Plus")) { + FunctionNode result = new FunctionNode(f.getNode(0)); + for (int i = 1; i < f.size(); i++) { + ASTNode deriv = derivative(f.getNode(i), var); + if (!deriv.equals(fZERO)) { + result.add(deriv); + } + } + return result; + } + if (isSymbol(head, "Times")) { + FunctionNode plusResult = new FunctionNode(fASTFactory.createSymbol("Plus")); + for (int i = 1; i < f.size(); i++) { + FunctionNode timesResult = new FunctionNode(f.getNode(0)); + boolean valid = true; + for (int j = 1; j < f.size(); j++) { + if (j == i) { + ASTNode deriv = derivative(f.getNode(j), var); + if (deriv.equals(fZERO)) { + valid = false; + } else { + timesResult.add(deriv); + } + } else { + timesResult.add(f.getNode(j)); + } + } + if (valid) { + plusResult.add(timesResult); + } + } + return plusResult; + } + } + } + return new FunctionNode(new SymbolNode("D"), node, var); + // return evaluateFunction((FunctionNode) node); + } + if (node instanceof SymbolNode) { + if (isSymbol((SymbolNode) node, var)) { + return new BigFractionNode(fDfpField.getOne()); + } + FieldElementVariable v = fVariableMap.get(node.toString()); + if (v != null) { + return new BigFractionNode(fDfpField.getZero()); + } + BigFraction dbl = SYMBOL_MAP.get(node.toString()); + if (dbl != null) { + return new BigFractionNode(fDfpField.getZero()); + } + return new BigFractionNode(fDfpField.getZero()); + } else if (node instanceof NumberNode) { + return new BigFractionNode(fDfpField.getZero()); + } + + throw new ArithmeticMathException( + "BigFractionEvaluator#derivative(ASTNode, SymbolNode) not possible for: " + node.toString()); + } + + /** + * Reevaluate the expression (possibly after a new Variable + * assignment) + * + * @param Expression + * @return + * @throws SyntaxError + */ + public BigFraction evaluate() { + if (fNode == null) { + throw new SyntaxError(0, 0, 0, " ", "No parser input defined", 1); + } + return evaluateNode(fNode); + } + + /** + * Parse the given expression String and evaluate it to a + * BigFraction value + * + * @param expression + * @return + * @throws SyntaxError + */ + public BigFraction evaluate(String expression) { + Parser p; + if (fRelaxedSyntax) { + p = new Parser(ASTNodeFactory.RELAXED_STYLE_FACTORY, true); + } else { + p = new Parser(ASTNodeFactory.MMA_STYLE_FACTORY, false); + } + fNode = p.parse(expression); + if (fNode instanceof FunctionNode) { + fNode = optimizeFunction((FunctionNode) fNode); + } + return evaluateNode(fNode); + } + + /** + * Evaluate an already parsed in FunctionNode into a + * souble number value. + * + * @param functionNode + * @return + * + * @throws ArithmeticMathException + * if the functionNode cannot be evaluated. + */ + public BigFraction evaluateFunction(final FunctionNode functionNode) { + if (functionNode.size() > 0 && functionNode.getNode(0) instanceof SymbolNode) { + String symbol = functionNode.getNode(0).toString(); + if (symbol.equals("If") || (fRelaxedSyntax && symbol.equalsIgnoreCase("if"))) { + if (functionNode.size() == 3) { + if (evaluateNodeLogical(functionNode.getNode(1))) { + return evaluateNode(functionNode.getNode(2)); + } + } else if (functionNode.size() == 4) { + if (evaluateNodeLogical(functionNode.getNode(1))) { + return evaluateNode(functionNode.getNode(2)); + } else { + return evaluateNode(functionNode.getNode(3)); + } + } + } else { + Object obj = FUNCTION_MAP.get(symbol); + if (obj instanceof IFieldElementFunctionNode) { + return ((IFieldElementFunctionNode) obj).evaluate(this, functionNode); + } + if (functionNode.size() == 1) { + if (obj instanceof IFieldElement0Function) { + return ((IFieldElement0Function) obj).evaluate(); + } + } else if (functionNode.size() == 2) { + if (obj instanceof IFieldElement1Function) { + return ((IFieldElement1Function) obj) + .evaluate(evaluateNode(functionNode.getNode(1))); + } + } else if (functionNode.size() == 3) { + ASTNode arg2 = functionNode.getNode(2); + if (obj instanceof IFieldElementInt2Function && arg2 instanceof IntegerNode) { + return ((IFieldElementInt2Function) obj) + .evaluate(evaluateNode(functionNode.getNode(1)), ((IntegerNode) arg2).getIntValue()); + } + if (obj instanceof IFieldElement2Function) { + return ((IFieldElement2Function) obj) + .evaluate(evaluateNode(functionNode.getNode(1)), evaluateNode(arg2)); + } + } + // if (fCallbackFunction != null) { + // BigFraction dfpArgs[] = new BigFraction[functionNode.size() - + // 1]; + // for (int i = 0; i < dfpArgs.length; i++) { + // dfpArgs[i] = evaluateNode(functionNode.getNode(i + 1)); + // } + // return fCallbackFunction.evaluate(this, functionNode, + // dfpArgs); + // } + } + } + throw new ArithmeticMathException( + "DfpEvaluator#evaluateFunction(FunctionNode) not possible for: " + functionNode.toString()); + } + + @Override + public boolean evaluateFunctionLogical(final FunctionNode functionNode) { + if (functionNode.size() > 0 && functionNode.getNode(0) instanceof SymbolNode) { + String symbol = functionNode.getNode(0).toString(); + if (functionNode.size() == 2) { + Object obj = FUNCTION_BOOLEAN_MAP.get(symbol); + if (obj instanceof IBooleanBoolean1Function) { + return ((IBooleanBoolean1Function) obj).evaluate(evaluateNodeLogical(functionNode.getNode(1))); + } + } else if (functionNode.size() == 3) { + Object obj = FUNCTION_BOOLEAN_MAP.get(symbol); + if (obj instanceof IBooleanFieldElement2Function) { + return ((IBooleanFieldElement2Function) obj) + .evaluate(evaluateNode(functionNode.getNode(1)), evaluateNode(functionNode.getNode(2))); + } else if (obj instanceof IBooleanBoolean2Function) { + return ((IBooleanBoolean2Function) obj).evaluate(evaluateNodeLogical(functionNode.getNode(1)), + evaluateNodeLogical(functionNode.getNode(2))); + } + // } else { + // Object obj = FUNCTION_BOOLEAN_MAP.get(symbol); + // if (obj instanceof IBooleanBigFractionFunction) { + // return ((IBooleanBigFractionFunction) obj).evaluate(this, + // functionNode); + // } + } + } + throw new ArithmeticMathException( + "BigFractionEvaluator#evaluateFunctionLogical(FunctionNode) not possible for: " + + functionNode.toString()); + + } + + /** + * Evaluate an already parsed in abstract syntax tree node into a + * BigFraction number value. + * + * @param node + * abstract syntax tree node + * + * @return the evaluated BigFraction number + * + * @throws ArithmeticMathException + * if the node cannot be evaluated. + */ + @Override + public BigFraction evaluateNode(final ASTNode node) { + if (node instanceof BigFractionNode) { + return ((BigFractionNode) node).getValue(); + } + if (node instanceof FunctionNode) { + return evaluateFunction((FunctionNode) node); + } + if (node instanceof SymbolNode) { + FieldElementVariable v = fVariableMap.get(node.toString()); + if (v != null) { + return v.getValue(); + } + BigFraction dbl = SYMBOL_MAP.get(node.toString()); + if (dbl != null) { + return dbl; + } + } else if (node instanceof NumberNode) { + if (node instanceof FractionNode) { + return new BigFraction(new BigInteger(((FractionNode) node).getNumerator().getString()), + new BigInteger(((FractionNode) node).getDenominator().getString())); + } else if (node instanceof IntegerNode) { + String iStr = ((NumberNode) node).getString(); + if (iStr != null) { + return new BigFraction(new BigInteger(iStr), BigInteger.ONE); + } else { + return new BigFraction(((IntegerNode) node).getIntValue(), 1); + } + } + + return new BigFraction(((NumberNode) node).doubleValue()); + } + + throw new ArithmeticMathException( + "BigFractionEvaluator#evaluateNode(ASTNode) not possible for: " + node.toString()); + } + + @Override + public boolean evaluateNodeLogical(final ASTNode node) { + if (node instanceof FunctionNode) { + return evaluateFunctionLogical((FunctionNode) node); + } + if (node instanceof SymbolNode) { + BooleanVariable v = fBooleanVariables.get(node.toString()); + if (v != null) { + return v.getValue(); + } + Boolean boole = SYMBOL_BOOLEAN_MAP.get(node.toString()); + if (boole != null) { + return boole.booleanValue(); + } + } + + throw new ArithmeticMathException( + "BigFractionEvaluator#evaluateNodeLogical(ASTNode) not possible for: " + node.toString()); + } + + private ASTNode getDerivativeResult(ASTNode arg1Derived, FunctionNode fun) { + if (!arg1Derived.equals(new BigFractionNode(fDfpField.getOne()))) { + FunctionNode res = new FunctionNode(fASTFactory.createSymbol("Times")); + res.add(arg1Derived); + res.add(fun); + return res; + } + return fun; + } + + @Override + public Field getField() { + return fDfpField; + } + + @Override + public IBooleanFunction getFunctionBooleanMap(String symbolName) { + return FUNCTION_BOOLEAN_MAP.get(symbolName); + } + + @Override + public IFieldElementFunction getFunctionMap(String symbolName) { + return FUNCTION_MAP.get(symbolName); + } + + @Override + public Boolean getSymbolBooleanMap(String symbolName) { + return SYMBOL_BOOLEAN_MAP.get(symbolName); + } + + @Override + public BigFraction getSymbolFieldElementMap(String symbolName) { + return SYMBOL_MAP.get(symbolName); + } + + /** + * Returns the BigFraction variable value to which the specified + * variableName is mapped, or {@code null} if this map contains no mapping + * for the variableName. + * + * @param variableName + * @return + */ + @Override + public FieldElementVariable getVariable(String variableName) { + if (fRelaxedSyntax) { + return fVariableMap.get(variableName.toLowerCase()); + } else { + return fVariableMap.get(variableName); + } + } + + /** + * Get the variable names from the given AST node. + * + * @param node + * an already parsed AST node + * @param result + * a set which contains the variable names + */ + public void getVariables(final ASTNode node, Set result) { + if (node instanceof FunctionNode) { + FunctionNode functionNode = (FunctionNode) node; + if (functionNode.size() > 0 && functionNode.getNode(0) instanceof SymbolNode) { + for (int i = 1; i < functionNode.size(); i++) { + getVariables(functionNode.getNode(i), result); + } + } + } + if (node instanceof SymbolNode) { + Object obj = SYMBOL_MAP.get(node.toString()); + if (obj == null) { + obj = SYMBOL_BOOLEAN_MAP.get(node.toString()); + if (obj == null) { + result.add(node.toString()); + } + } + + } + } + + /** + * Get the variable names from the given expression. + * + * @param expression + * @param result + * a set which contains the variable names + */ + public void getVariables(String expression, Set result) { + getVariables(expression, result, true); + } + + /** + * Get the variable names from the given expression. + * + * @param expression + * @param result + * a set which contains the variable names + * @param relaxedSyntax + * if true us e function syntax like + * sin(x) otherwise use Sin[x]. + */ + public void getVariables(String expression, Set result, boolean relaxedSyntax) { + Parser p = new Parser(relaxedSyntax ? ASTNodeFactory.RELAXED_STYLE_FACTORY : ASTNodeFactory.MMA_STYLE_FACTORY, + relaxedSyntax); + ASTNode node = p.parse(expression); + getVariables(node, result); + } + + void init() { + // TODO: get better precision for constants + // SYMBOL_MAP.put("Catalan", + // fDfpField.newDfp(0.91596559417721901505460351493238411077414937428167)); + // SYMBOL_MAP.put("Degree", fDfpField.newDfp(Math.PI / 180)); + // SYMBOL_MAP.put("E", fDfpField.getE()); + // SYMBOL_MAP.put("Pi", fDfpField.getPi()); + // SYMBOL_MAP.put("EulerGamma", + // fDfpField.newDfp(0.57721566490153286060651209008240243104215933593992)); + // SYMBOL_MAP.put("Glaisher", + // fDfpField.newDfp(1.2824271291006226368753425688697917277676889273250)); + // SYMBOL_MAP.put("GoldenRatio", + // fDfpField.newDfp(1.6180339887498948482045868343656381177203091798058)); + // SYMBOL_MAP.put("Khinchin", + // fDfpField.newDfp(2.6854520010653064453097148354817956938203822939945)); + + } + + /** + * Check if the given symbol is a SymbolNode and test if the + * names are equal. + * + * @param symbol1 + * @param symbol2Name + * @return + */ + public boolean isSymbol(SymbolNode symbol1, String symbol2Name) { + if (fRelaxedSyntax) { + return symbol1.getString().equalsIgnoreCase(symbol2Name); + } + return symbol1.getString().equals(symbol2Name); + } + + /** + * Check if the given symbol is a SymbolNode and test if the + * names are equal. + * + * @param symbol1 + * @param symbol2 + * @return + */ + public boolean isSymbol(SymbolNode symbol1, SymbolNode symbol2) { + if (fRelaxedSyntax) { + return symbol1.getString().equalsIgnoreCase(symbol2.getString()); + } + return symbol1.equals(symbol2); + } + + /** + * Optimize an already parsed in functionNode into an + * ASTNode. + * + * @param functionNode + * @return + * + */ + @Override + public ASTNode optimizeFunction(final FunctionNode functionNode) { + // if (functionNode.size() > 0) { + // boolean dfpOnly = true; + // ASTNode node; + // for (int i = 1; i < functionNode.size(); i++) { + // node = functionNode.getNode(i); + // if (node instanceof NumberNode) { + // if (node instanceof FractionNode) { + // functionNode.set(i, + // new BigFractionNode(fDfpField.newDfp(((FractionNode) + // node).getNumerator().toString()) + // .divide(fDfpField.newDfp(((FractionNode) + // node).getDenominator().toString())))); + // } else if (node instanceof IntegerNode) { + // String iStr = ((NumberNode) functionNode.getNode(i)).getString(); + // if (iStr != null) { + // functionNode.set(i, new BigFractionNode(fDfpField.newDfp(iStr))); + // } else { + // functionNode.set(i, new BigFractionNode( + // fDfpField.newDfp(((IntegerNode) + // functionNode.getNode(i)).getIntValue()))); + // } + // } else { + // functionNode.set(i, new BigFractionNode( + // fDfpField.newDfp(((NumberNode) + // functionNode.getNode(i)).getString()))); + // } + // } else if (functionNode.getNode(i) instanceof FunctionNode) { + // ASTNode optNode = optimizeFunction((FunctionNode) + // functionNode.getNode(i)); + // if (!(optNode instanceof BigFractionNode)) { + // dfpOnly = false; + // } + // functionNode.set(i, optNode); + // } else if (node instanceof SymbolNode) { + // BigFraction dbl = SYMBOL_MAP.get(node.toString()); + // if (dbl != null) { + // functionNode.set(i, new BigFractionNode(dbl)); + // } else { + // dfpOnly = false; + // } + // } else { + // dfpOnly = false; + // } + // } + // if (dfpOnly) { + // try { + // return new BigFractionNode(evaluateFunction(functionNode)); + // } catch (Exception e) { + // + // } + // } + // } + return functionNode; + } + + /** + * Parse the given expression String and store the resulting + * ASTNode in this BigFractionEvaluator + * + * @param expression + * @return + * @throws SyntaxError + */ + public ASTNode parse(String expression) { + Parser p; + if (fRelaxedSyntax) { + p = new Parser(ASTNodeFactory.RELAXED_STYLE_FACTORY, true); + } else { + p = new Parser(ASTNodeFactory.MMA_STYLE_FACTORY, false); + } + fNode = p.parse(expression); + if (fNode instanceof FunctionNode) { + fNode = optimizeFunction((FunctionNode) fNode); + } + return fNode; + } + + @Override + public void setUp(BigFraction data) { + super.setUp(data); + } + + public void setValue(FieldElementVariable variable, double value) { + variable.setValue(new BigFraction(value)); + } + + @Override + public void tearDown() { + } + + @Override + public BigFraction visit(ComplexNode node) { + return null; + } + + @Override + public BigFraction visit(DoubleNode node) { + return new BigFraction(node.doubleValue()); + } + + @Override + public BigFraction visit(FloatNode node) { + return new BigFraction(node.doubleValue()); + } + + @Override + public BigFraction visit(FractionNode node) { + return new BigFraction(new BigInteger(((FractionNode) node).getNumerator().getString()), + new BigInteger(((FractionNode) node).getDenominator().getString())); + } + + @Override + public BigFraction visit(IntegerNode node) { + String iStr = ((NumberNode) node).getString(); + if (iStr != null) { + return new BigFraction(new BigInteger(iStr), BigInteger.ONE); + } else { + return new BigFraction(((IntegerNode) node).getIntValue(), 1); + } + } + + @Override + public BigFraction visit(PatternNode node) { + return null; + } + + @Override + public BigFraction visit(StringNode node) { + return null; + } + +} diff --git a/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionEvaluator.java b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionEvaluator.java new file mode 100644 index 0000000..5a9806f --- /dev/null +++ b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionEvaluator.java @@ -0,0 +1,31 @@ +package org.matheclipse.parser.client.eval.bigfraction; + +import org.apache.commons.math3.fraction.BigFraction; +import org.matheclipse.parser.client.eval.api.FieldElementEvaluator; +import org.matheclipse.parser.client.eval.api.IASTVisitor; + +public class BigFractionEvaluator extends FieldElementEvaluator { + /** + * Returns a String representation of the given + * org.apache.commons.math3.fraction.BigFraction number. + * + * @param c + * @return + * + */ + public static String toString(BigFraction bf) { + return bf.toString(); + } + + public BigFractionEvaluator(IASTVisitor visitor, boolean relaxedSyntax) { + super(visitor, relaxedSyntax); + } + + public BigFractionEvaluator() { + this(new BigFractionEvalVisitor(false), false); + } + + public BigFractionEvaluator(boolean relaxedSyntax) { + this(new BigFractionEvalVisitor(relaxedSyntax), relaxedSyntax); + } +} diff --git a/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionNode.java b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionNode.java new file mode 100644 index 0000000..a06c32b --- /dev/null +++ b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionNode.java @@ -0,0 +1,58 @@ +/* + * Copyright 2005-2014 Axel Kramer (axelclk@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.matheclipse.parser.client.eval.bigfraction; + +import org.apache.commons.math3.fraction.BigFraction; +import org.matheclipse.parser.client.ast.ASTNode; + +/** + * + */ +public class BigFractionNode extends ASTNode { + + private final BigFraction value; + + public BigFractionNode(BigFraction value) { + super("DoubleNode"); + this.value = value; + } + + public BigFraction getValue() { + return value; + } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BigFractionNode) { + return value.equals(((BigFractionNode) obj).value); + } + return false; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionVariable.java b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionVariable.java new file mode 100644 index 0000000..4caabd0 --- /dev/null +++ b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/BigFractionVariable.java @@ -0,0 +1,45 @@ +/* + * Copyright 2005-2014 Axel Kramer (axelclk@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.matheclipse.parser.client.eval.bigfraction; + +import org.apache.commons.math3.fraction.BigFraction; +import org.matheclipse.parser.client.eval.api.FieldElementVariable; + +public class BigFractionVariable extends FieldElementVariable { + BigFraction value; + + public BigFractionVariable(BigFraction v) { + value = v; + } + + @Override + public BigFraction getValue() { + return value; + } + + @Override + public void setValue(BigFraction value) { + this.value = value; + } + + public void setValue(int value) { + this.value = new BigFraction(value); + } + + public void setValue(int numerator, int denominator) { + this.value = new BigFraction(numerator, denominator); + } +} diff --git a/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/package.html b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/package.html new file mode 100644 index 0000000..cee8028 --- /dev/null +++ b/symja-parser/src/main/java/org/matheclipse/parser/client/eval/bigfraction/package.html @@ -0,0 +1,11 @@ + + + + + + +

+Predefined evaluation engine BigFractionEvaluator for the org.apache.commons.math3.fraction.BigFraction class. +

+ + diff --git a/symja-parser/src/test/java/org/matheclipse/parser/test/eval/AllEvalTests.java b/symja-parser/src/test/java/org/matheclipse/parser/test/eval/AllEvalTests.java index d9a6079..076f32c 100644 --- a/symja-parser/src/test/java/org/matheclipse/parser/test/eval/AllEvalTests.java +++ b/symja-parser/src/test/java/org/matheclipse/parser/test/eval/AllEvalTests.java @@ -17,6 +17,7 @@ public static Test suite() { s.addTestSuite(EvalDoubleRelaxedTestCase.class); s.addTestSuite(EvalComplexRelaxedTestCase.class); s.addTestSuite(EvalDfpRelaxedTestCase.class); + s.addTestSuite(EvalBigFractionRelaxedTestCase.class); return s; } diff --git a/symja-parser/src/test/java/org/matheclipse/parser/test/eval/EvalBigFractionRelaxedTestCase.java b/symja-parser/src/test/java/org/matheclipse/parser/test/eval/EvalBigFractionRelaxedTestCase.java new file mode 100644 index 0000000..84deb66 --- /dev/null +++ b/symja-parser/src/test/java/org/matheclipse/parser/test/eval/EvalBigFractionRelaxedTestCase.java @@ -0,0 +1,112 @@ +package org.matheclipse.parser.test.eval; + +import org.apache.commons.math3.fraction.BigFraction; +import org.junit.Assert; +import org.matheclipse.parser.client.eval.BooleanVariable; +import org.matheclipse.parser.client.eval.bigfraction.BigFractionEvalVisitor; +import org.matheclipse.parser.client.eval.bigfraction.BigFractionEvaluator; +import org.matheclipse.parser.client.eval.bigfraction.BigFractionVariable; + +import junit.framework.TestCase; + +/** + * Tests evaluation in BigFraction expression mode + * + * @see org.apache.commons.math3.fraction.BigFraction + */ +public class EvalBigFractionRelaxedTestCase extends TestCase { + + public EvalBigFractionRelaxedTestCase(String name) { + super(name); + } + + public void check(String in, String compareWith) { + try { + BigFractionEvalVisitor engine = new BigFractionEvalVisitor(true); + BigFraction bf = engine.evaluate(in); + String result = BigFractionEvaluator.toString(bf); + assertEquals(result, compareWith); + } catch (Exception e) { + e.printStackTrace(); + assertEquals("", e.getMessage()); + } + } + + public void testEval001() { + + check("42", "42"); + check("1.5", "3 / 2"); + check("-42", "-42"); + check("+42", "42"); + check("-42.1", "-5925048259759309 / 140737488355328"); + check("+42.2", "2969561004297421 / 70368744177664"); + check("-3/4", "-3 / 4"); + check("+3/4", "3 / 4"); + check("0^2", "0"); + check("3^3", "27"); + check("2^9", "512"); + check("2^3^2", "512"); + check("(2^3)^2", "64"); + check("3+4*7", "31"); + check("3+4*7*3", "87"); + check("1+2+3+4*7*3", "90"); + } + + public void testEval003() { + try { + BigFractionVariable vc = new BigFractionVariable(new BigFraction(3)); + BigFractionEvalVisitor engine = new BigFractionEvalVisitor(true); + engine.defineVariable("x", vc); + BigFraction bf = engine.evaluate("x^2+3*x^3"); + String result = BigFractionEvaluator.toString(bf); + assertEquals(result, "90"); + + vc.setValue(new BigFraction(4)); + bf = engine.evaluate(); + result = BigFractionEvaluator.toString(bf); + assertEquals(result, "208"); + } catch (Exception e) { + e.printStackTrace(); + assertEquals("", e.getMessage()); + } + } + + public void testEval006() { + try { + BigFractionEvalVisitor engine = new BigFractionEvalVisitor(true); + BooleanVariable vb = new BooleanVariable(true); + engine.defineVariable("$1", vb); + BooleanVariable vb2 = new BooleanVariable(true); + engine.defineVariable("$2", vb2); + BigFraction bf = engine.evaluate("if($1 && $2, 1, 0)"); + Assert.assertEquals(BigFractionEvaluator.toString(bf), "1"); + vb.setValue(false); + bf = engine.evaluate(); + Assert.assertEquals(BigFractionEvaluator.toString(bf), "0"); + } catch (Exception e) { + e.printStackTrace(); + Assert.assertEquals("", e.getMessage()); + } + } + + public void testEval007() { + try { + + BigFractionEvalVisitor engine = new BigFractionEvalVisitor(true); + + BigFractionVariable vc = new BigFractionVariable(new BigFraction(3)); + engine.defineVariable("$1", vc); + + BigFractionVariable vc2 = new BigFractionVariable(new BigFraction(-4)); + engine.defineVariable("$2", vc2); + BigFraction bf = engine.evaluate("$i = $1+$2; IF($i==0, 1, -1)"); + Assert.assertEquals(BigFractionEvaluator.toString(bf), "-1"); + vc2.setValue(-3); + bf = engine.evaluate(); + Assert.assertEquals(BigFractionEvaluator.toString(bf), "1"); + } catch (Exception e) { + e.printStackTrace(); + Assert.assertEquals("", e.getMessage()); + } + } +} \ No newline at end of file