package org.matheclipse.core.reflection.system;

import java.util.HashMap;
import java.util.Map;

import org.matheclipse.core.basic.Config;
import org.matheclipse.core.eval.exception.Validate;
import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.ISymbol;
import org.matheclipse.core.patternmatching.IPatternMatcher;
import org.matheclipse.core.patternmatching.PatternMatcher;

/**
 * Collect expressions.
 */
public class Collect extends AbstractFunctionEvaluator {

	public Collect() {
	}

	@Override
	public IExpr evaluate(final IAST ast) {
		Validate.checkSize(ast, 3);
		try {
			if (!ast.get(2).isList()) {
				IExpr arg1 = F.evalExpandAll(ast.arg1());
				IExpr arg2 = ast.arg2();
				return collectSingleVariable(arg1, arg2, F.List(), 1);
			}
			IExpr arg1 = F.evalExpandAll(ast.arg1());
			IAST list = (IAST) ast.get(2);
			if (list.size() > 1) {
				return collectSingleVariable(arg1, list.arg1(), (IAST) ast.arg2(), 2);
			}
		} catch (Exception e) {
			if (Config.SHOW_STACKTRACE) {
				e.printStackTrace();
			}
		}
		return null;
	}

	public IExpr collectSingleVariable(IExpr arg1, IExpr arg2, final IAST list, final int pos) {
		if (arg1.isAST()) {
			Map map = new HashMap();
			IAST poly = (IAST) arg1;
			IAST rest = F.Plus();

			IPatternMatcher matcher = new PatternMatcher(arg2);
			collectToMap(poly, matcher, map, rest);
			if (pos < list.size()) {
				// collect next pattern in sub-expressions
				IAST result = F.Plus();
				if (rest.size() > 1) {
					result.add(collectSingleVariable(rest, list.get(pos), list, pos + 1));
				}
				for (IExpr key : map.keySet()) {
					IAST value = map.get(key);
					IExpr temp = collectSingleVariable(value.getOneIdentity(F.C0), list.get(pos), list, pos + 1);
					result.add(F.Times(key, temp));
				}
				return result;
			}

			for (IExpr key : map.keySet()) {
				IAST value = map.get(key);
				rest.add(F.Times(key).addOneIdentity(value));
			}
			return rest.getOneIdentity(F.C0);
		}
		return arg1;
	}

	public void collectToMap(IExpr expr, IPatternMatcher matcher, Map map, IAST rest) {
		if (expr.isFree(matcher, false)) {
			rest.add(expr);
			return;
		} else if (matcher.apply(expr)) {
			addPowerFactor(expr, F.C1, map);
			return;
		} else if (isPowerMatched(expr, matcher)) {
			addPowerFactor((IAST) expr, F.C1, map);
			return;
		} else if (expr.isPlus()) {
			IAST plusAST = (IAST) expr;
			IAST clone = plusAST.clone();
			int i = 1;
			while (i < clone.size()) {
				if (collectToMapPlus(clone.get(i), matcher, map)) {
					clone.remove(i);
				} else {
					i++;
				}
			}
			if (clone.size() > 1) {
				rest.addOneIdentity(clone);
			}
			return;
		} else if (expr.isTimes()) {
			IAST timesAST = (IAST) expr;
			for (int i = 1; i < timesAST.size(); i++) {
				if (matcher.apply(timesAST.get(i)) || isPowerMatched(timesAST.get(i), matcher)) {
					IAST clone = timesAST.clone();
					clone.remove(i);
					addOneIdentityPowerFactor(timesAST.get(i), clone, map);
					return;
				}
			}
			rest.add(expr);
			return;
		}
		rest.add(expr);
		return;
	}

	public boolean collectToMapPlus(IExpr expr, IPatternMatcher matcher, Map map) {
		if (expr.isFree(matcher, false)) {
			return false;
		} else if (matcher.apply(expr)) {
			addPowerFactor(expr, F.C1, map);
			return true;
		} else if (isPowerMatched(expr, matcher)) {
			addPowerFactor(expr, F.C1, map);
			return true;
		} else if (expr.isTimes()) {
			IAST timesAST = (IAST) expr;
			for (int i = 1; i < timesAST.size(); i++) {
				if (matcher.apply(timesAST.get(i)) || isPowerMatched(timesAST.get(i), matcher)) {
					IAST clone = timesAST.clone();
					clone.remove(i);
					addOneIdentityPowerFactor(timesAST.get(i), clone, map);
					return true;
				}
			}
		}

		return false;
	}

	public void addOneIdentityPowerFactor(IExpr key, IAST subAST, Map map) {
		IAST resultList = map.get(key);
		if (resultList == null) {
			resultList = F.Plus();
			map.put(key, resultList);
		}
		resultList.addOneIdentity(subAST);
	}

	public void addPowerFactor(IExpr key, IExpr value, Map map) {
		IAST resultList = map.get(key);
		if (resultList == null) {
			resultList = F.Plus();
			map.put(key, resultList);
		}
		resultList.add(value);
	}

	public boolean isPowerMatched(IExpr poly, IPatternMatcher matcher) {
		return poly.isPower() && ((IAST) poly).get(2).isNumber() && matcher.apply(((IAST) poly).arg1());
	}

	@Override
	public void setUp(final ISymbol symbol) {
	}

}

 

Ads help maintain this website.