/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jmathexpr.arithmetic.equation;
import jmathexpr.Constant;
import jmathexpr.Expression;
import jmathexpr.Precedence;
import jmathexpr.Variable;
import jmathexpr.arithmetic.ANumber;
import jmathexpr.arithmetic.Numbers;
import jmathexpr.arithmetic.Polynomial;
import jmathexpr.arithmetic.equation.rule.Fraction;
import jmathexpr.arithmetic.equation.rule.RuleMachine;
import jmathexpr.arithmetic.natural.Naturals;
import jmathexpr.arithmetic.op.Addition;
import jmathexpr.arithmetic.op.Division;
import jmathexpr.arithmetic.op.Multiplication;
import jmathexpr.arithmetic.op.Negation;
import jmathexpr.arithmetic.op.Subtraction;
import jmathexpr.arithmetic.op.Sum;
import jmathexpr.arithmetic.pattern.PolynomialTermPattern;
import jmathexpr.arithmetic.rule.AssociativeLaw;
import jmathexpr.arithmetic.rule.DistributiveLaw;
import jmathexpr.op.Operation;
import jmathexpr.relation.Equality;
import jmathexpr.set.Set;
import jmathexpr.util.pattern.ExpressionPattern;
import jmathexpr.util.rule.CompositeRule;
import jmathexpr.util.rule.SimpleRule;
import jmathexpr.util.rule.SubRule;
/**
* Linear equation in one unknown.
*
* @author Elemér Furka
*/
public class LinearEquation extends Equation {
/**
* Creates a new linear equation specifying the two sides of the equality.
*
* @param lhs left hand side of the equality
* @param rhs right hand side of the equality
* @param x the unknown in the equation
*/
public LinearEquation(Expression lhs, Expression rhs, Variable x) {
super(lhs, rhs, x);
}
/**
* Creates a new linear equation instance using an existing equality.
*
* @param equality the equality to be transformed into an equation
* @param x the unknown in the equation
*/
public LinearEquation(Equality equality, Variable x) {
this(equality.lhs(), equality.rhs(), x);
}
@Override
protected LinearEquation create(Expression lhs, Expression rhs) {
return new LinearEquation(lhs, rhs, x);
}
@Override
public Set solve() throws EquationSolveException {
rules = new RuleMachine(this);
rules.addRule(new Fraction());
rules.addRule(new DistributiveLaw());
rules.addRule(new AssociativeLaw());
rules.addRule(new VariableOnBothSides());
rules.addRule(new AxpBeC());
rules.addRule(new AxeB());
Set result = rules.execute();
result = check(result);
return result;
}
/**
* Tests whether the given expression could be converted into a linear
* equation in the given indeterminate.
*
* @param expr an arbitrary expression
* @param x the indeterminate
* @return true iff the given expression is an equation that only contains
* linear terms
*/
public static boolean isConvertible(Expression expr, Variable x) {
if (!(expr instanceof Equality)) {
return false;
}
Expression lhs = ((Equality) expr).lhs();
Expression rhs = ((Equality) expr).rhs();
boolean lhsIsLinear = isLinear(lhs, x);
boolean rhsIsLinear = isLinear(rhs, x);
return (lhsIsLinear && (rhs.isConstant() || rhsIsLinear))
|| (rhsIsLinear && (lhs.isConstant() || lhsIsLinear));
}
public static boolean isLinear(Expression expr, Variable x) {
ExpressionPattern pattern = new PolynomialTermPattern(x, 1);
if (pattern.matches(expr)) {
return true;
} else if (expr instanceof Negation) {
return isLinear(((Negation) expr).getChild(), x);
} else if (expr instanceof Division) {
Expression lhs = ((Division) expr).lhs();
Expression rhs = ((Division) expr).rhs();
return rhs.isConstant() && isLinear(lhs, x);
} else if (expr instanceof Operation
&& expr.getPrecedence() == Precedence.Addition) {
boolean isLinear = false;
for (Expression t : ((Operation) expr).operands()) {
if (isLinear(t, x)) {
isLinear = true;
} else if (!t.isConstant()) {
return false;
}
}
return isLinear;
} else if (expr instanceof Polynomial) {
return ((Polynomial) expr).degree().equals(Naturals.one());
}
return false;
}
/**
* The special case ax + b = cx + d -> b = (c-a)x + d or (a-c)x + b = d.
*/
private class VariableOnBothSides extends SimpleRule {
private final PolynomialTermPattern ax = new PolynomialTermPattern(x, 1);
private final Constant b = Numbers.constant("b");
private final PolynomialTermPattern cx = new PolynomialTermPattern(x, 1);
private final Constant d = Numbers.constant("d");
@Override
public boolean matches(Expression expr) {
target = expr;
return new Equality(new Sum(ax, b), new Sum(cx, d)).matches(expr);
}
@Override
public Expression apply() {
ANumber ca = (ANumber) ax.coefficient();
ANumber cc = (ANumber) cx.coefficient();
ANumber coef = ca.lt(cc) ? ca : cc;
if (coef.isNegative()) {
coef = coef.negate();
Expression cx = coef.isOne() ? x.evaluate() : new Multiplication(coef, x.evaluate());
return new Equality(Sum.add(((Equality) target).lhs(), cx),
Sum.add(((Equality) target).rhs(), cx));
} else {
Expression cx = coef.isOne() ? x.evaluate() : new Multiplication(coef, x.evaluate());
return new Equality(Sum.subtract(((Equality) target).lhs(), cx),
Sum.subtract(((Equality) target).rhs(), cx));
}
}
}
/**
* The special case ax +- b = c.
*/
private class AxpBeC extends CompositeRule {
private final PolynomialTermPattern ax = new PolynomialTermPattern(x, 1);
private final Constant b = Numbers.constant("b");
private final Constant c = Numbers.constant("c");
private AxpBeC() {
super(false);
}
@Override
public boolean matches(Expression expr) {
target = expr;
if ((rule = new AdditionC()).matches(expr)) return true;
if ((rule = new SubtractionC()).matches(expr)) return true;
if ((rule = new SubtractionX()).matches(expr)) return true;
return false;
}
/**
* ax + b = c /-b
*/
private class AdditionC extends SubRule {
@Override
public boolean matches(Expression expr) {
return new Equality(new Addition(ax, b), c).matches(expr);
}
@Override
public Expression apply() {
ANumber term = (ANumber) b.hit();
return new Equality(Sum.subtract(((Equality) target).lhs(), term),
Sum.subtract(((Equality) target).rhs(), term));
}
}
/**
* ax - b = c /+b
*/
private class SubtractionC extends SubRule {
@Override
public boolean matches(Expression expr) {
return new Equality(new Subtraction(ax, b), c).matches(expr);
}
@Override
public Expression apply() {
Expression term = b.hit();
return new Equality(Sum.add(((Equality) target).lhs(), term),
Sum.add(((Equality) target).rhs(), term));
}
}
/**
* b - ax = c /+ax
*/
private class SubtractionX extends SubRule {
@Override
public boolean matches(Expression expr) {
return new Equality(new Subtraction(b, ax), c).matches(expr);
}
@Override
public Expression apply() {
Expression var = ax.hit();
return new Equality(Sum.add(((Equality) target).lhs(), var),
Sum.add(((Equality) target).rhs(), var));
}
}
}
/**
* The special case ax = b.
*/
private class AxeB extends SimpleRule {
private final Constant a = Numbers.constant("a");
private final Constant b = Numbers.constant("b");
@Override
public boolean matches(Expression expr) {
target = expr;
Expression ax = new Multiplication(a, x);
boolean matches = new Equality(ax, b).matches(expr) || new Equality(b, ax).matches(expr);
// Don't match the case x = b (a = 1)
matches = matches && !a.hit().equals(Naturals.one());
return matches;
}
@Override
public Expression apply() {
Expression coef = a.hit();
return new Equality(new Division(((Equality) target).lhs(), coef),
new Division(((Equality) target).rhs(), coef));
}
}
}