package org.matheclipse.core.eval;

import java.io.PrintStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.matheclipse.core.basic.Config;
import org.matheclipse.core.convert.AST2Expr;
import org.matheclipse.core.eval.exception.IterationLimitExceeded;
import org.matheclipse.core.eval.exception.RecursionLimitExceeded;
import org.matheclipse.core.eval.interfaces.ICoreFunctionEvaluator;
import org.matheclipse.core.eval.interfaces.IFunctionEvaluator;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.expression.MethodSymbol;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IEvaluationEngine;
import org.matheclipse.core.interfaces.IEvaluator;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.IPatternObject;
import org.matheclipse.core.interfaces.ISymbol;
import org.matheclipse.core.list.algorithms.EvaluationSupport;
import org.matheclipse.parser.client.Parser;
import org.matheclipse.parser.client.ast.ASTNode;
import org.matheclipse.parser.client.math.MathException;

import com.google.common.base.Predicate;

/**
 * 
 */
public class EvalEngine implements Serializable, IEvaluationEngine {
	/**
	 * 
	 */
	private static final long serialVersionUID = 407328682800652434L;

	/**
	 * Evaluate an expression. If evaluation is not possible return the input object.
	 * 
	 * @param expr
	 *            the expression which should be evaluated
	 * @return the evaluated object
	 * @see EvalEngine#evalWithoutNumericReset(IExpr)
	 */
	public static final IExpr eval(final IExpr expr) {
		return (instance.get()).evaluate(expr);
	}

	/**
	 * Evaluate an expression. If evaluation is not possible return the input object.
	 * 
	 * @param expr
	 *            the expression which should be evaluated
	 * @return the evaluated object or null if no evaluation was possible
	 * @see EvalEngine#evalWithoutNumericReset(IExpr)
	 */
	public static final IExpr evalNull(final IExpr expr) {
		return (instance.get()).evaluateNull(expr);
	}

	synchronized public static int getNextAnonymousCounter() {
		return ++fAnonymousCounter;
	}

	synchronized public static String getNextCounter() {
		return Integer.toString(++fAnonymousCounter);
	}

	/**
	 * Get the local variable stack for a given symbol name. If the local variable stack doesn't exist, return null
	 * 
	 * @param symbolName
	 * @return null if the stack doesn't exist
	 */
	final public static Stack localStack(final ISymbol symbol) {
		return get().getLocalVariableStackMap().get(symbol);
	}

	/**
	 * Get the local variable stack for a given symbol name. If the local variable stack doesn't exist, create a new one for the
	 * symbol.
	 * 
	 * @param symbolName
	 * @return
	 */
	public static Stack localStackCreate(final ISymbol symbol) {
		Map> localVariableStackMap = get().getLocalVariableStackMap();
		Stack temp = localVariableStackMap.get(symbol);
		if (temp != null) {
			return temp;
		}
		temp = new Stack();
		localVariableStackMap.put(symbol, temp);
		return temp;
	}

	/**
	 * Set the thread local evaluation engine instance
	 * 
	 * @return
	 */
	public static void set(final EvalEngine engine) {
		instance.set(engine);
	}

	public static IAST threadASTListArgs(final IAST ast) {
		IAST result;
		int listLength = 0;
		final int astSize = ast.size();
		for (int i = 1; i < astSize; i++) {
			if (ast.get(i).isList()) {
				if (listLength == 0) {
					listLength = ((IAST) ast.get(i)).size() - 1;
				} else {
					if (listLength != ((IAST) ast.get(i)).size() - 1) {
						ast.setEvalFlags(IAST.IS_LISTABLE_THREADED);
						return null;
					}
				}
			}
		}
		if ((listLength != 0) && ((result = EvaluationSupport.threadList(ast, listLength, 1)) != null)) {
			result.setEvalFlags(IAST.IS_LISTABLE_THREADED);
			return result;
		}
		ast.setEvalFlags(IAST.IS_LISTABLE_THREADED);
		return null;
	}

	/**
	 * Associate a symbol name in this ThreadLocal with the symbol created in this thread
	 * 
	 * @see ExprFactory.fSymbolMap for global symbol names
	 */
	private Map fUserVariableMap;

	/**
	 * Associate a symbol name with a local variable stack in this thread.
	 * 
	 */
	transient private IdentityHashMap> fLocalVariableStackMap = null;

	/**
	 * If set to true the current thread should stop evaluation;
	 */
	transient volatile boolean fStopRequested;

	transient int fRecursionCounter;

	/**
	 * if true the engine evaluates in "numeric" mode, otherwise the engine evaluates in "symbolic"
	 * mode.
	 */
	transient boolean fNumericMode;

	transient boolean fEvalLHSMode;

	transient String fSessionID;

	/**
	 * If true the engine evaluates in "Trace()" function mode.
	 * 
	 * @see #evalTrace(IExpr, Predicate, IAST)
	 */
	transient boolean fTraceMode;

	transient TraceStack fTraceStack = null;

	transient PrintStream fOutPrintStream = null;

	protected int fRecursionLimit;

	protected int fIterationLimit;

	static int fAnonymousCounter = 0;

	protected boolean fPackageMode = false;

	transient int fModuleCounter = 0;

	private boolean fRelaxedSyntax;

	/**
	 * List for results in Reap[] function.
	 */
	transient IAST reapList = null;

	protected Set fModifiedVariablesList;

	/**
	 * The history list for the Out[] function.
	 * 
	 * @see org.matheclipse.core.reflection.Out
	 */
	transient protected List fOutList = new ArrayList(10);

	/**
	 * Contains the last result ("answer") expression of this evaluation engine or null if no answer is
	 * stored in the evaluation engine.
	 */
	transient protected IExpr fAnswer = null;

	/**
	 * Flag for disabling the appending of expressions to the history list for the Out[] function.
	 * 
	 * @see org.matheclipse.core.reflection.Out
	 */
	transient boolean fOutListDisabled = true;

	/**
	 * If true the engine evaluates in "quiet" mode (i.e. no warning messages are shown during evaluation).
	 * 
	 * @see org.matheclipse.core.builtin.function.Quiet
	 */
	transient boolean fQuietMode = false;

	public final static boolean DEBUG = false;

	transient private static final ThreadLocal instance = new ThreadLocal() {
		private int fID = 1;

		@Override
		public EvalEngine initialValue() {
			if (DEBUG) {
				System.out.println("ThreadLocal" + fID);
			}
			return new EvalEngine("ThreadLocal" + (fID++), 0, System.out, false, true);
		}
	};

	/**
	 * Get the thread local evaluation engine instance
	 * 
	 * @return
	 */
	public static EvalEngine get() {
		return instance.get();
	}

	/**
	 * Removes the current thread's value for the EvalEngine's thread-local variable.
	 * 
	 * @see java.lang.ThreadLocal#remove()
	 */
	public static void remove() {
		instance.remove();
	}

	/**
	 * Public constructor for serialization.
	 * 
	 */
	public EvalEngine() {
		this("", 0, System.out, false, true);
	}

	public EvalEngine(boolean relaxedSyntax) {
		this("", 0, System.out, relaxedSyntax, true);
	}

	public EvalEngine(boolean relaxedSyntax, boolean outListDisabled) {
		this("", 0, System.out, relaxedSyntax, outListDisabled);
	}

	public EvalEngine(final F f, final PrintStream out) {
		this("", -1, -1, out, false, true);
	}

	public EvalEngine(final String sessionID, final int recursionLimit, final int iterationLimit, final PrintStream out,
			boolean relaxedSyntax, boolean outListDisabled) {
		fSessionID = sessionID;
		// fExpressionFactory = f;
		fRecursionLimit = recursionLimit;
		fIterationLimit = iterationLimit;
		fOutPrintStream = out;
		fRelaxedSyntax = relaxedSyntax;
		fOutListDisabled = outListDisabled;
		// fNamespace = fExpressionFactory.getNamespace();

		init();

		// set this EvalEngine to the thread local
		set(this);
	}

	/**
	 * Constructor for an EvaluationEngine
	 * 
	 * @param sessionID
	 *            an ID which uniquely identifies this session
	 * @param recursionLimit
	 *            the maximum allowed recursion limit (if set to zero, no limit will be proofed)
	 * @param out
	 * @param relaxedSyntax
	 * @see javax.servlet.http.HttpSession#getID()
	 */
	public EvalEngine(final String sessionID, final int recursionLimit, final PrintStream out, boolean relaxedSyntax,
			boolean outListDisabled) {
		this(sessionID, recursionLimit, -1, out, relaxedSyntax, outListDisabled);
	}

	public EvalEngine(final String sessionID, final PrintStream out) {
		this(sessionID, -1, -1, out, false, true);
	}

	/**
	 * For every evaluation store the list of modified variables in an internal list.
	 * 
	 * @param arg0
	 * @return
	 */
	public boolean addModifiedVariable(ISymbol arg0) {
		return fModifiedVariablesList.add(arg0);
	}

	/**
	 * Add an expression to the Out[] list. To avoid memory leaks you can disable the appending of expressions to the
	 * output history.
	 * 
	 * @see #setOutListDisabled(boolean)
	 */
	public boolean addOut(IExpr arg0) {
		// remember the last result
		if (arg0 == null) {
			fAnswer = F.Null;
		} else {
			fAnswer = arg0;
		}
		ISymbol ans = F.$s("$ans");
		ans.putDownRule(F.Set, true, ans, fAnswer, false);
		if (fOutListDisabled) {
			return true;
		}
		return fOutList.add(fAnswer);
	}

	public void addRules(IAST ruleList) {
		boolean oldPackageMode = isPackageMode();
		boolean oldTraceMode = isTraceMode();
		try {
			setPackageMode(true);
			setTraceMode(false);
			final int ruleSize = ruleList.size();
			for (int i = 1; i < ruleSize; i++) {
				if (ruleList.get(i) != null) {
					evaluate(ruleList.get(i));
				}
			}
		} finally {
			setPackageMode(oldPackageMode);
			setTraceMode(oldTraceMode);
		}
	}

	public void beginTrace(Predicate matcher, IAST list) {
		setTraceMode(true);
		fTraceStack = new TraceStack(matcher, list);
	}

	public IAST endTrace() {
		setTraceMode(false);
		IAST ast = fTraceStack.getList();
		fTraceStack = null;
		if (ast.size() > 1) {
			return ast.getAST(1);
		}
		return ast;
	}

	/**
	 * Evaluate the arguments of the given ast, taking the attributes HoldFirst, HoldRest into account.
	 * 
	 * @param ast
	 * @param attr
	 * @return
	 */
	private IAST evalArgs(final IAST ast, final int attr) {
		final int astSize = ast.size();
		if (astSize > 1) {
			boolean numericMode = fNumericMode;
			boolean localNumericMode = fNumericMode;
			if (!fNumericMode) {
				for (int i = 1; i < astSize; i++) {
					if (ast.get(i).isNumeric()) {
						localNumericMode = true;
						break;
					}
				}
			}

			IAST resultList = null;
			IExpr evaledExpr;
			if ((ISymbol.HOLDFIRST & attr) == ISymbol.NOATTRIBUTE) {
				// the HoldFirst attribute isn't set here
				try {
					if ((ISymbol.NHOLDFIRST & attr) == ISymbol.NHOLDFIRST) {
						fNumericMode = false;
					} else {
						fNumericMode = localNumericMode;
					}
					if ((evaledExpr = evalLoop(ast.get(1))) != null) {
						resultList = ast.clone();
						resultList.setEvalFlags(ast.getEvalFlags() & IAST.IS_MATRIX_OR_VECTOR);
						resultList.set(1, evaledExpr);
						if (astSize == 2) {
							return resultList;
						}
					}
				} finally {
					if ((ISymbol.NHOLDFIRST & attr) == ISymbol.NHOLDFIRST) {
						fNumericMode = numericMode;
					}
				}
			}
			if ((ISymbol.HOLDREST & attr) == ISymbol.NOATTRIBUTE) {
				// the HoldRest attribute isn't set here
				numericMode = fNumericMode;
				try {
					if ((ISymbol.NHOLDREST & attr) == ISymbol.NHOLDREST) {
						fNumericMode = false;
					} else {
						fNumericMode = localNumericMode;
					}
					for (int i = 2; i < astSize; i++) {
						if ((evaledExpr = evalLoop(ast.get(i))) != null) {
							if (resultList == null) {
								resultList = ast.clone();
								resultList.setEvalFlags(ast.getEvalFlags() & IAST.IS_MATRIX_OR_VECTOR);
							}
							resultList.set(i, evaledExpr);
						}
					}
				} finally {
					if ((ISymbol.NHOLDREST & attr) == ISymbol.NHOLDREST) {
						fNumericMode = numericMode;
					}
				}
			}
			if (resultList != null) {
				return resultList;
			}
		}
		return null;
	}

	/**
	 * Evaluate an AST. The evaluation steps are controlled by the header attributes.
	 * 
	 * @param ast
	 * @return
	 */
	public IExpr evalAST(IAST ast) {
		IExpr head = ast.head();
		ISymbol symbol = null;
		if (head instanceof ISymbol) {
			symbol = (ISymbol) head;
			final IEvaluator module = symbol.getEvaluator();
			if (module instanceof ICoreFunctionEvaluator) {
				// evaluate a built-in function.
				if (fNumericMode) {
					return ((ICoreFunctionEvaluator) module).numericEval(ast);
				}
				return ((ICoreFunctionEvaluator) module).evaluate(ast);
			}

		} else {
			symbol = ast.topHead();
		}

		final int astSize = ast.size();
		if (astSize == 2) {
			return evalASTArg1(ast);
		}

		// first evaluate the header !
		IExpr result = evalLoop(head);
		if (result != null) {
			IAST resultList = ast.clone();
			resultList.set(0, result);
			return resultList;
		}

		if (astSize != 1) {
			final int attr = symbol.getAttributes();

			if ((result = flattenSequences(ast)) != null) {
				return result;
			}

			// ONEIDENTITY is checked in the evalASTArg1() method!

			IAST flattened = null;
			if ((ISymbol.FLAT & attr) == ISymbol.FLAT) {
				// associative symbol
				if ((flattened = EvaluationSupport.flatten(ast)) != null) {
					IAST resultList = evalArgs(flattened, attr);
					if (resultList != null) {
						return resultList;
					}
					return flattened;
				}
			}

			IAST resultList = evalArgs(ast, attr);
			if (resultList != null) {
				return resultList;
			}

			if ((ISymbol.LISTABLE & attr) == ISymbol.LISTABLE
					&& !((ast.getEvalFlags() & IAST.IS_LISTABLE_THREADED) == IAST.IS_LISTABLE_THREADED)) {
				// thread over the lists
				resultList = threadASTListArgs(ast);
				if (resultList != null) {
					return resultList;
				}
			}

			if (astSize > 2 && (ISymbol.ORDERLESS & attr) == ISymbol.ORDERLESS) {
				// commutative symbol
				EvaluationSupport.sort(ast);
			}
		}

		for (int i = 1; i < ast.size(); i++) {
			if (!(ast.get(i) instanceof IPatternObject)) {
				final IExpr arg = ast.get(i);
				ISymbol lhsSymbol = null;
				if (arg.isSymbol()) {
					lhsSymbol = (ISymbol) arg;
				} else {
					lhsSymbol = arg.topHead();
				}
				if ((result = lhsSymbol.evalUpRule(this, ast)) != null) {
					return result;
				}
			}
		}

		return evalASTBuiltinFunction(symbol, ast);

	}

	/**
	 * Evaluate an AST with only one argument (i.e. head[arg1]). The evaluation steps are controlled by the header
	 * attributes.
	 * 
	 * @param ast
	 * @return
	 */
	private IExpr evalASTArg1(final IAST ast) {
		// special case ast.size() == 2
		// head == ast[0] --- arg1 == ast[1]
		IExpr result;
		if ((result = evalLoop(ast.head())) != null) {
			// first evaluate the header !
			IAST resultList = ast.clone();
			resultList.set(0, result);
			return resultList;
		}

		final ISymbol symbol = ast.topHead();
		final int attr = symbol.getAttributes();

		if ((result = flattenSequences(ast)) != null) {
			return result;
		}

		if ((ISymbol.ONEIDENTITY & attr) == ISymbol.ONEIDENTITY) {
			return ast.get(1);
		}

		if ((ISymbol.FLAT & attr) == ISymbol.FLAT) {
			final IExpr arg1 = ast.get(1);
			if (arg1.topHead().equals(symbol)) {
				// associative
				return arg1;
			}
		}

		if ((result = evalArgs(ast, attr)) != null) {
			return result;
		}

		if ((ISymbol.LISTABLE & attr) == ISymbol.LISTABLE) {
			final IExpr arg1 = ast.get(1);
			if (arg1.isList()) {
				// thread over the list
				if ((result = EvaluationSupport.threadList(ast, ((IAST) arg1).size() - 1, 1)) != null) {
					return result;
				}
			}
		}

		if (!(ast.get(1) instanceof IPatternObject)) {
			final IExpr arg1 = ast.get(1);
			ISymbol lhsSymbol = null;
			if (arg1.isSymbol()) {
				lhsSymbol = (ISymbol) arg1;
			} else {
				lhsSymbol = arg1.topHead();
			}
			if ((result = lhsSymbol.evalUpRule(this, ast)) != null) {
				return result;
			}
		}

		return evalASTBuiltinFunction(symbol, ast);
	}

	/**
	 * 
	 * @param symbol
	 * @param ast
	 * @return
	 */
	private IExpr evalASTBuiltinFunction(final ISymbol symbol, final IAST ast) {

		if (fEvalLHSMode) {
			final int attr = symbol.getAttributes();
			if ((ISymbol.HOLDALL & attr) == ISymbol.HOLDALL) {
				// check for Set or SetDelayed necessary, because of dynamic
				// evaluation then initializing rules for predefined symbols
				// (i.e. Sin,
				// Cos,...)
				if (!(symbol.equals(F.Set) || symbol.equals(F.SetDelayed) || symbol.equals(F.UpSet) || symbol
						.equals(F.UpSetDelayed))) {
					return null;
				}
			} else {
				if ((ISymbol.NUMERICFUNCTION & attr) != ISymbol.NUMERICFUNCTION) {
					return null;
				}
			}
		}

		if (!symbol.equals(F.Integrate)) {
			IExpr result;
			if ((result = symbol.evalDownRule(this, ast)) != null) {
				return result;
			}
		}

		if (symbol instanceof MethodSymbol) {
			return ((MethodSymbol) symbol).invoke(ast);
		} else {
			final IEvaluator module = symbol.getEvaluator();
			if (module instanceof IFunctionEvaluator) {
				// evaluate a built-in function.
				if (fNumericMode) {
					return ((IFunctionEvaluator) module).numericEval(ast);
				}
				return ((IFunctionEvaluator) module).evaluate(ast);
			}
		}
		return null;
	}

	/**
	 * Evaluate an object, if evaluation is not possible return null.
	 * 
	 * @param expr
	 *            the expression which should be evaluated
	 * @return the evaluated expression or null is evasluation isn't possible
	 * @see EvalEngine#evalWithoutNumericReset(IExpr)
	 */
	public IExpr evalLoop(final IExpr expr) {
		if ((fRecursionLimit > 0) && (fRecursionCounter > fRecursionLimit)) {
			if (Config.DEBUG) {
				System.out.println(expr.toString());
			}
			RecursionLimitExceeded.throwIt(fRecursionLimit, expr);
		}

		try {
			fRecursionCounter++;
			if (fTraceMode) {
				fTraceStack.pushList();
			}
			IExpr temp = expr.evaluate(this);
			if (temp != null) {
				// if (temp == F.Null&&!expr.isAST(F.SetDelayed)) {
				// System.out.println(expr.toString());
				// }
				// if (expr.isAST(F.Integrate)) {
				// System.out.println(expr.toString());
				// System.out.println(" --> " + temp.toString());
				// }
				if (fTraceMode) {
					fTraceStack.addIfEmpty(expr);
					fTraceStack.add(temp);
				}
				IExpr result = temp;
				int iterationCounter = 1;
				do {
					temp = result.evaluate(this);
					if (temp != null) {
						// if (temp == F.Null&&!result.isAST(F.SetDelayed)) {
						// System.out.println(expr.toString());
						// }
						// if (result.isAST(F.Integrate)) {
						// System.out.println(result.toString());
						// System.out.println(" --> " + temp.toString());
						// }
						if (fTraceMode) {
							fTraceStack.add(temp);
						}
						result = temp;
						if (fIterationLimit >= 0 && fIterationLimit <= ++iterationCounter) {
							IterationLimitExceeded.throwIt(iterationCounter, result);
						}
					}
				} while (temp != null);
				return result;
			}
			return null;
		} finally {
			fRecursionCounter--;
			if (fTraceMode) {
				fTraceStack.popList();
			}
		}
	}

	/**
	 * Transform the ast recursively, according to the attributes Flat, HoldAll, HoldFirst, HoldRest, Orderless for the
	 * left-hand-side of a Set[] or SetDelayed[] expression
	 * 
	 * @param ast
	 * @return
	 */
	public IAST evalSetAttributes(IAST ast) {
		boolean evalLHSMode = fEvalLHSMode;
		try {
			fEvalLHSMode = true;
			if ((ast.getEvalFlags() & IAST.IS_FLATTENED_OR_SORTED_MASK) != 0x0000) {
				// already flattened or sorted
				return ast;
			}
			final ISymbol symbol = ast.topHead();
			final int attr = symbol.getAttributes();
			// final Predicate isPattern = Predicates.isPattern();

			IAST resultList = ast;
			IAST result;

			if ((ISymbol.HOLDALL & attr) != ISymbol.HOLDALL) {
				final int astSize = ast.size();
				resultList = ast.clone();
				if ((ISymbol.HOLDFIRST & attr) == ISymbol.NOATTRIBUTE) {
					// the HoldFirst attribute isn't set here
					if (astSize > 1 && ast.get(1).isAST()) {
						IAST temp = (IAST) ast.get(1);
						resultList.set(1, evaluate(temp));
					}
				}
				if ((ISymbol.HOLDREST & attr) == ISymbol.NOATTRIBUTE) {
					// the HoldRest attribute isn't set here
					for (int i = 2; i < astSize; i++) {
						if (ast.get(i).isAST()) {
							IAST temp = (IAST) ast.get(i);
							resultList.set(i, evaluate(temp));
						}
					}
				}

			}
			if (resultList.size() > 2) {
				if ((ISymbol.FLAT & attr) == ISymbol.FLAT) {
					// associative
					if ((result = EvaluationSupport.flatten(resultList)) != null) {
						resultList = result;
					}
				}
				if ((ISymbol.ORDERLESS & attr) == ISymbol.ORDERLESS) {
					EvaluationSupport.sort(resultList);
				}
			}
			return resultList;
		} finally {
			fEvalLHSMode = evalLHSMode;
		}
	}

	/**
	 * Evaluate the expression and return the Trace[expr] (i.e. all (sub-)expressions needed to calculate the result).
	 * 
	 * @param expr
	 *            the expression which should be evaluated.
	 * @param matcher
	 *            a filter which determines the expressions which should be traced, If the matcher is set to null, all
	 *            expressions are traced.
	 * @param list
	 *            an IAST object which will be cloned for containing the traced expressions. Typically a F.List() will
	 *            be used.
	 * @return
	 */
	public final IAST evalTrace(final IExpr expr, Predicate matcher, IAST list) {
		IAST traceList = F.List();
		try {
			beginTrace(matcher, list);
			evaluate(expr);
		} finally {
			traceList = endTrace();
		}
		return traceList;
	}

	/**
	 * Test if expr could be evaluated to True. If a
	 * org.matheclipse.parser.client.math.MathException occurs during evaluation, return False.
	 * 
	 * @param expr
	 * @return
	 */
	public final boolean evalTrue(final IExpr expr) {
		try {
			return evaluate(expr).equals(F.True);
		} catch (MathException fce) {
			if (Config.DEBUG) {
				fce.printStackTrace();
			}
			return false;
		}
	}

	/**
	 * Store the current numeric mode and evaluate the expression expr. After evaluation reset the numeric mode to the
	 * value stored before the evaluation starts. If evaluation is not possible return the input object.
	 * 
	 * @param expr
	 *            the object which should be evaluated
	 * @return the evaluated object
	 */
	public final IExpr evaluate(final IExpr expr) {
		boolean numericMode = fNumericMode;
		// StackContext.enter();
		try {
			return evalWithoutNumericReset(expr);
			// if (fTraceMode) {
			// fTraceList = StackContext.outerCopy(fTraceList);
			// }
			// return StackContext.outerCopy(temp);
		} finally {
			fNumericMode = numericMode;
			// StackContext.exit();
		}
	}

	/**
	 * Parse the given expression String into an IExpr and evaluate it.
	 * 
	 * @param astString
	 *            an expression in math formula notation
	 * @return
	 * @throws org.matheclipse.parser.client.SyntaxError
	 *             if a parsing error occurs
	 */
	final public IExpr evaluate(String expression) {
		return evaluate(parse(expression));
	}

	/**
	 * 
	 * Evaluate an object and reset the numeric mode to the value before the evaluation step. If evaluation is not possible return
	 * null.
	 * 
	 * @param expr
	 *            the object which should be evaluated
	 * @return the evaluated object of null if no evaluation was possible
	 */
	public final IExpr evaluateNull(final IExpr expr) {
		boolean numericMode = fNumericMode;
		try {
			return evalLoop(expr);
		} finally {
			fNumericMode = numericMode;
		}
	}

	/**
	 * Evaluate an object without resetting the numeric mode after the evaluation step. If evaluation is not possible return the
	 * input object,
	 * 
	 * @param expr
	 *            the object which should be evaluated
	 * @return the evaluated object
	 * 
	 */
	@Override
	public final IExpr evalWithoutNumericReset(final IExpr expr) {
		IExpr temp = evalLoop(expr);
		return temp == null ? expr : temp;
	}

	private IAST flattenSequences(final IAST ast) {
		IAST seqResult = null;
		final int astSize = ast.size();
		for (int i = 1; i < astSize; i++) {
			if (ast.get(i).isSequence()) {
				IAST seq = (IAST) ast.get(i);
				if (seqResult == null) {
					seqResult = ast.copyUntil(i);
				}
				seqResult.addAll(seq, 1, seq.size());
			} else if (seqResult != null) {
				seqResult.add(ast.get(i));
			}
		}
		return seqResult;
	}

	/**
	 * Get the last result ("answer") expression of this evaluation engine.
	 * 
	 * @return null if no answer is stored in the evaluation engine.
	 */
	public IExpr getAnswer() {
		return fAnswer;
	}

	public int getIterationLimit() {
		return fIterationLimit;
	}

	final public Map> getLocalVariableStackMap() {
		if (fLocalVariableStackMap == null) {
			fLocalVariableStackMap = new IdentityHashMap>();
		}
		return fLocalVariableStackMap;
	}

	/**
	 * Get the list of modified variables
	 * 
	 * @return
	 */
	public Set getModifiedVariables() {
		return fModifiedVariablesList;
	}

	/**
	 * Get the expression of the Out[] list at the given index
	 * 
	 * @param index
	 * @return
	 */
	public IExpr getOut(int index) {
		return fOutList.get(index);
	}

	public List getOutList() {
		return fOutList;
	}

	public PrintStream getOutPrintStream() {
		return fOutPrintStream;
	}

	/**
	 * @return the reapList
	 */
	public IAST getReapList() {
		return reapList;
	}

	/**
	 * @return
	 */
	public int getRecursionLimit() {
		return fRecursionLimit;
	}

	/**
	 * @return
	 */
	public String getSessionID() {
		return fSessionID;
	}

	/**
	 * Returns the ISymbol variable created in this thread to which the specified symbolName is mapped, or
	 * null if this map contains no mapping for the symbolName.
	 * 
	 * @param name
	 * @return
	 */
	public ISymbol getUserVariable(final String symbolName) {
		if (fUserVariableMap == null) {
			fUserVariableMap = new HashMap();
		}
		return fUserVariableMap.get(symbolName);
	}

	/**
	 * Associates the symbolName key with the ISymbol value.
	 * 
	 * @param symbolName
	 * @param symbol
	 * @return
	 */
	public ISymbol putUserVariable(final String symbolName, final ISymbol symbol) {
		if (fUserVariableMap == null) {
			fUserVariableMap = new HashMap();
		}
		return fUserVariableMap.put(symbolName, symbol);
	}

	/**
	 * Remove all moduleVariables from this evaluation engine.
	 * 
	 * @param moduleVariables
	 */
	public void removeUserVariables(final Map moduleVariables) {
		// remove all module variables from eval engine
		if (fUserVariableMap != null) {
			for (ISymbol symbol : moduleVariables.values()) {
				fUserVariableMap.remove(symbol.toString());
			}
		}
	}

	/**
	 * Increment the module counter by 1 and return the result.
	 * 
	 * @return the module counter
	 */
	public int incModuleCounter() {
		return ++fModuleCounter;
	}

	@Override
	final public void init() {
		fRecursionCounter = 0;
		fNumericMode = false;
		fEvalLHSMode = false;
		fTraceMode = false;
		fTraceStack = null;
		// fTraceList = null;
		fStopRequested = false;
		fModifiedVariablesList = new HashSet();
	}

	/**
	 * The engine evaluates the left-hand-side of a Set, SetDelayed,... expression.
	 * 
	 * @return
	 */
	public boolean isEvalLHSMode() {
		return fEvalLHSMode;
	}

	/**
	 * @return
	 */
	public boolean isNumericMode() {
		return fNumericMode;
	}

	public boolean isOutListDisabled() {
		return fOutListDisabled;
	}

	public boolean isPackageMode() {
		return fPackageMode;
	}

	/**
	 * If true the engine evaluates in "quiet" mode (i.e. no warning messages are showw in the evaluation).
	 * 
	 * @see org.matheclipse.core.builtin.function.Quiet
	 */
	public boolean isQuietMode() {
		return fQuietMode;
	}

	/**
	 * @return the fRelaxedSyntax
	 */
	public boolean isRelaxedSyntax() {
		return fRelaxedSyntax;
	}

	/**
	 * @return Returns the stopRequested.
	 */
	public boolean isStopRequested() {
		return fStopRequested;
	}

	/**
	 * If the trace mode is set the system writes an evaluation trace list or if additionally the stop after evaluation mode
	 * is set returns the first evaluated result.
	 * 
	 * @return
	 * @see org.matheclipse.core.reflection.system.Trace
	 */
	public boolean isTraceMode() {
		return fTraceMode;
	}

	/**
	 * Parse the given expression String into an IExpr without evaluation.
	 * 
	 * @param astString
	 *            an expression in math formula notation
	 * @return
	 * @throws org.matheclipse.parser.client.SyntaxError
	 *             if a parsing error occurs
	 */
	final public IExpr parse(String expression) {
		if (fRelaxedSyntax) {
			final Parser parser = new Parser(fRelaxedSyntax);
			final ASTNode node = parser.parse(expression);
			return AST2Expr.CONST_LC.convert(node);
		} else {
			final Parser parser = new Parser();
			final ASTNode node = parser.parse(expression);
			return AST2Expr.CONST.convert(node);
		}
	}

	/**
	 * Parse the given expression String into an ASTNode without evaluation.
	 * 
	 * @param astString
	 *            an expression in math formula notation
	 * @return
	 * @throws org.matheclipse.parser.client.SyntaxError
	 *             if a parsing error occurs
	 */
	final public ASTNode parseNode(String expression) {
		if (fRelaxedSyntax) {
			final Parser parser = new Parser(fRelaxedSyntax);
			return parser.parse(expression);
		} else {
			final Parser parser = new Parser();
			return parser.parse(expression);
		}
	}

	/**
	 * Reset the numeric mode flag and the recursion counter
	 * 
	 */
	public void reset() {
		fNumericMode = false;
		fEvalLHSMode = false;
		fRecursionCounter = 0;
	}

	public void setIterationLimit(final int i) {
		fIterationLimit = i;
	}

	/**
	 * @param b
	 */
	public void setNumericMode(final boolean b) {
		fNumericMode = b;
	}

	public void setOutListDisabled(boolean outListDisabled) {
		this.fOutListDisabled = outListDisabled;
	}

	public void setOutPrintStream(final PrintStream outPrintStream) {
		fOutPrintStream = outPrintStream;
	}

	public void setPackageMode(boolean packageMode) {
		fPackageMode = packageMode;
	}

	/**
	 * If true the engine evaluates in "quiet" mode (i.e. no warning messages are showw in the evaluation).
	 * 
	 * @param quietMode
	 */
	public void setQuietMode(boolean quietMode) {
		this.fQuietMode = quietMode;
	}

	/**
	 * @param reapList
	 *            the reapList to set
	 */
	public void setReapList(IAST reapList) {
		this.reapList = reapList;
	}

	/**
	 * @param i
	 */
	public void setRecursionLimit(final int i) {
		fRecursionLimit = i;
	}

	/**
	 * @param fRelaxedSyntax
	 *            the fRelaxedSyntax to set
	 */
	public void setRelaxedSyntax(boolean fRelaxedSyntax) {
		this.fRelaxedSyntax = fRelaxedSyntax;
	}

	/**
	 * @param string
	 */
	public void setSessionID(final String string) {
		fSessionID = string;
	}

	/**
	 * @param stopRequested
	 *            The stopRequested to set.
	 */
	public void setStopRequested(final boolean stopRequested) {
		fStopRequested = stopRequested;
	}

	/**
	 * @param b
	 */
	public void setTraceMode(final boolean b) {
		fTraceMode = b;
	}

	/**
	 * The size of the Out[] list
	 * 
	 * @return
	 */
	public int sizeOut() {
		return fOutList.size();
	}

	public void stopRequest() {
		fStopRequested = true;
	}

	@Override
	public String toString() {
		StringBuffer buf = new StringBuffer();
		if (fUserVariableMap != null) {
			buf.append(fUserVariableMap.toString());
		}
		if (fLocalVariableStackMap != null) {
			buf.append(fLocalVariableStackMap.toString());
		}
		if (SystemNamespace.DEFAULT != null) {
			buf.append(SystemNamespace.DEFAULT.toString());
		}
		return buf.toString();
	}
}
 

Ads help maintain this website.