/*
 * Copyright 2005-2009 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;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

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.api.AbstractASTVisitor;
import org.matheclipse.parser.client.eval.api.IEvaluator;
import org.matheclipse.parser.client.math.ArithmeticMathException;
import org.matheclipse.parser.client.math.Complex;
import org.matheclipse.parser.client.math.MathException;

/**
 * Abstract AST visitor with empty default method implementation.
 * 
 * @param 
 */
public class ComplexEvalVisitor extends AbstractASTVisitor {

	public final static boolean DEBUG = false;

	// private static double EPSILON = 1.0e-15;

	private static Map SYMBOL_MAP;

	private static Map SYMBOL_BOOLEAN_MAP;

	private static Map FUNCTION_MAP;

	private static Map FUNCTION_BOOLEAN_MAP;

	static class ArcTanFunction implements IComplex1Function {
		public Complex evaluate(Complex arg1) {
			return arg1.atan();// ComplexUtils.atan(arg1);
		}

	}

	static class LogFunction implements IComplex1Function, IComplex2Function {
		public Complex evaluate(Complex arg1) {
			return arg1.log();// ComplexUtils.log(arg1);
		}

		public Complex evaluate(Complex base, Complex z) {
			return z.log().divide(base.log());// ComplexUtils.log(z).divide(ComplexUtils.log(base));
		}
	}

	static class CompoundExpressionFunction implements IComplexFunction {
		public Complex evaluate(IEvaluator engine, FunctionNode function) {
			Complex result = null;
			int end = function.size();
			for (int i = 1; i < end; i++) {
				result = engine.evaluateNode(function.getNode(i));
				if (DEBUG) {
					System.out.println(result);
				}
			}
			return result;
		}
	}

	static class SetFunction implements IComplexFunction {
		public Complex evaluate(IEvaluator engine, FunctionNode function) {
			if (function.size() != 3) {
				throw new ArithmeticMathException("SetFunction#evaluate(DoubleEvaluator,FunctionNode) needs 2 arguments: "
						+ function.toString());
			}
			if (!(function.getNode(1) instanceof SymbolNode)) {
				throw new ArithmeticMathException("SetFunction#evaluate(DoubleEvaluator,FunctionNode) symbol required on the left hand side: "
						+ function.toString());
			}
			String variableName = ((SymbolNode) function.getNode(1)).getString();
			Complex result = engine.evaluateNode(function.getNode(2));
			ComplexVariable dv = engine.getVariable(variableName);
			if (dv == null) {
				dv = new ComplexVariable(result);
			} else {
				dv.setValue(result);
			}
			engine.defineVariable(variableName, dv);
			return result;
		}
	}

	static class PlusFunction implements IComplexFunction, IComplex2Function {
		public Complex evaluate(Complex arg1, Complex arg2) {
			return arg1.add(arg2);
		}

		public Complex evaluate(IEvaluator engine, FunctionNode function) {
			Complex result = new Complex(0.0, 0.0);
			for (int i = 1; i < function.size(); i++) {
				result = result.add(engine.evaluateNode(function.getNode(i)));
			}
			return result;
		}
	}

	static class TimesFunction implements IComplexFunction, IComplex2Function {
		public Complex evaluate(Complex arg1, Complex arg2) {
			return arg1.multiply(arg2);
		}

		public Complex evaluate(IEvaluator engine, FunctionNode function) {
			Complex result = new Complex(1.0, 0.0);
			for (int i = 1; i < function.size(); i++) {
				result = result.multiply(engine.evaluateNode(function.getNode(i)));
			}
			return result;
		}
	}

	static {
		SYMBOL_MAP = new ConcurrentHashMap();
		SYMBOL_MAP.put("Catalan", new Complex(0.91596559417721901505460351493238411077414937428167, 0.0));
		SYMBOL_MAP.put("Degree", new Complex(Math.PI / 180, 0.0));
		SYMBOL_MAP.put("E", new Complex(Math.E, 0.0));
		SYMBOL_MAP.put("I", new Complex(0.0, 1.0));
		SYMBOL_MAP.put("Pi", new Complex(Math.PI, 0.0));
		SYMBOL_MAP.put("EulerGamma", new Complex(0.57721566490153286060651209008240243104215933593992, 0.0));
		SYMBOL_MAP.put("Glaisher", new Complex(1.2824271291006226368753425688697917277676889273250, 0.0));
		SYMBOL_MAP.put("GoldenRatio", new Complex(1.6180339887498948482045868343656381177203091798058, 0.0));
		SYMBOL_MAP.put("Khinchin", new Complex(2.6854520010653064453097148354817956938203822939945, 0.0));

		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() {
			public boolean evaluate(boolean arg1, boolean arg2) {
				return arg1 && arg2;
			}
		});
		FUNCTION_BOOLEAN_MAP.put("Not", new IBooleanBoolean1Function() {
			public boolean evaluate(boolean arg1) {
				return !arg1;
			}
		});
		FUNCTION_BOOLEAN_MAP.put("Or", new IBooleanBoolean2Function() {
			public boolean evaluate(boolean arg1, boolean arg2) {
				return arg1 || arg2;
			}
		});

		FUNCTION_BOOLEAN_MAP.put("Equal", new IBooleanComplex2Function() {
			public boolean evaluate(Complex arg1, Complex arg2) {
				return arg1.equals(arg2);
			}
		});

		FUNCTION_BOOLEAN_MAP.put("Unequal", new IBooleanComplex2Function() {
			public boolean evaluate(Complex arg1, Complex arg2) {
				return !arg1.equals(arg2);
			}
		});

		FUNCTION_MAP = new ConcurrentHashMap();
		FUNCTION_MAP.put("ArcTan", new ArcTanFunction());
		FUNCTION_MAP.put("Log", new LogFunction());
		FUNCTION_MAP.put("CompoundExpression", new CompoundExpressionFunction());
		FUNCTION_MAP.put("Set", new SetFunction());
		FUNCTION_MAP.put("Plus", new PlusFunction());
		FUNCTION_MAP.put("Times", new TimesFunction());
		//
		// Functions with 0 argument
		//
		FUNCTION_MAP.put("Random", new IComplex0Function() {
			public Complex evaluate() {
				return new Complex(Math.random(), Math.random());
			}
		});
		//
		// Functions with 1 argument
		//
		FUNCTION_MAP.put("ArcCos", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.acos();// acos(arg1);
			}
		});
		FUNCTION_MAP.put("ArcSin", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.asin();// asin(arg1);
			}
		});

		FUNCTION_MAP.put("Cos", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.cos();// cos(arg1);
			}
		});
		FUNCTION_MAP.put("Cosh", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.cosh();// cosh(arg1);
			}
		});
		FUNCTION_MAP.put("Exp", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.exp();// exp(arg1);
			}
		});
		FUNCTION_MAP.put("Sin", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.sin();// sin(arg1);
			}
		});
		FUNCTION_MAP.put("Sinh", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.sinh();// sinh(arg1);
			}
		});
		FUNCTION_MAP.put("Sqrt", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.sqrt();// sqrt(arg1);
			}
		});
		FUNCTION_MAP.put("Tan", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.tan();// tan(arg1);
			}
		});
		FUNCTION_MAP.put("Tanh", new IComplex1Function() {
			public Complex evaluate(Complex arg1) {
				return arg1.tanh();// tanh(arg1);
			}
		});

		//
		// Functions with 2 arguments
		//
		FUNCTION_MAP.put("Power", new IComplex2Function() {
			public Complex evaluate(Complex arg1, Complex arg2) {
				return arg1.pow(arg2);// pow(arg1, arg2);
			}
		});
	}

	private Map fVariableMap;

	private Map fBooleanVariables;

	private final boolean fRelaxedSyntax;

	public ComplexEvalVisitor(boolean relaxedSyntax) {
		fVariableMap = new HashMap();
		fBooleanVariables = new HashMap();
		fRelaxedSyntax = relaxedSyntax;
		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 Complex getResult() {
		return null;
	}

	public void setUp(Complex data) {
	}

	public void tearDown() {
	}

	public Complex visit(ComplexNode node) {
		return node.complexValue();
	}

	public Complex visit(DoubleNode node) {
		return new Complex(node.doubleValue(), 0.0);
	}

	public Complex visit(FloatNode node) {
		return new Complex(node.doubleValue(), 0.0);
	}

	public Complex visit(FractionNode node) {
		return new Complex(node.doubleValue(), 0.0);
	}

	public Complex visit(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 IComplexFunction) {
					return ((IComplexFunction) obj).evaluate(this, functionNode);
				}
				if (functionNode.size() == 1) {
					if (obj instanceof IComplex0Function) {
						return ((IComplex0Function) obj).evaluate();
					}
				} else if (functionNode.size() == 2) {
					if (obj instanceof IComplex1Function) {
						return ((IComplex1Function) obj).evaluate(evaluateNode(functionNode.getNode(1)));
					}
				} else if (functionNode.size() == 3) {
					if (obj instanceof IComplex2Function) {
						return ((IComplex2Function) obj).evaluate(evaluateNode(functionNode.getNode(1)), evaluateNode(functionNode.getNode(2)));
					}
				}
			}
		}
		throw new MathException("EvalDouble#evaluateFunction(FunctionNode) not possible for: " + functionNode.toString());

	}

	public Complex visit(IntegerNode node) {
		return new Complex(node.doubleValue(), 0.0);
	}

	public Complex visit(PatternNode node) {
		return null;
	}

	public Complex visit(StringNode node) {
		return null;
	}

	public Complex visit(SymbolNode node) {
		ComplexVariable v = fVariableMap.get(node.toString());
		if (v != null) {
			return v.getValue();
		}
		Complex c = SYMBOL_MAP.get(node.toString());
		if (c != null) {
			return c;
		}
		throw new MathException("ComplexEvalVisitor#visit(SymbolNode) not possible for: " + node.toString());
	}

	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("EvalDouble#evaluateNodeLogical(ASTNode) not possible for: " + node.toString());
	}

	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 IBooleanComplex2Function) {
					return ((IBooleanComplex2Function) 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 IBooleanDoubleFunction) {
				// return ((IBooleanDoubleFunction) obj).evaluate(this, functionNode);
				// }
			}
		}
		throw new ArithmeticMathException("EvalDouble#evaluateFunctionLogical(FunctionNode) not possible for: " + functionNode.toString());

	}

	/**
	 * Returns a String representation of the given
	 * Complex number.
	 * 
	 * @param c
	 * @return
	 * 
	 */
	// public static String toString(Complex c) {
	// double real = c.getReal();
	// double imag = c.getImaginary();
	// if (imag == 0.0) {
	// return Double.valueOf(real).toString();
	// } else {
	// if (imag >= 0.0) {
	// return Double.valueOf(real).toString() + "+I*" +
	// Double.valueOf(imag).toString();
	// } else {
	// return Double.valueOf(real).toString() + "+I*(" +
	// Double.valueOf(imag).toString() + ")";
	// }
	// }
	// }

	/**
	 * Define a value for a given variable name.
	 * 
	 * @param variableName
	 * @param value
	 */
	public void defineVariable(String variableName, ComplexVariable value) {
		fVariableMap.put(variableName, value);
	}

	/**
	 * Returns the Complex variable value to which the specified variableName is
	 * mapped, or {@code null} if this map contains no mapping for the
	 * variableName.
	 * 
	 * @param variableName
	 * @return
	 */
	public ComplexVariable getVariable(String variableName) {
		return fVariableMap.get(variableName);
	}

	/**
	 * Define a boolean value for a given variable name.
	 * 
	 * @param variableName
	 * @param value
	 */
	public void defineVariable(String variableName, BooleanVariable value) {
		fBooleanVariables.put(variableName, value);
	}

	/**
	 * Clear all defined variables for this evaluator.
	 */
	public void clearVariables() {
		fVariableMap.clear();
	}

	/**
	 * Optimize an already parsed in functionNode into an
	 * ASTNode.
	 * 
	 * @param functionNode
	 * @return
	 * 
	 */
	public ASTNode optimizeFunction(final FunctionNode functionNode) {
		if (functionNode.size() > 0) {
			boolean complexOnly = true;
			ASTNode node;
			for (int i = 1; i < functionNode.size(); i++) {
				node = functionNode.getNode(i);
				if (node instanceof NumberNode) {
					functionNode.set(i, new ComplexNode(((NumberNode) functionNode.getNode(i)).doubleValue()));
				} else if (functionNode.getNode(i) instanceof FunctionNode) {
					ASTNode optNode = optimizeFunction((FunctionNode) functionNode.getNode(i));
					if (!(optNode instanceof ComplexNode)) {
						complexOnly = false;
					}
					functionNode.set(i, optNode);
				} else if (node instanceof SymbolNode) {
					Complex c = SYMBOL_MAP.get(node.toString());
					if (c != null) {
						functionNode.set(i, new ComplexNode(c));
					} else {
						complexOnly = false;
					}
				} else {
					complexOnly = false;
				}
			}
			if (complexOnly) {
				try {
					return new ComplexNode(visit(functionNode));
				} catch (Exception e) {

				}
			}
		}
		return functionNode;
	}
}

 

Ads help maintain this website.