package org.matheclipse.core.patternmatching;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.matheclipse.combinatoric.MultisetPartitionsIterator;
import org.matheclipse.combinatoric.NumberPartitionsIterator;
import org.matheclipse.core.eval.EvalEngine;
import org.matheclipse.core.eval.exception.ConditionException;
import org.matheclipse.core.eval.exception.ReturnException;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.IPattern;
import org.matheclipse.core.interfaces.IPatternObject;
import org.matheclipse.core.interfaces.IPatternSequence;
import org.matheclipse.core.interfaces.ISymbol;

public class PatternMatcher extends IPatternMatcher implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -6708462090303928690L;

	private static class Entry {
		IExpr fPatternExpr;
		IExpr fEvalExpr;

		public Entry(IExpr patternExpr, IExpr evalExpr) {
			this.fPatternExpr = patternExpr;
			this.fEvalExpr = evalExpr;
		}
	}

	/**
	 * Manage a stack of pairs of expressions, which have to match each other
	 * 
	 */
	protected class StackMatcher {

		private ArrayList fStack = new ArrayList();

		public StackMatcher() {

		}

		/**
		 * @return
		 * @see java.util.Stack#peek()
		 */
		public Entry peek() {
			return fStack.get(fStack.size() - 1);
		}

		/**
		 * @return
		 * @see java.util.Stack#pop()
		 */
		public Entry pop() {
			return fStack.remove(fStack.size() - 1);
		}

		/**
		 * Remove all elements starting at the given fromPosition.
		 * 
		 * @param fromPosition
		 */
		public void removeFrom(int fromPosition) {
			int len = fStack.size();
			while (len > fromPosition) {
				fStack.remove(len - 1);
				len--;
			}
		}

		public boolean push(IExpr patternExpr, IExpr evalExpr) {
			if (patternExpr.isPatternExpr()) {
				if (patternExpr.isAST()) {
					// insert for delayed evaluation in matchRest() method
					fStack.add(new Entry(patternExpr, evalExpr));
					return true;
				}
				if (patternExpr.isPattern()) {
					return matchPattern((IPattern) patternExpr, evalExpr);
				} else if (patternExpr.isPatternSequence()) {
					return matchPatternSequence((IPatternSequence) patternExpr, F.Sequence(evalExpr));
				}
				throw new UnsupportedOperationException("Object doesn't support pattern-matching");
			}
			return patternExpr.equals(evalExpr);
		}

		/**
		 * Match the entries of the stack recursively starting from the top entry.
		 * 
		 * @return true if all expressions could be matched.
		 */
		public boolean matchRest() {
			if (isEmpty()) {
				return checkCondition();
			}
			boolean matched = true;
			Entry entry = pop();
			try {
				matched = matchExpr(entry.fPatternExpr, entry.fEvalExpr, this);
				return matched;
			} finally {
				if (!matched) {
					fStack.add(entry);
				}
			}
		}

		/**
		 * Check if this stack is empty.
		 * 
		 * @return
		 * @see java.util.ArrayList#isEmpty()
		 */
		public boolean isEmpty() {
			return fStack.isEmpty();
		}

		/**
		 * The size of the stack.
		 * 
		 * @return
		 * @see java.util.ArrayList#size()
		 */
		public int size() {
			return fStack.size();
		}
	}

	/**
	 * Matches an IAST with header attribute Orderless .
	 * 
	 * @see ISymbol#ORDERLESS
	 */
	public class OrderlessMatcher {

		private IAST fLHSPatternAST;

		private IAST fLHSEvalAST;

		/**
		 * The used (i.e. matched) expression indexes in the LHS evaluation expression; -1 indicates an unused index.
		 */
		private int[] fUsedIndex;

		/**
		 * Match a pattern expression against an evaluation expression, there the arguments are commutative (i.e. the head of the
		 * AST expression has attribute Orderless)
		 * 
		 * @param lhsPatternAST
		 *            the pattern AST
		 * @param lhsEvalAST
		 *            the evaluation AST
		 */
		public OrderlessMatcher(final IAST lhsPatternAST, final IAST lhsEvalAST) {
			this.fLHSPatternAST = lhsPatternAST;
			this.fLHSEvalAST = lhsEvalAST;
			this.fUsedIndex = new int[fLHSPatternAST.size() - 1];
			for (int l = 0; l < fUsedIndex.length; l++) {
				fUsedIndex[l] = -1;
			}
		}

		/**
		 * 
		 * @param lhsPosition
		 *            the position in the LHS expression which should actually be matched.
		 * @param stackMatcher
		 *            TODO
		 * @return
		 */
		public boolean matchOrderlessAST(int lhsPosition, StackMatcher stackMatcher) {
			if (lhsPosition >= fLHSPatternAST.size()) {
				return stackMatcher.matchRest();
			}
			boolean isNotInUse;
			IExpr subPattern = fLHSPatternAST.get(lhsPosition);
			IExpr[] patternValues = fPatternMap.copyPattern();
			for (int j = 1; j < fLHSEvalAST.size(); j++) {
				isNotInUse = true;
				for (int k = 0; k < fLHSPatternAST.size() - 1; k++) {
					if (fUsedIndex[k] == j) {
						isNotInUse = false;
						break;
					}
				}
				if (isNotInUse) {
					boolean matched = false;
					int lastStackSize = stackMatcher.size();
					try {
						if (stackMatcher.push(subPattern, fLHSEvalAST.get(j))) {
							fUsedIndex[lhsPosition - 1] = j;
							if (matchOrderlessAST(lhsPosition + 1, stackMatcher)) {
								matched = true;
								return true;
							}
						}
					} finally {
						if (!matched) {
							fPatternMap.resetPattern(patternValues);
							stackMatcher.removeFrom(lastStackSize);
							fUsedIndex[lhsPosition - 1] = -1;
						}
					}
				}
			}
			return false;
		}

		public void filterResult(IAST result) {
			for (int i = 0; i < fUsedIndex.length; i++) {
				result.set(fUsedIndex[i], null);
			}
			int indx = 1;
			while (indx < result.size()) {
				if (result.get(indx) == null) {
					result.remove(indx);
				} else {
					indx++;
				}
			}
		}
	}

	/**
	 * Additional condition for pattern-matching maybe null
	 * 
	 */
	protected IExpr fPatternCondition;

	/**
	 * A map from a pattern to a possibly found value during pattern-matching. Will be set to null if the
	 * left-hand-side pattern expression contains no pattern.
	 */
	protected PatternMap fPatternMap;

	/**
	 * Needed for serialization
	 * 
	 * @param patternExpr
	 */
	public PatternMatcher() {
		super(null);
		this.fLhsPatternExpr = null;
		this.fPatternCondition = null;
		this.fPatternMap = new PatternMap();
	}

	public PatternMatcher(final IExpr patternExpr) {
		super(patternExpr);
		this.fLhsPatternExpr = patternExpr;
		this.fPatternCondition = null;
		if (patternExpr.isCondition()) {
			this.fLhsPatternExpr = ((IAST) patternExpr).get(1);
			this.fPatternCondition = ((IAST) patternExpr).get(2);
		}
		this.fPatternMap = new PatternMap();
		init(fLhsPatternExpr);
	}

	protected final void init(IExpr patternExpr) {
		fPatternMap.determinePatterns(patternExpr);
	}

	/**
	 * Check if the condition for the right-hand-sides Module[] or Condition[] expressions evaluates to
	 * true. Override it in subclasses.
	 * 
	 * @return true
	 * @see PatternMatcherAndEvaluator#checkRHSCondition(EvalEngine)
	 */
	public boolean checkRHSCondition(EvalEngine engine) {
		return true;
	}

	/**
	 * Check if the condition for this pattern matcher evaluates to true.
	 */
	public boolean checkCondition() {

		if (fPatternCondition != null) {
			final EvalEngine engine = EvalEngine.get();
			boolean traceMode = false;
			try {
				traceMode = engine.isTraceMode();
				engine.setTraceMode(false);
				final IExpr substConditon = fPatternMap.substitutePatternSymbols(fPatternCondition);
				if (engine.evalTrue(substConditon)) {
					return checkRHSCondition(engine);
				}
				return false;
			} finally {
				engine.setTraceMode(traceMode);
			}
		}
		return true;
	}

	/**
	 * Check if the two left-hand-side pattern expressions are equivalent. (i.e. f[x_,y_] is equivalent to
	 * f[a_,b_] )
	 * 
	 * @param patternExpr1
	 * @param patternExpr2
	 * @param pm1
	 * @param pm2
	 * @return
	 */
	public static boolean equivalent(final IExpr patternExpr1, final IExpr patternExpr2) {
		if (!patternExpr1.isPatternExpr()) {
			if (!patternExpr2.isPatternExpr()) {
				return patternExpr1.equals(patternExpr2);
			}
			return false;
		}
		if (patternExpr1.isAST()) {
			if (patternExpr2.isAST()) {
				final IAST l1 = (IAST) patternExpr1;
				final IAST l2 = (IAST) patternExpr2;
				if (l1.size() != l2.size()) {
					return false;
				}
				for (int i = 0; i < l1.size(); i++) {
					if (l1.get(i).hashCode() != l2.get(i).hashCode()) {
						if (l1.get(i).isPatternExpr() && l2.get(i).isPatternExpr()) {
							continue;
						}
						return false;
					}
				}
				for (int i = 0; i < l1.size(); i++) {
					if (!equivalent(l1.get(i), l2.get(i))) {
						return false;
					}
				}
				return true;
			}
			return false;
		}
		if (patternExpr1.isPattern()) {
			if (patternExpr2.isPattern()) {
				// test if the pattern indices are equal
				final IPattern p1 = (IPattern) patternExpr1;
				final IPattern p2 = (IPattern) patternExpr2;
				if (p1.getIndex() != p2.getIndex()) {
					return false;
				}
				// test if the "check" expressions are equal
				final Object o1 = p1.getCondition();
				final Object o2 = p2.getCondition();
				if ((o1 == null) || (o2 == null)) {
					return o1 == o2;
				}
				return o1.equals(o2);
			}
			return false;
		}
		if (patternExpr1.isPatternSequence()) {
			if (patternExpr2.isPatternSequence()) {
				// test if the pattern indices are equal
				final IPatternSequence p1 = (IPatternSequence) patternExpr1;
				final IPatternSequence p2 = (IPatternSequence) patternExpr2;
				if (p1.getIndex() != p2.getIndex()) {
					return false;
				}
				// test if the "check" expressions are equal
				final Object o1 = p1.getCondition();
				final Object o2 = p2.getCondition();
				if ((o1 == null) || (o2 == null)) {
					return o1 == o2;
				}
				return o1.equals(o2);
			}
			return false;
		}
		return patternExpr1.equals(patternExpr2);
	}

	/**
	 * Returns the matched pattern in the order they appear in the pExpr
	 * 
	 * @param resultList
	 * @param pExpr
	 */
	@Override
	public void getPatterns(final List resultList, final IExpr pExpr) {
		if (pExpr.isAST()) {
			final IAST list = (IAST) pExpr;
			getPatterns(resultList, list.head());
			for (int i = 1; i < list.size(); i++) {
				getPatterns(resultList, list.get(i));
			}
		} else if (pExpr.isPattern()) {
			resultList.add(fPatternMap.getValue((IPattern) pExpr));
		}
	}

	/**
	 * Set the symbol values for the matched patterns.
	 * 
	 * @param resultList
	 * @param pExpr
	 */
	// public void setPatternValue2Local(final IExpr pExpr) {
	// if (pExpr.isAST()) {
	// final IAST list = (IAST) pExpr;
	// setPatternValue2Local(list.head());
	// for (int i = 0; i < list.size(); i++) {
	// setPatternValue2Local(list.get(i));
	// }
	// } else if (pExpr.isPattern()) {
	// ISymbol sym = ((IPattern) pExpr).getSymbol();
	// if (!sym.hasLocalVariableStack()) {
	// throw new
	// UnsupportedOperationException("Pattern symbol has to be defined with local stack");
	// }
	// sym.set(fPatternMap.getValue((IPattern) pExpr));
	// }
	// }

	/**
	 * Returns true if the given expression contains no patterns
	 * 
	 * @return
	 */
	@Override
	final public boolean isRuleWithoutPatterns() {
		return fPatternMap.isRuleWithoutPatterns();
	}

	@Override
	public boolean apply(final IExpr leftHandSide) {

		if (isRuleWithoutPatterns()) {
			// no patterns found match equally:
			return fLhsPatternExpr.equals(leftHandSide);
		}

		fPatternMap.initPattern();
		return matchExpr(fLhsPatternExpr, leftHandSide);
	}

	/**
	 * Checks if the two expressions match each other
	 * 
	 * 
	 * @return
	 */
	protected boolean matchExpr(IExpr lhsPatternExpr, final IExpr lhsEvalExpr) {
		return matchExpr(lhsPatternExpr, lhsEvalExpr, new StackMatcher());
	}

	/**
	 * Checks if the two expressions match each other
	 * 
	 * @return
	 */
	protected boolean matchExpr(IExpr lhsPatternExpr, final IExpr lhsEvalExpr, StackMatcher stackMatcher) {
		boolean matched = false;
		if (lhsPatternExpr.isAST()) {
			if (lhsPatternExpr.isCondition()) {
				// expression /; test
				lhsPatternExpr = fPatternMap.substitutePatternSymbols(lhsPatternExpr);
				if (lhsPatternExpr.isAST()) {
					lhsPatternExpr = PatternMatcher.evalLeftHandSide((IAST) lhsPatternExpr);
				}
				final PatternMatcher matcher = new PatternMatcher(lhsPatternExpr);
				if (matcher.apply(lhsEvalExpr)) {
					matched = true;
					fPatternMap.copyPatternValuesFromPatternMatcher(matcher.fPatternMap);
				}
			} else {
				IAST lhsPatternAST = (IAST) lhsPatternExpr;
				IExpr[] patternValues = fPatternMap.copyPattern();
				try {
					matched = matchAST(lhsPatternAST, lhsEvalExpr, stackMatcher);
					if ((lhsPatternAST.getEvalFlags() & IAST.CONTAINS_DEFAULT_PATTERN) == IAST.CONTAINS_DEFAULT_PATTERN) {
						if (!matched) {
							IExpr temp = null;
							ISymbol symbol = lhsPatternAST.topHead();
							int attr = symbol.getAttributes();
							fPatternMap.resetPattern(patternValues);
							temp = matchDefaultAST(symbol, attr, lhsPatternAST);
							if (temp != null) {
								matched = matchExpr(temp, lhsEvalExpr, stackMatcher);
							}
						}
					}
				} finally {
					if (!matched) {
						fPatternMap.resetPattern(patternValues);
					}
				}
			}
		} else if (lhsPatternExpr instanceof IPatternObject) {
			if (lhsPatternExpr.isPattern()) {
				matched = matchPattern((IPattern) lhsPatternExpr, lhsEvalExpr);
			} else if (lhsPatternExpr.isPatternSequence()) {
				matched = matchPatternSequence((IPatternSequence) lhsPatternExpr, F.Sequence(lhsEvalExpr));
			}
		} else {
			matched = lhsPatternExpr.equals(lhsEvalExpr);
		}
		if (matched) {
			return stackMatcher.matchRest();
		}
		return false;

	}

	/**
	 * Match the ast with its Default[] values.
	 * 
	 * @param symbol
	 * @param attr
	 * @param ast
	 * @return
	 */
	private IExpr matchDefaultAST(ISymbol symbol, int attr, IAST ast) {
		IAST cloned = F.ast(ast.head(), ast.size(), false);
		for (int i = 1; i < ast.size(); i++) {
			if (ast.get(i).isPattern() && ((IPattern) ast.get(i)).isDefault()) {
				IExpr positionDefaultValue = symbol.getDefaultValue(i);
				if (positionDefaultValue != null) {
					if (!matchPattern((IPattern) ast.get(i), positionDefaultValue)) {
						return null;
					}
					continue;
				} else {
					IExpr commonDefaultValue = symbol.getDefaultValue();
					if (commonDefaultValue != null) {
						if (!matchPattern((IPattern) ast.get(i), commonDefaultValue)) {
							return null;
						}
						continue;
					}
				}

			}
			cloned.add(ast.get(i));
		}
		if (cloned.size() == 2) {
			return cloned.get(1);
		}
		return null;
	}

	private boolean matchFlatAndFlatOrderlessAST(final ISymbol sym, final IAST lhsPatternAST, final IAST lhsEvalAST,
			StackMatcher stackMatcher) {
		if ((sym.getAttributes() & ISymbol.ORDERLESS) == ISymbol.ORDERLESS) {
			FlatOrderlessStepVisitor visitor = new FlatOrderlessStepVisitor(sym, lhsPatternAST, lhsEvalAST, stackMatcher,
					fPatternMap);
			MultisetPartitionsIterator iter = new MultisetPartitionsIterator(visitor, lhsPatternAST.size() - 1);
			return !iter.execute();
		} else {
			FlatStepVisitor visitor = new FlatStepVisitor(sym, lhsPatternAST, lhsEvalAST, stackMatcher, fPatternMap);
			NumberPartitionsIterator iter = new NumberPartitionsIterator(visitor, lhsEvalAST.size() - 1, lhsPatternAST.size() - 1);
			return !iter.execute();
		}
	}

	protected boolean matchAST(final IAST lhsPatternAST, final IExpr lhsEvalExpr, StackMatcher stackMatcher) {
		if (lhsEvalExpr instanceof IAST) {
			if (!lhsPatternAST.isPatternExpr() && lhsPatternAST.equals(lhsEvalExpr)) {
				return stackMatcher.matchRest();
			}

			final IAST lhsEvalAST = (IAST) lhsEvalExpr;
			final ISymbol sym = lhsPatternAST.topHead();
			if (lhsPatternAST.size() <= lhsEvalAST.size()) {
				if ((lhsPatternAST.isFlatAST()) && sym.equals(lhsEvalAST.topHead())
						&& !(lhsPatternAST.isOrderlessAST() && lhsPatternAST.size() == lhsEvalAST.size())) {
					if (!matchExpr(lhsPatternAST.head(), lhsEvalAST.head())) {
						return false;
					}
					return matchFlatAndFlatOrderlessAST(sym, lhsPatternAST, lhsEvalAST, stackMatcher);
				}

				if (lhsPatternAST.size() < lhsEvalAST.size()) {
					if (lhsPatternAST.isEvalFlagOn(IAST.CONTAINS_PATTERN_SEQUENCE)) {
						if (!matchExpr(lhsPatternAST.head(), lhsEvalAST.head())) {
							return false;
						}
						int lastPosition = lhsPatternAST.size() - 1;
						if (lhsPatternAST.get(lastPosition).isPatternSequence()) {
							// TODO only the special case, where the last element is
							// a pattern sequence, is handled here
							IAST seq = F.Sequence();
							seq.addAll(lhsEvalAST, lastPosition, lhsEvalAST.size());
							if (matchPatternSequence((IPatternSequence) lhsPatternAST.get(lastPosition), seq)) {
								return matchAST(lhsPatternAST.copyUntil(lastPosition), lhsEvalAST.copyUntil(lastPosition),
										stackMatcher);
							}
						}
					}

					return false;
				}
			}

			if (lhsPatternAST.size() != lhsEvalAST.size()) {
				return false;
			}

			IExpr e1 = lhsPatternAST.head();
			IExpr e2 = lhsEvalAST.head();
			if (e1.isSymbol() && e2.isSymbol()) {
				if (!e1.equals(e2)) {
					return false;
				}
			} else {
				// TODO create correct stack-matcher for the following call:
				if (!matchExpr(lhsPatternAST.head(), lhsEvalAST.head())) {
					return false;
				}
			}

			if (lhsPatternAST.isOrderlessAST()) {
				// only pure Orderless things (without Flat) will be handled
				// here:
				// final OrderlessMatcher foMatcher = new
				// OrderlessMatcher(lhsPatternAST, lhsEvalAST);
				// return foMatcher.matchOrderlessAST(1, stackMatcher);
				OrderlessStepVisitor visitor = new OrderlessStepVisitor(sym, lhsPatternAST, lhsEvalAST, stackMatcher, fPatternMap);
				MultisetPartitionsIterator iter = new MultisetPartitionsIterator(visitor, lhsPatternAST.size() - 1);
				return !iter.execute();
			}

			return matchASTSequence(lhsPatternAST, lhsEvalAST, 0, stackMatcher);
		}

		return false;
	}

	/**
	 * Match all sub-expresions which contain no pattern objects if possible (i.e. no FLAT or Orderless expressions,...)
	 * 
	 * Distinguishes between "equally" matched list-expressions and list expressions with expr.isPatternExpr()==true.
	 * 
	 * @param lhsPatternAST
	 * @param lhsEvalAST
	 * @param lhsEvalOffset
	 * @param stackMatcher
	 * @return
	 */
	private boolean matchASTSequence(final IAST lhsPatternAST, final IAST lhsEvalAST, final int lhsEvalOffset,
			StackMatcher stackMatcher) {
		// distinguish between "equally" matched list-expressions and
		// AST expressions with "CONTAINS_PATTERN" flag
		IExpr[] patternValues = fPatternMap.copyPattern();
		int lastStackSize = stackMatcher.size();
		boolean matched = true;
		try {
			// loop from the end
			for (int i = lhsPatternAST.size() - 1; i > 0; i--) {
				if (!stackMatcher.push(lhsPatternAST.get(i), lhsEvalAST.get(lhsEvalOffset + i))) {
					matched = false;
					return false;
				}
			}
			if (!stackMatcher.matchRest()) {
				matched = false;
				return false;
			}
			return true;
		} finally {
			if (!matched) {
				stackMatcher.removeFrom(lastStackSize);
				fPatternMap.resetPattern(patternValues);
			}
		}
	}

	protected IExpr evalAST(final IAST lhsPatternAST, final IAST lhsEvalAST, final IExpr rhsExpr, StackMatcher stackMatcher) {
		if (lhsPatternAST.size() < lhsEvalAST.size()) {
			if (lhsPatternAST.isOrderlessAST()) {
				if (!matchExpr(lhsPatternAST.head(), lhsEvalAST.head(), new StackMatcher())) {
					return null;
				}
				final OrderlessMatcher foMatcher = new OrderlessMatcher(lhsPatternAST, lhsEvalAST);
				boolean matched = foMatcher.matchOrderlessAST(1, stackMatcher);
				if (matched) {
					IAST lhsResultAST = (lhsEvalAST).clone();
					foMatcher.filterResult(lhsResultAST);
					try {
						IExpr result = fPatternMap.substitutePatternSymbols(rhsExpr);
						result = F.eval(result);
						lhsResultAST.add(result);
						return lhsResultAST;
					} catch (final ConditionException e) {
						// fall through
					} catch (final ReturnException e) {
						lhsResultAST.add(e.getValue());
						return lhsResultAST;
					}
					return null;
				}

			}
			if (lhsPatternAST.isFlatAST()) {
				if (!matchExpr(lhsPatternAST.head(), lhsEvalAST.head(), new StackMatcher())) {
					return null;
				}
				int len = lhsEvalAST.size() - lhsPatternAST.size();
				for (int i = 0; i < len; i++) {
					if (matchASTSequence(lhsPatternAST, lhsEvalAST, i, stackMatcher)) {
						IAST lhsResultAST = (lhsEvalAST).clone();
						for (int j = 1; j < lhsPatternAST.size(); j++) {
							lhsResultAST.remove(i + 1);
						}
						try {
							IExpr result = fPatternMap.substitutePatternSymbols(rhsExpr);
							result = F.eval(result);
							lhsResultAST.add(i + 1, result);
							return lhsResultAST;
						} catch (final ConditionException e) {
							// fall through
						} catch (final ReturnException e) {
							lhsResultAST.add(i + 1, e.getValue());
							return lhsResultAST;
						}
						return null;
					}
				}
			}
		}
		return null;
	}

	private boolean matchPattern(final IPattern pattern, final IExpr expr) {
		if (!pattern.isConditionMatched(expr)) {
			return false;
		}

		IExpr value = fPatternMap.getValue(pattern);
		if (value != null) {
			return expr.equals(value);
		}
		fPatternMap.setValue(pattern, expr);
		return true;
		// ISymbol sym = pattern.getSymbol();
		// if (sym != null) {
		// final IExpr value = fPatternMap.getValue(sym);
		// if (value != null) {
		// return expr.equals(value);
		// }
		//
		// fPatternMap.setValue(sym, expr);
		// }
		// return true;
	}

	private boolean matchPatternSequence(final IPatternSequence pattern, final IAST sequence) {
		if (!pattern.isConditionMatchedSequence(sequence)) {
			return false;
		}

		IExpr value = fPatternMap.getValue(pattern);
		if (value != null) {
			return sequence.equals(value);
		}
		fPatternMap.setValue(pattern, sequence);
		return true;
		// ISymbol sym = pattern.getSymbol();
		//
		// if (sym != null) {
		// final IExpr value = fPatternMap.getValue(sym);
		// if (value != null) {
		// return sequence.equals(value);
		// }
		//
		// fPatternMap.setValue(sym, sequence);
		// }
		// return true;
	}

	@Override
	public IExpr eval(final IExpr leftHandSide) {
		return null;
	}

	@Override
	public boolean equals(final Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj instanceof PatternMatcher) {
			final PatternMatcher pm = (PatternMatcher) obj;
			if (fPatternMap.size() != pm.fPatternMap.size()) {
				return false;
			}
			if (isRuleWithoutPatterns()) {
				return fLhsPatternExpr.equals(pm.fLhsPatternExpr);
			}
			if (equivalent(fLhsPatternExpr, pm.fLhsPatternExpr)) {
				if ((fPatternCondition != null) && (pm.fPatternCondition != null)) {
					return fPatternCondition.equals(pm.fPatternCondition);
				}
				if ((fPatternCondition != null) || (pm.fPatternCondition != null)) {
					return false;
				}
				return true;
			}
		}
		return false;
	}

	@Override
	public int hashCode() {
		return fLhsPatternExpr.hashCode();
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		PatternMatcher v = (PatternMatcher) super.clone();
		v.fPatternCondition = fPatternCondition;
		v.fPatternMap = fPatternMap.clone();
		return v;
	}

	/**
	 * Get the additional condition for pattern-matching
	 * 
	 */
	public IExpr getCondition() {
		return fPatternCondition;
	}

	/**
	 * Sets an additional evaluation-condition for pattern-matching
	 * 
	 */
	public void setCondition(final IExpr condition) {
		fPatternCondition = condition;
	}

	/**
	 * Transform the ast recursively, according to the attributes Flat, HoldAll, HoldFirst, HoldRest, Orderless for the
	 * left-hand-side of a Set[] or SetDelayed[] expression. Delegates to EvalEngine#evalSetAttributes()method
	 * 
	 * @param ast
	 * @return
	 * @see org.matheclipse.core.eval.EvalEngine#evalSetAttributes()
	 */
	public static IExpr evalLeftHandSide(final IAST leftHandSide, final EvalEngine engine) {
		return engine.evalSetAttributes((IAST) leftHandSide);
		// if (temp != null) {
		// return temp;
		// }
		// return leftHandSide;
	}

	public static IExpr evalLeftHandSide(IAST leftHandSide) {
		return evalLeftHandSide(leftHandSide, EvalEngine.get());
	}

}
 

Ads help maintain this website.