package org.matheclipse.core.patternmatching;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.matheclipse.core.expression.F;
import org.matheclipse.core.generic.Functors;
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;

/**
 * A map from a pattern to a possibly found value during pattern-matching.
 * 
 */
public class PatternMap implements Cloneable, Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -5384429232269800438L;

	/**
	 * Count the number of patterns with associated symbols in the pattern map.
	 */
	private int fPatternCounter;

	/**
	 * If true the rule contains no pattern.
	 */
	private boolean fRuleWithoutPattern;

	/**
	 * Contains the symbols of the patterns. The corresponding value (or null) is stored in
	 * fPatternValuesArray.
	 */
	private ISymbol[] fSymbolsArray;

	/**
	 * Contains the current values of the pattern symbols. The corresponding symbol is stored in fSymbolsArray.
	 */
	private IExpr[] fPatternValuesArray;

	public PatternMap() {
		this(new IExpr[0]);
	}

	private PatternMap(IExpr[] exprArray) {
		this.fPatternCounter = 0;
		this.fRuleWithoutPattern = true;
		this.fPatternValuesArray = exprArray;
	}

	/**
	 * Set the index of fPatternSymbolsArray where the pattern stores it's assigned value during pattern
	 * matching.
	 * 
	 * @param pattern
	 * @param patternIndexMap
	 */
	protected void addPattern(TreeMap patternIndexMap, IPatternObject pattern) {
		fRuleWithoutPattern = false;
		ISymbol sym = pattern.getSymbol();
		if (sym != null) {
			Integer i = patternIndexMap.get(sym);
			if (i != null) {
				// for "named" patterns (i.e. "x_" or "x_IntegerQ")
				pattern.setIndex(i.intValue());
				return;
			}
			pattern.setIndex(fPatternCounter);
			patternIndexMap.put(sym, Integer.valueOf(fPatternCounter++));
		}
	}

	protected void addSinglePattern(IPatternObject pattern) {
		fRuleWithoutPattern = false;
		ISymbol sym = pattern.getSymbol();
		if (sym != null) {
			pattern.setIndex(0);
			this.fSymbolsArray = new ISymbol[1];
			this.fPatternValuesArray = new IExpr[1];
			fSymbolsArray[0] = sym;
			fPatternCounter++;
		}
	}

	/**
	 * Determine all patterns (i.e. all objects of instance IPattern) in the given expression
	 * 
	 * Increments this classes pattern counter.
	 * 
	 * @param lhsPatternExpr
	 *            the (left-hand-side) expression which could contain pattern objects.
	 */
	protected void determinePatterns(final IExpr lhsPatternExpr) {
		if (lhsPatternExpr instanceof IAST) {
			TreeMap patternIndexMap = new TreeMap();
			determinePatternsRecursive(patternIndexMap, (IAST) lhsPatternExpr);
			this.fSymbolsArray = new ISymbol[fPatternCounter];
			this.fPatternValuesArray = new IExpr[fPatternCounter];
			for (ISymbol sym : patternIndexMap.keySet()) {
				Integer indx = patternIndexMap.get(sym);
				fSymbolsArray[indx.intValue()] = sym;
			}
		} else if (lhsPatternExpr instanceof IPatternObject) {
			addSinglePattern((IPatternObject) lhsPatternExpr);
		}
	}

	/**
	 * Determine all patterns (i.e. all objects of instance IPattern) in the given expression
	 * 
	 * Increments this classes pattern counter.
	 * 
	 * @param patternIndexMap
	 * @param lhsPatternExpr
	 *            the (left-hand-side) expression which could contain pattern objects.
	 */
	private int determinePatternsRecursive(TreeMap patternIndexMap, final IAST lhsPatternExpr) {
		final IAST ast = (IAST) lhsPatternExpr;
		int listEvalFlags = IAST.NO_FLAG;
		for (int i = 0; i < ast.size(); i++) {
			IExpr temp = ast.get(i);
			if (temp.isAST()) {
				listEvalFlags |= determinePatternsRecursive(patternIndexMap, (IAST) temp);
			} else if (temp instanceof IPatternObject) {
				if (temp.isPattern()) {
					IPattern pat = (IPattern) temp;
					addPattern(patternIndexMap, pat);
					if (pat.isDefault()) {
						// the ast contains a pattern with default value
						// (i.e. "x_.")
						listEvalFlags |= IAST.CONTAINS_DEFAULT_PATTERN;
					} else {
						// the ast contains a pattern without value (i.e.
						// "x_")
						listEvalFlags |= IAST.CONTAINS_PATTERN;
					}
				} else if (temp.isPatternSequence()) {
					addPattern(patternIndexMap, (IPatternSequence) temp);
					// the ast contains a pattern sequence (i.e. "x__")
					listEvalFlags |= IAST.CONTAINS_PATTERN_SEQUENCE;
				}
			}
		}
		ast.setEvalFlags(listEvalFlags);
		// disable flag "pattern with default value"
		listEvalFlags &= IAST.CONTAINS_NO_DEFAULT_PATTERN_MASK;
		return listEvalFlags;
	}

	@Override
	protected PatternMap clone() {
		PatternMap result = new PatternMap(null);
		// avoid Arrays.copyOf because of Android version
		result.fPatternValuesArray = new IExpr[fPatternValuesArray.length];
		System.arraycopy(fPatternValuesArray, 0, result.fPatternValuesArray, 0, fPatternValuesArray.length);
		// don't clone the fSymbolsArray which is final after the
		// #determinepatterns()
		// method
		result.fSymbolsArray = fSymbolsArray;
		result.fPatternCounter = fPatternCounter;
		result.fRuleWithoutPattern = fRuleWithoutPattern;
		return result;
	}

	/**
	 * Copy the current values into a new array.
	 * 
	 * @return
	 * @see PatternMap#resetPattern(IExpr[])
	 */
	public IExpr[] copyPattern() {
		IExpr[] patternValuesArray = new IExpr[fPatternValuesArray.length];
		System.arraycopy(fPatternValuesArray, 0, patternValuesArray, 0, fPatternValuesArray.length);
		return patternValuesArray;
	}

	/**
	 * Copy the found pattern matches from the given patternMap back to this maps pattern values.
	 * 
	 * @param patternMap
	 */
	public void copyPatternValuesFromPatternMatcher(final PatternMap patternMap) {
		// for (ISymbol pattern : patternMap.fSymbolsArray) {
		ISymbol[] symbolsArray = patternMap.fSymbolsArray;
		for (int i = 0; i < symbolsArray.length; i++) {
			for (int j = 0; j < fSymbolsArray.length; j++) {
				if (fSymbolsArray[j] == symbolsArray[i]) {
					fPatternValuesArray[j] = patternMap.fPatternValuesArray[i];
				}
			}
		}
	}

	private Map getRulesMap() {
		final Map rulesMap = new IdentityHashMap(fSymbolsArray.length * 2);
		for (int i = 0; i < fSymbolsArray.length; i++) {
			if (fPatternValuesArray[i] != null) {
				rulesMap.put(fSymbolsArray[i], fPatternValuesArray[i]);
			}
		}
		return rulesMap;
	}

	public IExpr getValue(IPatternObject pattern) {
		int indx = pattern.getIndex();
		if (indx >= 0) {
			return fPatternValuesArray[indx];
		}
		return null;
	}

	public List getValuesAsList() {
		List args = new ArrayList(fPatternValuesArray.length);
		IExpr arg;
		for (int i = 0; i < fPatternValuesArray.length; i++) {
			arg = fPatternValuesArray[i];
			if (arg == null)
				return null;
			args.add(arg);
		}
		return args;
	}

	/**
	 * Set all pattern values to null;
	 */
	protected void initPattern() {
		Arrays.fill(fPatternValuesArray, null);
	}

	/**
	 * Check if all symbols in the symbols array have corresponding values assigned.
	 * 
	 * @return
	 */
	public boolean isAllPatternsAssigned() {
		if (fPatternValuesArray != null) {
			// all patterns have values assigned?
			for (int i = 0; i < fPatternValuesArray.length; i++) {
				if (fPatternValuesArray[i] == null) {
					return false;
				}
			}
		}
		return true;
	}

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

	/**
	 * Reset the values to the values in the given array
	 * 
	 * @param patternValuesArray
	 * @see PatternMap#copyPattern()
	 */
	public void resetPattern(IExpr[] patternValuesArray) {
		System.arraycopy(patternValuesArray, 0, fPatternValuesArray, 0, fPatternValuesArray.length);
	}

	public void setValue(IPatternObject pattern, IExpr expr) {
		int indx = pattern.getIndex();
		if (indx >= 0) {
			fPatternValuesArray[indx] = expr;
		}
	}

	public int size() {
		return fPatternValuesArray.length;
	}

	/**
	 * Substitute all symbols in the given expression with the current value of the given arrays
	 * 
	 * @param expression
	 * 
	 * @return
	 */
	public IExpr substitutePatternSymbols(final IExpr expression) {
		if (fPatternValuesArray != null) {
			return F.subst(expression, Functors.rules(getRulesMap()));
		}
		return expression;
	}

}
 

Ads help maintain this website.