/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jmathexpr.arithmetic.equation;
import java.util.ArrayList;
import java.util.List;
import jmathexpr.Expression;
import jmathexpr.Precedence;
import jmathexpr.Variable;
import jmathexpr.arithmetic.ANumber;
import jmathexpr.arithmetic.Numbers;
import jmathexpr.arithmetic.Polynomial;
import static jmathexpr.arithmetic.equation.LinearEquation.isLinear;
import jmathexpr.arithmetic.equation.rule.EquationRule;
import jmathexpr.arithmetic.equation.rule.Fraction;
import jmathexpr.arithmetic.equation.rule.RuleMachine;
import jmathexpr.arithmetic.func.Sqrt;
import jmathexpr.arithmetic.natural.NaturalNumber;
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.pattern.PolynomialTermPattern;
import jmathexpr.op.Operation;
import jmathexpr.relation.Equality;
import jmathexpr.set.EmptySet;
import jmathexpr.set.FiniteSet;
import jmathexpr.set.OrderedPair;
import jmathexpr.set.Set;
import jmathexpr.util.context.ExpressionContext;
import jmathexpr.util.pattern.AnyPattern;
import jmathexpr.util.pattern.ExpressionPattern;
import jmathexpr.util.pattern.TerminationPattern;
import jmathexpr.util.rule.SimpleRule;
/**
* Quadratic equation in one unknown.
*
* @author Elemér Furka
*/
public class QuadraticEquation extends Equation {
/**
* Creates a new quadratic 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 QuadraticEquation(Expression lhs, Expression rhs, Variable x) {
super(lhs, rhs, x);
}
/**
* Creates a new quadratic equation instance using an existing equality.
*
* @param equality the equality to be transformed into an equation
* @param x the unknown in the equation
*/
public QuadraticEquation(Equality equality, Variable x) {
this(equality.lhs(), equality.rhs(), x);
}
@Override
protected QuadraticEquation create(Expression lhs, Expression rhs) {
return new QuadraticEquation(lhs, rhs, x);
}
@Override
public Set solve() throws EquationSolveException {
rules = new RuleMachine(this);
rules.addRule(new Fraction());
rules.addRule(new Canonical());
rules.addRule(new RootFreeA());
rules.addRule(new PlusMinus());
rules.addRule(new Factor());
rules.addRule(new ZeroProduct());
rules.addRule(new QuadraticFormula());
Set result = rules.execute();
result = check(result);
return result;
}
/**
* Tests whether the given expression could be converted into a quadratic
* 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
* quadratic, linear or constant 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 lhsIsQuadratic = isQuadratic(lhs, x);
boolean rhsIsQuadratic = isQuadratic(rhs, x);
return (lhsIsQuadratic
&& (rhs.isConstant() || LinearEquation.isLinear(rhs, x) || rhsIsQuadratic))
|| (rhsIsQuadratic
&& (lhs.isConstant() || LinearEquation.isLinear(lhs, x) || lhsIsQuadratic));
}
private static boolean isQuadratic(Expression expr, Variable x) {
NaturalNumber two = Naturals.getInstance().create(2);
ExpressionPattern pattern = new PolynomialTermPattern(x, 2);
if (pattern.matches(expr)) {
return true;
} else if (expr instanceof Negation) {
return isQuadratic(((Negation) expr).getChild(), x);
} else if (expr instanceof Division) {
Expression lhs = ((Division) expr).lhs();
Expression rhs = ((Division) expr).rhs();
return rhs.isConstant() && isQuadratic(lhs, x);
} else if (expr instanceof Operation
&& expr.getPrecedence() == Precedence.Addition) {
boolean isQuadratic = false;
for (Expression t : ((Operation) expr).operands()) {
if (isQuadratic(t, x)) {
isQuadratic = true;
} else if (!(t.isConstant() || isLinear(t, x))) {
return false;
}
}
return isQuadratic;
} else if (expr instanceof Polynomial) {
return ((Polynomial) expr).degree().equals(two);
}
return false;
}
private class Canonical extends SimpleRule {
@Override
public boolean matches(Expression expr) {
target = expr;
return true;
}
@Override
public Expression apply() {
Polynomial pl = Polynomial.create(((Equality) target).lhs(), x);
Polynomial pr = Polynomial.create(((Equality) target).rhs(), x);
Polynomial reduced;
if (pl.degree().lt(pr.degree())) {
reduced = pr.subtract(pl);
} else {
reduced = pl.subtract(pr);
}
Expression a = reduced.leadCoefficient();
if (a instanceof ANumber && ((ANumber) a).isNegative()) {
reduced = reduced.negate();
}
return new Equality(reduced, Naturals.zero());
}
}
/**
* sqrt(a) x^2 + bx + c = 0 / * sqrt(a)
*/
private class RootFreeA extends SimpleRule {
private Polynomial p;
private final AnyPattern arg = new AnyPattern();
@Override
public boolean matches(Expression expr) {
p = (Polynomial) ((Equality) expr).lhs();
Expression a = p.leadCoefficient();
return new Sqrt(arg).matches(a);
}
@Override
public Expression apply() {
return new Equality(new Multiplication(p, new Sqrt(arg.hit())), Naturals.zero());
}
}
/**
* ax^2 - c = 0 -> x = +-sqrt(c/a)
*/
private class PlusMinus extends SimpleRule {
private Polynomial p;
@Override
public boolean matches(Expression expr) {
p = (Polynomial) ((Equality) expr).lhs();
Expression linear = p.getCoefficient(1);
return linear instanceof ANumber && ((ANumber) linear).isZero();
}
@Override
public Expression apply() {
Expression a = p.getCoefficient(2);
Expression c = p.getCoefficient(0);
if (c instanceof ANumber) {
if (((ANumber) c).isZero()) {
Expression x1 = Naturals.zero();
ExpressionContext.getInstance().addExpression(new Equality(x, x1));
return new FiniteSet(x1);
} else if (!((ANumber) c).isNegative()) {
return new EmptySet();
}
}
Expression x1 = new Sqrt(new Division(new Negation(c), a));
Expression x2 = new Negation(x1);
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 1), x1));
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 2), x2));
x1 = x1.evaluate();
x2 = x2.evaluate();
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 1), x1));
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 2), x2));
return new FiniteSet(x1, x2);
}
}
/**
* ax^2 + bx = (ax + b)x
*/
private class Factor extends SimpleRule {
private Polynomial p;
@Override
public boolean matches(Expression expr) {
p = (Polynomial) ((Equality) expr).lhs();
Expression constant = p.getTerm(Naturals.zero());
return constant instanceof ANumber && ((ANumber) constant).isZero();
}
@Override
public Expression apply() {
OrderedPair factor = p.euclideanDivision(Polynomial.create(x, x));
return new Equality(new Multiplication(factor.a(), x), Naturals.zero());
}
}
/**
* a * b = 0 -> a = 0 or b = 0
*/
private class ZeroProduct extends SimpleRule implements EquationRule {
private final TerminationPattern a = new AnyPattern();
private final TerminationPattern b = new AnyPattern();
@Override
public boolean matches(Expression expr) {
return new Equality(new Multiplication(a, b), Naturals.zero()).matches(expr);
}
@Override
public Expression apply() {
throw new UnsupportedOperationException("Call convertedEquations() instead!");
}
@Override
public List<Equation> convertedEquations() {
List<Equation> equations = new ArrayList();
equations.add(new LinearEquation(a.hit(), Naturals.zero(), x));
equations.add(new LinearEquation(b.hit(), Naturals.zero(), x));
return equations;
}
}
/**
* x1,2 = (-b +- sqrt(b^2 - 4ac))/2a
*/
private class QuadraticFormula extends SimpleRule {
private Polynomial p;
@Override
public boolean matches(Expression expr) {
p = (Polynomial) ((Equality) expr).lhs();
return true;
}
@Override
public Expression apply() {
Expression dd = p.discriminant();
ExpressionContext.getInstance().addExpression(new Equality(Numbers.constant("D"), dd));
dd = dd.evaluate();
ExpressionContext.getInstance().addExpression(new Equality(Numbers.constant("D"), dd));
Expression a = p.getCoefficient(2);
Expression b = p.getCoefficient(1);
Expression c = p.getCoefficient(0);
NaturalNumber two = Naturals.getInstance().create(2);
if (dd instanceof ANumber) {
ANumber d = (ANumber) dd;
if (d.isNegative()) {
return new EmptySet();
} else if (d.isZero()) {
return new FiniteSet(new Division(new Negation(b),
new Multiplication(two, a)).evaluate());
}
}
Expression sd = new Sqrt(dd);
Expression x1 = new Division(
new Addition(new Negation(b), sd),
new Multiplication(two, a));
Expression x2 = new Division(
new Subtraction(new Negation(b), sd),
new Multiplication(two, a));
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 1), x1));
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 2), x2));
x1 = x1.evaluate();
x2 = x2.evaluate();
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 1), x1));
ExpressionContext.getInstance().addExpression(new Equality(Numbers.variable("x", 2), x2));
return new FiniteSet(x1, x2);
}
}
}