package org.matheclipse.core.reflection.system;

import java.util.ArrayList;
import java.util.Collections;

import org.matheclipse.core.eval.exception.Validate;
import org.matheclipse.core.eval.exception.WrongArgumentType;
import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.generic.Predicates;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IComplex;
import org.matheclipse.core.interfaces.IComplexNum;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.IFraction;
import org.matheclipse.core.interfaces.IInteger;
import org.matheclipse.core.interfaces.INum;
import org.matheclipse.core.interfaces.IPattern;
import org.matheclipse.core.interfaces.IPatternSequence;
import org.matheclipse.core.interfaces.IStringX;
import org.matheclipse.core.interfaces.ISymbol;
import org.matheclipse.core.visit.AbstractVisitorBoolean;

import com.google.common.base.Predicate;

/**
 * Try to eliminate a variable in a set of equations (i.e. Equal[...] expressions).
 * 
 * See Wikipedia - System of linear
 * equations - Elimination of variables.
 */
public class Eliminate extends AbstractFunctionEvaluator {

	private static class VariableCounterVisitor extends AbstractVisitorBoolean implements Comparable {
		/**
		 * Count the number of nodes in fExpr, which equals fVariable.
		 */
		int fVariableCounter;

		/**
		 * Count the total number of nodes in fExpr..
		 */
		int fNodeCounter;

		/**
		 * The maximum number of recursion levels for visiting nodes, which equals fVariable.
		 */
		int fMaxVariableDepth;

		/**
		 * Holds the current recursion level for visiting nodes.
		 */
		int fCurrentDepth;

		final IExpr fVariable;
		final IAST fExpr;

		public VariableCounterVisitor(final IAST expr, final IExpr variable) {
			super();
			fVariable = variable;
			fExpr = expr;
			fVariableCounter = 0;
			fNodeCounter = 0;
			fMaxVariableDepth = 0;
			fCurrentDepth = 0;
		}

		@Override
		public int compareTo(VariableCounterVisitor other) {
			if (fVariableCounter < other.fVariableCounter) {
				return -1;
			}
			if (fVariableCounter > other.fVariableCounter) {
				return 1;
			}
			if (fMaxVariableDepth < other.fMaxVariableDepth) {
				return -1;
			}
			if (fMaxVariableDepth > other.fMaxVariableDepth) {
				return 1;
			}
			if (fNodeCounter < other.fNodeCounter) {
				return -1;
			}
			if (fNodeCounter > other.fNodeCounter) {
				return 1;
			}
			return 0;
		}

		public IAST getExpr() {
			return fExpr;
		}

		@Override
		public boolean visit(IAST ast) {
			fNodeCounter++;
			if (ast.equals(fVariable)) {
				fVariableCounter++;
				if (fMaxVariableDepth < fCurrentDepth) {
					fMaxVariableDepth = fCurrentDepth;
				}
				return true;
			}
			try {
				fCurrentDepth++;
				for (int i = 1; i < ast.size(); i++) {
					ast.get(i).accept(this);
				}
			} finally {
				fCurrentDepth--;
			}

			return false;
		}

		@Override
		public boolean visit(ISymbol symbol) {
			fNodeCounter++;
			if (symbol.equals(fVariable)) {
				fVariableCounter++;
				if (fMaxVariableDepth < fCurrentDepth) {
					fMaxVariableDepth = fCurrentDepth;
				}
				return true;
			}
			return false;
		}

		public boolean visit(IInteger element) {
			fNodeCounter++;
			return false;
		}

		public boolean visit(IFraction element) {
			fNodeCounter++;
			return false;
		}

		public boolean visit(IComplex element) {
			fNodeCounter++;
			return false;
		}

		public boolean visit(INum element) {
			fNodeCounter++;
			return false;
		}

		public boolean visit(IComplexNum element) {
			fNodeCounter++;
			return false;
		}

		public boolean visit(IPattern element) {
			fNodeCounter++;
			return false;
		}

		public boolean visit(IPatternSequence element) {
			fNodeCounter++;
			return false;
		}

		public boolean visit(IStringX element) {
			fNodeCounter++;
			return false;
		}

	}

	/**
	 * Check if the argument at the given position is an equation (i.e. Equal[a,b]) or a list of equations and return a list of
	 * expressions, which should be equal to 0.
	 * 
	 * @param ast
	 * @param position
	 * @return
	 */
	private static IAST checkEquations(final IAST ast, int position) {
		IAST equalList = F.List();
		IAST eqns = null;
		IAST eq;
		if (ast.get(position).isList()) {
			eqns = (IAST) ast.get(position);
			for (int i = 1; i < eqns.size(); i++) {
				if (eqns.get(i).isAST(F.Equal, 3)) {
					eq = (IAST) eqns.get(i);
					equalList.add(F.Equal(F.evalExpandAll(eq.arg1()), F.evalExpandAll(eq.arg2())));
				} else {
					// not an equation
					throw new WrongArgumentType(eqns, eqns.get(i), i, "Equal[] expression (a==b) expected");
				}
			}
		} else {
			if (ast.get(position).isAST(F.Equal, 3)) {
				eq = (IAST) ast.get(position);
				equalList.add(F.Equal(F.evalExpandAll(eq.arg1()), F.evalExpandAll(eq.arg2())));
			} else {
				// not an equation
				throw new WrongArgumentType(ast, ast.arg1(), 1, "Equal[] expression (a==b) expected");
			}
		}
		return equalList;
	}

	/**
	 * Analyze an Equal() expression.
	 * 
	 * @param equalAST
	 *            an Equal() expression.
	 * @param variable
	 *            the variable which should be eliminated.
	 * @return null if we can't find an equation for the given variable.
	 */
	private static IExpr eliminateAnalyze(IAST equalAST, ISymbol variable) {
		IExpr arg1 = equalAST.arg1();
		IExpr arg2 = equalAST.arg2();
		Predicate predicate = Predicates.in(variable);
		boolean boolArg1 = arg1.isFree(predicate, true);
		boolean boolArg2 = arg2.isFree(predicate, true);
		IExpr result = null;
		if (!boolArg1 && boolArg2) {
			result = extractVariable(arg1, arg2, predicate, variable);
		} else if (boolArg1 && !boolArg2) {
			result = extractVariable(arg2, arg1, predicate, variable);
		}
		return result;
	}

	/**
	 * Extract a value for the given variabe.
	 * 
	 * @param exprWithVariable
	 *            expression which contains the given variabe.
	 * @param exprWithoutVariable
	 *            expression which doesn't contain the given variabe.
	 * @param variable
	 *            the variable which should be eliminated.
	 * @return null if we can't find an equation for the given variable.
	 */
	public static IExpr extractVariable(IExpr exprWithVariable, IExpr exprWithoutVariable, Predicate predicate,
			ISymbol variable) {
		if (exprWithVariable.isAST()) {
			IAST ast = (IAST) exprWithVariable;
			if (ast.size() == 2) {
				IAST inverseFunction = InverseFunction.getInverseFunction(ast);
				if (inverseFunction != null) {
					// example: Sin(f(x)) == y -> f(x) == ArcSin(y)
					inverseFunction.add(exprWithoutVariable);
					return extractVariable(ast.arg1(), inverseFunction, predicate, variable);
				}
			} else {
				if (ast.isPlus()) {
					// a + b + c....
					IAST rest = F.Plus();
					IAST plusClone = ast.clone();
					int j = 1;
					for (int i = 1; i < ast.size(); i++) {
						if (ast.get(i).isFree(predicate, true)) {
							j++;
						} else {
							rest.add(ast.get(i));
							plusClone.remove(j);
						}
					}
					if (plusClone.size() == 1) {
						// no change for given expression
						return null;
					}
					IExpr value = F.Subtract(exprWithoutVariable, plusClone);
					return extractVariable(rest.getOneIdentity(F.C0), value, predicate, variable);
				} else if (ast.isTimes()) {
					// a * b * c....
					IAST rest = F.Times();
					IAST timesClone = ast.clone();
					int j = 1;
					for (int i = 1; i < ast.size(); i++) {
						if (ast.get(i).isFree(predicate, true)) {
							j++;
						} else {
							rest.add(ast.get(i));
							timesClone.remove(j);
						}
					}
					if (timesClone.size() == 1) {
						// no change for given expression
						return null;
					}
					IExpr value = F.Divide(exprWithoutVariable, timesClone);
					return extractVariable(rest.getOneIdentity(F.C1), value, predicate, variable);
				} else if (ast.isPower()) {
					if (ast.arg2().isFree(predicate, true)) {
						// f(x) ^ a
						IExpr value = F.Power(exprWithoutVariable, F.Divide(F.C1, ast.arg2()));
						return extractVariable(ast.arg1(), value, predicate, variable);
					} else if (ast.arg1().isFree(predicate, true)) {
						// a ^ f(x)
						IExpr value = F.Divide(F.Log(exprWithoutVariable), F.Log(ast.arg1()));
						return extractVariable(ast.arg2(), value, predicate, variable);
					}
				}
			}
		} else if (exprWithVariable.equals(variable)) {
			return exprWithoutVariable;
		}
		return null;
	}

	public Eliminate() {
	}

	/**
	 * Analyze the Equal() terms, if we find an expression which equals the given variabe
	 * 
	 * @param termsEqualZeroList
	 *            list of Equal() terms
	 * @param variable
	 *            the variable which should be eliminated.
	 * @return null if we can't eliminate an equation from the list for the given variabe
	 */
	public IAST eliminateOneVariable(ArrayList analyzerList, ISymbol variable) {
		IAST equalAST;
		IAST termsEqualZeroList = F.List();
		VariableCounterVisitor exprAnalyzer;
		for (int i = 0; i < analyzerList.size(); i++) {
			exprAnalyzer = analyzerList.get(i);
			equalAST = exprAnalyzer.getExpr();
			IExpr variableExpr = eliminateAnalyze(equalAST, variable);
			if (variableExpr != null) {
				variableExpr = F.eval(variableExpr);
				IExpr temp;
				IExpr expr;
				IAST rule = F.Rule(variable, variableExpr);
				analyzerList.remove(i);
				for (int j = 0; j < analyzerList.size(); j++) {
					expr = analyzerList.get(j).getExpr();
					temp = expr.replaceAll(rule);
					if (temp != null) {
						temp = F.evalExpandAll(temp);
						termsEqualZeroList.add(temp);
					} else {
						termsEqualZeroList.add(expr);
					}
				}
				return termsEqualZeroList;

			}
		}
		return null;
	}

	/** {@inheritDoc} */
	@Override
	public IExpr evaluate(final IAST ast) {
		Validate.checkSize(ast, 3);
		try {
			IAST vars = Validate.checkSymbolOrSymbolList(ast, 2);

			IAST termsEqualZeroList = checkEquations(ast, 1);

			IAST result = termsEqualZeroList;
			IAST temp;
			IAST equalAST;
			ISymbol variable;
			VariableCounterVisitor exprAnalyzer;
			for (int i = 1; i < vars.size(); i++) {
				variable = (ISymbol) vars.get(i);

				ArrayList analyzerList = new ArrayList();
				for (int j = 1; j < result.size(); j++) {
					equalAST = result.getAST(j);
					exprAnalyzer = new VariableCounterVisitor(equalAST, variable);
					equalAST.accept(exprAnalyzer);
					analyzerList.add(exprAnalyzer);
				}
				Collections.sort(analyzerList);

				temp = eliminateOneVariable(analyzerList, variable);
				if (temp != null) {
					result = temp;
				} else {
					return result;
				}
			}
			return result;
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return null;
	}
}

 

Ads help maintain this website.