package org.matheclipse.core.reflection.system;

import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;

import org.matheclipse.core.basic.Config;
import org.matheclipse.core.convert.ExprVariables;
import org.matheclipse.core.convert.JASConvert;
import org.matheclipse.core.eval.exception.JASConversionException;
import org.matheclipse.core.eval.exception.Validate;
import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator;
import org.matheclipse.core.expression.ASTRange;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.IInteger;
import org.matheclipse.core.interfaces.IRational;
import org.matheclipse.core.interfaces.ISignedNumber;
import org.matheclipse.core.interfaces.ISymbol;
import org.matheclipse.core.polynomials.IPartialFractionGenerator;
import org.matheclipse.core.polynomials.PartialFractionGenerator;

import edu.jas.arith.BigInteger;
import edu.jas.arith.BigRational;
import edu.jas.poly.GenPolynomial;
import edu.jas.ufd.FactorAbstract;
import edu.jas.ufd.FactorFactory;
import edu.jas.ufd.SquarefreeAbstract;
import edu.jas.ufd.SquarefreeFactory;

/**
 * Evaluate the partial fraction decomposition of a univariate polynomial fraction.
 * 
 * See Wikipedia - Partial fraction decomposition
 */
public class Apart extends AbstractFunctionEvaluator {

	public Apart() {
	}

	@Override
	public IExpr evaluate(final IAST ast) {
		Validate.checkRange(ast, 2, 3);
		IAST variableList = null;
		if (ast.size() == 3) {
			variableList = Validate.checkSymbolOrSymbolList(ast, 2);
		} else {
			ExprVariables eVar = new ExprVariables(ast.arg1());
			if (!eVar.isSize(1)) {
				// partial fraction only possible for univariate polynomials
				return null;
			}
			variableList = eVar.getVarList();
		}

		final IExpr arg = ast.arg1();
		if (arg.isTimes() || arg.isPower()) {
			IExpr[] parts = Apart.getFractionalParts(ast.arg1());
			if (parts != null) {
				IAST plusResult = partialFractionDecompositionRational(new PartialFractionGenerator(), parts,
						(ISymbol) variableList.arg1());
				if (plusResult != null) {
					return plusResult.getOneIdentity(F.C0);
				}
			}
		} else {
			return ast.arg1();
		}

		return null;
	}

	/**
	 * Returns an AST with head Plus, which contains the partial fraction decomposition of the numerator and
	 * denominator parts.
	 * 
	 * @deprecated untested at the moment
	 * @param parts
	 * @param variableList
	 * @return null if the partial fraction decomposition wasn't constructed
	 */
	public static IAST partialFractionDecompositionInteger(IExpr[] parts, IAST variableList) {
		try {
			IExpr exprNumerator = F.evalExpandAll(parts[0]);
			IExpr exprDenominator = F.evalExpandAll(parts[1]);
			ASTRange r = new ASTRange(variableList, 1);
			List varList = r.toList();

			String[] varListStr = new String[1];
			varListStr[0] = variableList.arg1().toString();
			JASConvert jas = new JASConvert(varList, BigInteger.ZERO);
			GenPolynomial numerator = jas.expr2JAS(exprNumerator);
			GenPolynomial denominator = jas.expr2JAS(exprDenominator);

			// get factors
			FactorAbstract factorAbstract = FactorFactory.getImplementation(BigInteger.ZERO);
			SortedMap, Long> sfactors = factorAbstract.baseFactors(denominator);

			List> D = new ArrayList>(sfactors.keySet());

			SquarefreeAbstract sqf = SquarefreeFactory.getImplementation(BigInteger.ZERO);
			List>> Ai = sqf.basePartialFraction(numerator, sfactors);
			// returns [ [Ai0, Ai1,..., Aie_i], i=0,...,k ] with A/prod(D) =
			// A0 + sum( sum ( Aij/di^j ) ) with deg(Aij) < deg(di).

			if (Ai.size() > 0) {
				IAST result = F.Plus();
				IExpr temp;
				if (!Ai.get(0).get(0).isZERO()) {
					temp = F.eval(jas.integerPoly2Expr(Ai.get(0).get(0)));
					if (temp.isAST()) {
						((IAST) temp).addEvalFlags(IAST.IS_DECOMPOSED_PARTIAL_FRACTION);
					}
					result.add(temp);
				}
				for (int i = 1; i < Ai.size(); i++) {
					List> list = Ai.get(i);
					long j = 0L;
					for (GenPolynomial genPolynomial : list) {
						if (!genPolynomial.isZERO()) {
							temp = F.eval(F.Times(jas.integerPoly2Expr(genPolynomial),
									F.Power(jas.integerPoly2Expr(D.get(i - 1)), F.integer(j * (-1L)))));
							if (!temp.equals(F.C0)) {
								if (temp.isAST()) {
									((IAST) temp).addEvalFlags(IAST.IS_DECOMPOSED_PARTIAL_FRACTION);
								}
								result.add(temp);
							}
						}
						j++;
					}

				}
				return result;
			}
		} catch (JASConversionException e) {
			if (Config.DEBUG) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * Returns an AST with head Plus, which contains the partial fraction decomposition of the numerator and
	 * denominator parts.
	 * 
	 * @param parts
	 * @param variableList
	 * @return null if the partial fraction decomposition wasn't constructed
	 */
	public static IAST partialFractionDecompositionRational(IPartialFractionGenerator pf, IExpr[] parts, ISymbol x) {
		try {
			IAST variableList = F.List(x);
			IExpr exprNumerator = F.evalExpandAll(parts[0]);
			IExpr exprDenominator = F.evalExpandAll(parts[1]);
			ASTRange r = new ASTRange(variableList, 1);
			List varList = r.toList();

			String[] varListStr = new String[1];
			varListStr[0] = variableList.arg1().toString();
			JASConvert jas = new JASConvert(varList, BigRational.ZERO);
			GenPolynomial numerator = jas.expr2JAS(exprNumerator);
			GenPolynomial denominator = jas.expr2JAS(exprDenominator);

			// get factors
			FactorAbstract factorAbstract = FactorFactory.getImplementation(BigRational.ZERO);
			SortedMap, Long> sfactors = factorAbstract.baseFactors(denominator);

			List> D = new ArrayList>(sfactors.keySet());

			SquarefreeAbstract sqf = SquarefreeFactory.getImplementation(BigRational.ZERO);
			List>> Ai = sqf.basePartialFraction(numerator, sfactors);
			// returns [ [Ai0, Ai1,..., Aie_i], i=0,...,k ] with A/prod(D) =
			// A0 + sum( sum ( Aij/di^j ) ) with deg(Aij) < deg(di).

			if (Ai.size() > 0) {
				// IAST result = F.Plus();
				pf.setJAS(jas);
				if (!Ai.get(0).get(0).isZERO()) {
					pf.addNonFractionalPart(Ai.get(0).get(0));
				}
				for (int i = 1; i < Ai.size(); i++) {
					List> list = Ai.get(i);
					int j = 0;
					for (GenPolynomial genPolynomial : list) {
						if (!genPolynomial.isZERO()) {
							GenPolynomial Di_1 = D.get(i - 1);
							pf.addSinglePartialFraction(genPolynomial, Di_1, j);
						}
						j++;
					}

				}
				return pf.getResult();
			}
		} catch (JASConversionException e) {
			if (Config.DEBUG) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * Split the expression into numerator and denominator parts, by separating positive and negative powers.
	 * 
	 * @param arg
	 * @return the numerator and denominator expression
	 */
	public static IExpr[] getFractionalParts(final IExpr arg) {
		IExpr[] parts = null;
		if (arg.isTimes()) {
			parts = Apart.getFractionalPartsTimes((IAST) arg, true);
		} else if (arg.isPower()) {
			IAST temp = (IAST) arg;
			if (temp.arg2().isSignedNumber()) {
				ISignedNumber sn = (ISignedNumber) temp.arg2();
				parts = new IExpr[2];
				if (sn.equals(F.CN1)) {
					parts[0] = F.C1;
					parts[1] = temp.arg1();
				} else if (sn.isNegative()) {
					parts[0] = F.C1;
					parts[1] = F.Power(temp.arg1(), sn.negate());
				} else {
					if (sn.isInteger() && temp.arg1().isAST()) {
						// positive integer
						IAST function = (IAST) temp.arg1();
						IAST denomForm = Denominator.getDenominatorForm(function);
						if (denomForm != null) {
							parts[0] = F.C1;
							parts[1] = F.Power(denomForm, sn);
							return parts;
						}

					}
					parts[0] = arg;
					parts[1] = F.C1;
				}
			}
		} else {
			if (arg.isAST()) {
				IAST denomForm = Denominator.getDenominatorForm((IAST) arg);
				if (denomForm != null) {
					parts = new IExpr[2];
					parts[0] = F.C1;
					parts[1] = denomForm;
					return parts;
				}
			}
			parts = new IExpr[2];
			parts[0] = arg;
			parts[1] = F.C1;
		}
		return parts;
	}

	/**
	 * Return the numerator and denominator for the given Times[...] AST, by separating positive and negative powers.
	 * 
	 * @param ast
	 *            a times expression (a*b*c....)
	 * @param splitFractionalNumbers
	 *            TODO
	 * @return the numerator and denominator expression
	 */
	public static IExpr[] getFractionalPartsTimes(final IAST ast, boolean splitFractionalNumbers) {
		IExpr[] result = new IExpr[2];

		IAST numerator = F.Times();
		IAST denominator = F.Times();
		IExpr arg;
		IAST argAST;
		for (int i = 1; i < ast.size(); i++) {
			arg = ast.get(i);
			if (arg.isAST()) {
				argAST = (IAST) arg;
				if (argAST.size() == 2) {
					IAST denomForm = Denominator.getDenominatorForm(argAST);
					if (denomForm != null) {
						denominator.add(denomForm);
						continue;
					}
				} else if (arg.isPower()) {
					if (argAST.arg2().isSignedNumber()) {
						ISignedNumber sn = (ISignedNumber) argAST.arg2();
						if (sn.equals(F.CN1)) {
							denominator.add(argAST.arg1());
							continue;
						}
						if (sn.isNegative()) {
							denominator.add(F.Power(argAST.arg1(), ((ISignedNumber) argAST.arg2()).negate()));
							continue;
						}
						if (sn.isInteger() && argAST.arg1().isAST()) {
							// positive integer
							IAST function = (IAST) argAST.arg1();
							IAST denomForm = Denominator.getDenominatorForm(function);
							if (denomForm != null) {
								denominator.add(F.Power(denomForm, sn));
								continue;
							}

						}
					}
				}
			} else if (splitFractionalNumbers && arg.isRational()) {
				IInteger numer = ((IRational) arg).getNumerator();
				if (!numer.equals(F.C1)) {
					numerator.add(numer);
				}
				IInteger denom = ((IRational) arg).getDenominator();
				if (!denom.equals(F.C1)) {
					denominator.add(denom);
				}
				continue;
			}
			numerator.add(arg);
		}
		result[0] = numerator.getOneIdentity(F.C1);
		result[1] = denominator.getOneIdentity(F.C1);
		return result;

	}
}
 

Ads help maintain this website.