package org.matheclipse.core.expression;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.matheclipse.core.basic.Config;
import org.matheclipse.core.convert.AST2Expr;
import org.matheclipse.core.eval.EvalEngine;
import org.matheclipse.core.eval.SystemNamespace;
import org.matheclipse.core.eval.exception.RuleCreationError;
import org.matheclipse.core.eval.exception.WrongArgumentType;
import org.matheclipse.core.eval.interfaces.ISignedNumberConstant;
import org.matheclipse.core.eval.interfaces.ISymbolEvaluator;
import org.matheclipse.core.form.output.OutputFormFactory;
import org.matheclipse.core.generic.UnaryVariable2Slot;
import org.matheclipse.core.generic.interfaces.INumericFunction;
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.ISymbol;
import org.matheclipse.core.patternmatching.DownRulesData;
import org.matheclipse.core.patternmatching.IPatternMatcher;
import org.matheclipse.core.patternmatching.PatternMatcher;
import org.matheclipse.core.patternmatching.PatternMatcherAndInvoker;
import org.matheclipse.core.patternmatching.PatternMatcherEquals;
import org.matheclipse.core.patternmatching.UpRulesData;
import org.matheclipse.core.util.OpenIntToIExprHashMap;
import org.matheclipse.core.visit.IVisitor;
import org.matheclipse.core.visit.IVisitorBoolean;
import org.matheclipse.core.visit.IVisitorInt;

import com.google.common.base.Function;

/**
 * Implements Symbols for function, constant and variable names
 * 
 */
public class Symbol extends ExprImpl implements ISymbol {

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

	/**
	 * 
	 */

	private static final int DEFAULT_VALUE_INDEX = Integer.MIN_VALUE;

	/**
	 * The attribute values of the symbol represented by single bits.
	 */
	private int fAttributes = NOATTRIBUTE;

	private transient IEvaluator fEvaluator;

	/**
	 * The pattern matching "down value" rules associated with this symbol.
	 */
	private transient DownRulesData fDownRulesData = new DownRulesData();

	/**
	 * The pattern matching "up value" rules associated with this symbol.
	 */
	private transient UpRulesData fUpRulesData = new UpRulesData();

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IExpr[] reassignSymbolValue(Function function, ISymbol functionSymbol) {
		IExpr[] result = new IExpr[2];
		IExpr symbolValue;
		if (hasLocalVariableStack()) {
			symbolValue = get();
			result[0] = symbolValue;
			IExpr calculatedResult = function.apply(symbolValue);
			if (calculatedResult != null) {
				set(calculatedResult);
				result[1] = calculatedResult;
				return result;
			}

		} else {
			if (fDownRulesData != null) {
				PatternMatcherEquals pme = fDownRulesData.getEqualDownRules().get(this);
				if (pme != null) {
					symbolValue = pme.getRHS();
					if (symbolValue != null) {
						result[0] = symbolValue;
						IExpr calculatedResult = function.apply(symbolValue);
						if (calculatedResult != null) {
							pme.setRHS(calculatedResult);
							result[1] = calculatedResult;
							return result;
						}
					}
				}
			}
		}
		throw new WrongArgumentType(this, functionSymbol.toString() + " - Symbol: " + toString()
				+ " has no value! Reassignment with a new value is not possible");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IExpr getAssignedValue() {
		if (hasLocalVariableStack()) {
			return get();
		} else {
			if (fDownRulesData != null) {
				PatternMatcherEquals pme = fDownRulesData.getEqualDownRules().get(this);
				if (pme != null) {
					return pme.getRHS();
				}
			}
		}
		return null;
	}

	private OpenIntToIExprHashMap fDefaultValues = null;

	static class DummyEvaluator implements IEvaluator {
		@Override
		public void setUp(ISymbol symbol) {

		}
	}

	protected static final DummyEvaluator DUMMY_EVALUATOR = new DummyEvaluator();

	/* package private */String fSymbolName;

	/**
	 * The hash value of this object computed in the constructor.
	 * 
	 */
	final int fHashValue;

	public Symbol(final String symbolName) {
		this(symbolName, null);
	}

	public Symbol(final String symbolName, final IEvaluator evaluator) {
		super();
		fHashValue = (symbolName == null) ? 197 : 7 * symbolName.hashCode();
		fSymbolName = symbolName;
		fEvaluator = evaluator;
	}

	/** {@inheritDoc} */
	@Override
	public IExpr apply(IExpr... expressions) {
		return F.ast(expressions, this);
	}

	/** {@inheritDoc} */
	@Override
	public void pushLocalVariable() {
		pushLocalVariable(null);
	}

	/** {@inheritDoc} */
	@Override
	public void pushLocalVariable(final IExpr expression) {
		final Stack localVariableStack = EvalEngine.localStackCreate(this);
		localVariableStack.push(expression);
	}

	/** {@inheritDoc} */
	@Override
	public void popLocalVariable() {
		final Stack fLocalVariableStack = EvalEngine.localStack(this);
		fLocalVariableStack.pop();
	}

	/** {@inheritDoc} */
	@Override
	public void clear(EvalEngine engine) {
		if (!engine.isPackageMode()) {
			if (Config.SERVER_MODE && (fSymbolName.charAt(0) != '$')) {
				throw new RuleCreationError(null);
			}
		}
		if (fDownRulesData != null) {
			fDownRulesData.clear();
		}
	}

	/** {@inheritDoc} */
	@Override
	public void clearAll(EvalEngine engine) {
		clear(engine);
		fAttributes = NOATTRIBUTE;
	}

	/** {@inheritDoc} */
	@Override
	public boolean equals(final Object obj) {
		// Symbols are unique objects
		// Makes no sense to compare the symbol names.
		return this == obj;
	}

	@Override
	public boolean isSymbolName(String name) {
		if (Config.PARSER_USE_LOWERCASE_SYMBOLS) {
			return fSymbolName.equalsIgnoreCase(name);
		}
		return fSymbolName.equals(name);
	}

	/** {@inheritDoc} */
	@Override
	public IExpr evaluate(EvalEngine engine) {
		if (hasLocalVariableStack()) {
			return get();
		}
		IExpr result;
		if ((result = evalDownRule(engine, this)) != null) {
			return result;
		}
		final IEvaluator module = getEvaluator();
		if (module instanceof ISymbolEvaluator) {
			if (engine.isNumericMode()) {
				return ((ISymbolEvaluator) module).numericEval(this);
			}
			return ((ISymbolEvaluator) module).evaluate(this);
		}
		return null;
	}

	/** {@inheritDoc} */
	@Override
	public IExpr evalDownRule(final IEvaluationEngine ee, final IExpr expression) {
		if (fDownRulesData == null) {
			return null;
		}
		return fDownRulesData.evalDownRule(ee, expression);
	}

	/** {@inheritDoc} */
	@Override
	public IExpr evalUpRule(final IEvaluationEngine ee, final IExpr expression) {
		if (fUpRulesData == null) {
			return null;
		}
		return fUpRulesData.evalUpRule(ee, expression);
	}

	/** {@inheritDoc} */
	@Override
	public final int getAttributes() {
		return fAttributes;
	}

	/** {@inheritDoc} */
	@Override
	public IEvaluator getEvaluator() {
		if (fEvaluator == null) {
			fEvaluator = DUMMY_EVALUATOR;
			if (Config.PARSER_USE_LOWERCASE_SYMBOLS) {
				SystemNamespace.DEFAULT.setEvaluator(this);
			} else {
				if (Character.isUpperCase(fSymbolName.charAt(0))) {
					SystemNamespace.DEFAULT.setEvaluator(this);
				}
			}
		}
		return fEvaluator;
	}

	/** {@inheritDoc} */
	@Override
	public boolean hasLocalVariableStack() {
		final Stack localVariableStack = EvalEngine.localStack(this);
		return (localVariableStack != null) && !(localVariableStack.isEmpty());
	}

	/** {@inheritDoc} */
	@Override
	public IExpr get() {
		final Stack localVariableStack = EvalEngine.localStack(this);
		if (localVariableStack == null) {
			return null;
		}
		return localVariableStack.peek();
	}

	/** {@inheritDoc} */
	@Override
	public void set(final IExpr value) {
		final Stack localVariableStack = EvalEngine.localStack(this);

		localVariableStack.set(localVariableStack.size() - 1, value);
	}

	/** {@inheritDoc} */
	@Override
	public int hashCode() {
		return fHashValue;
	}

	/** {@inheritDoc} */
	@Override
	public int hierarchy() {
		return SYMBOLID;
	}

	/** {@inheritDoc} */
	@Override
	public final boolean isString(final String str) {
		return fSymbolName.equals(str);
	}

	/** {@inheritDoc} */
	@Override
	public IPatternMatcher putDownRule(ISymbol symbol, final boolean equalRule, final IExpr leftHandSide,
			final IExpr rightHandSide, boolean packageMode) {
		return putDownRule(symbol, equalRule, leftHandSide, rightHandSide, DEFAULT_RULE_PRIORITY, packageMode);
	}

	/** {@inheritDoc} */
	@Override
	public IPatternMatcher putDownRule(ISymbol setSymbol, final boolean equalRule, final IExpr leftHandSide,
			final IExpr rightHandSide, final int priority, boolean packageMode) {
		if (!packageMode) {
			if (Config.SERVER_MODE && (fSymbolName.charAt(0) != '$')) {
				throw new RuleCreationError(leftHandSide);
			}

			EvalEngine.get().addModifiedVariable(this);
		}
		if (fDownRulesData == null) {
			fDownRulesData = new DownRulesData();
		}
		return fDownRulesData.putDownRule(setSymbol, equalRule, leftHandSide, rightHandSide, priority);
	}

	/** {@inheritDoc} */
	@Override
	public PatternMatcher putDownRule(final PatternMatcherAndInvoker pmEvaluator) {
		if (fDownRulesData == null) {
			fDownRulesData = new DownRulesData();
		}
		return fDownRulesData.putDownRule(pmEvaluator);
	}

	/** {@inheritDoc} */
	@Override
	public IPatternMatcher putUpRule(ISymbol symbol, boolean equalRule, IAST leftHandSide, IExpr rightHandSide) {
		return putUpRule(symbol, equalRule, leftHandSide, rightHandSide, DEFAULT_RULE_PRIORITY);
	}

	/** {@inheritDoc} */
	@Override
	public IPatternMatcher putUpRule(ISymbol setSymbol, final boolean equalRule, final IAST leftHandSide,
			final IExpr rightHandSide, final int priority) {
		EvalEngine engine = EvalEngine.get();
		if (!engine.isPackageMode()) {
			if (Config.SERVER_MODE && (fSymbolName.charAt(0) != '$')) {
				throw new RuleCreationError(leftHandSide);
			}

			engine.addModifiedVariable(this);
		}
		if (fUpRulesData == null) {
			fUpRulesData = new UpRulesData();
		}
		return fUpRulesData.putUpRule(setSymbol, equalRule, leftHandSide, rightHandSide, priority);
	}

	/** {@inheritDoc} */
	@Override
	public void setAttributes(final int attributes) {
		fAttributes = attributes;
		if (fSymbolName.charAt(0) == '$' && Config.SERVER_MODE) {
			EvalEngine engine = EvalEngine.get();
			engine.addModifiedVariable(this);
		}
	}

	/** {@inheritDoc} */
	@Override
	public final void setEvaluator(final IEvaluator evaluator) {
		fEvaluator = evaluator;
		evaluator.setUp(this);
	}

	/**
	 * Compares this expression with the specified expression for order. Returns a negative integer, zero, or a positive integer as
	 * this expression is canonical less than, equal to, or greater than the specified expression.
	 */
	@Override
	public int compareTo(final IExpr obj) {
		if (obj instanceof Symbol) {
			if (this == obj) {
				// Symbols are unique objects
				// Makes no sense to compare the symbol names, if they are equal
				return 0;
			}
			return fSymbolName.compareTo(((Symbol) obj).fSymbolName);
		}
		if (obj instanceof AST) {
			final AST ast = (AST) obj;
			if (ast.size() > 1) {
				if (ast.isPower()) {
					if (ast.get(1) instanceof ISymbol) {
						final int cp = fSymbolName.compareTo(((Symbol) ast.get(1)).fSymbolName);
						if (cp != 0) {
							return cp;
						}
						if (EvalEngine.get().isNumericMode()) {
							return F.CD1.compareTo(ast.get(2));
						}
						return F.C1.compareTo(ast.get(2));
					}
				} else if (ast.isTimes()) {
					// compare with the last ast element:
					final IExpr lastTimes = ast.last();
					if (lastTimes instanceof AST) {
						final IExpr lastTimesHeader = ((IAST) lastTimes).head();
						if ((lastTimesHeader == F.Power) && (((IAST) lastTimes).size() == 3)) {
							final int cp = compareTo(((IAST) lastTimes).get(1));
							if (cp != 0) {
								return cp;
							}
							return F.C1.compareTo(((IAST) lastTimes).get(2));
						}
					}
					final int cp = compareTo(lastTimes);
					if (cp != 0) {
						return cp;
					}
				}
			}
			return -1;
		}
		return (hierarchy() - (obj).hierarchy());
	}

	/** {@inheritDoc} */
	@Override
	public final boolean isAtom() {
		return true;
	}

	/** {@inheritDoc} */
	@Override
	public boolean isConstant() {
		return (fAttributes & CONSTANT) == CONSTANT;
	}

	/** {@inheritDoc} */
	@Override
	public boolean isTrue() {
		return this.equals(F.True);
	}

	/** {@inheritDoc} */
	@Override
	public final boolean isValue() {
		return evaluate(EvalEngine.get()) != null;
	}

	/** {@inheritDoc} */
	@Override
	public boolean isFalse() {
		return this.equals(F.False);
	}

	/** {@inheritDoc} */
	@Override
	public boolean isNegative() {
		if (isNumericFunction()) {
			IExpr temp = F.evaln(this);
			if (temp.isSignedNumber() && temp.isNegative()) {
				return true;
			}
		}
		return false;
	}

	/** {@inheritDoc} */
	@Override
	public boolean isPositive() {
		if (isNumericFunction()) {
			IExpr temp = F.evaln(this);
			if (temp.isSignedNumber() && temp.isPositive()) {
				return true;
			}
		}
		return false;
	}

	/** {@inheritDoc} */
	@Override
	public ISymbol head() {
		return F.SymbolHead;
	}

	/** {@inheritDoc} */
	public final String getSymbol() {
		return fSymbolName;
	}

	@Override
	/**
	 * {@inheritDoc}
	 */
	public IExpr variables2Slots(final Map map, final List variableList) {
		final UnaryVariable2Slot uv2s = new UnaryVariable2Slot(map, variableList);
		return uv2s.apply(this);
	}

	@Override
	/**
	 * {@inheritDoc}
	 */
	public String internalFormString(boolean symbolsAsFactoryMethod, int depth) {
		if (symbolsAsFactoryMethod) {
			if (fSymbolName.length() == 1 && Character.isLowerCase(fSymbolName.charAt(0))) {
				char ch = fSymbolName.charAt(0);
				if ('a' <= ch && ch <= 'z') {
					return fSymbolName;
				}
			}
			if (Character.isUpperCase(fSymbolName.charAt(0))) {
				String alias = F.PREDEFINED_INTERNAL_FORM_STRINGS.get(fSymbolName);
				if (alias != null) {
					if (alias.contains("::")) {
						return "$s(\"" + alias + "\")";
					}
					return alias;
				}
			}
			return "$s(\"" + fSymbolName + "\")";
		}
		if (Config.PARSER_USE_LOWERCASE_SYMBOLS) {
			String name = AST2Expr.PREDEFINED_SYMBOLS_MAP.get(fSymbolName.toString().toLowerCase());
			if (name != null) {
				return name;
			}
		}
		return fSymbolName;
	}

	@Override
	public String toString() {
		return fSymbolName;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String fullFormString() {
		if (Config.PARSER_USE_LOWERCASE_SYMBOLS) {
			String str = AST2Expr.PREDEFINED_SYMBOLS_MAP.get(fSymbolName);
			if (str != null) {
				return str;
			}
		}
		return fSymbolName;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List definition() {
		ArrayList result = new ArrayList();
		if (fDownRulesData != null) {
			result.addAll(fDownRulesData.definition());
		}
		if (fUpRulesData == null) {
			return result;
		}
		result.addAll(fUpRulesData.definition());
		return result;
	}

	/** {@inheritDoc} */
	@Override
	public IExpr getDefaultValue() {
		// special case for a general default value
		if (fDefaultValues == null) {
			return null;
		}
		return fDefaultValues.get(DEFAULT_VALUE_INDEX);
	}

	/** {@inheritDoc} */
	@Override
	public IExpr getDefaultValue(int pos) {
		// default value at this position
		if (fDefaultValues == null) {
			return null;
		}
		return fDefaultValues.get(Integer.valueOf(pos));
	}

	/** {@inheritDoc} */
	@Override
	public void setDefaultValue(IExpr expr) {
		// special case for a general default value
		if (fDefaultValues == null) {
			fDefaultValues = new OpenIntToIExprHashMap();
		}
		fDefaultValues.put(DEFAULT_VALUE_INDEX, expr);
	}

	/** {@inheritDoc} */
	@Override
	public void setDefaultValue(int pos, IExpr expr) {
		// default value at this position
		if (fDefaultValues == null) {
			fDefaultValues = new OpenIntToIExprHashMap();
		}
		fDefaultValues.put(Integer.valueOf(pos), expr);
	}

	/** {@inheritDoc} */
	@Override
	public String definitionToString() throws IOException {
		// dummy call to ensure, that the associated rules are loaded:
		getEvaluator();

		StringWriter buf = new StringWriter();

		OutputFormFactory off = OutputFormFactory.get();
		off.setIgnoreNewLine(true);
		List list = definition();
		buf.append("{");
		for (int i = 0; i < list.size(); i++) {
			off.convert(buf, list.get(i));
			if (i < list.size() - 1) {
				buf.append(",\n ");
				off.setColumnCounter(0);
			}
		}
		buf.append("}\n");
		return buf.toString();
	}

	/** {@inheritDoc} */
	@Override
	public void readSymbol(java.io.ObjectInputStream stream) throws IOException {
		fSymbolName = stream.readUTF();
		fAttributes = stream.read();
		boolean hasDownRulesData = stream.readBoolean();
		if (hasDownRulesData) {
			fDownRulesData.readSymbol(stream);
		}
		boolean hasUpRulesData = stream.readBoolean();
		if (hasUpRulesData) {
			fUpRulesData.readSymbol(stream);
		}
	}

	/** {@inheritDoc} */
	@Override
	public void writeSymbol(java.io.ObjectOutputStream stream) throws java.io.IOException {
		stream.writeUTF(fSymbolName);
		stream.write(fAttributes);
		if (fDownRulesData == null) {
			stream.writeBoolean(false);
		} else {
			stream.writeBoolean(true);
			fDownRulesData.writeSymbol(stream);
		}
		if (fUpRulesData == null) {
			stream.writeBoolean(false);
		} else {
			stream.writeBoolean(true);
			fUpRulesData.writeSymbol(stream);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public  T accept(IVisitor visitor) {
		return visitor.visit(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean accept(IVisitorBoolean visitor) {
		return visitor.visit(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int accept(IVisitorInt visitor) {
		return visitor.visit(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IExpr mapConstantDouble(INumericFunction function) {
		if (isConstant()) {
			IEvaluator evaluator = getEvaluator();
			if (evaluator instanceof ISignedNumberConstant) {
				ISignedNumberConstant numericConstant = (ISignedNumberConstant) evaluator;
				double value = numericConstant.evalReal();
				if (value < Integer.MAX_VALUE && value > Integer.MIN_VALUE) {
					return function.apply(value);
				}
			}
		}
		return null;
	}

	@Override
	public IExpr negate() {
		return F.Times(F.CN1, this);
	}
}
 

Ads help maintain this website.