/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jmathexpr.arithmetic.op;
import jmathexpr.Expression;
import jmathexpr.Precedence;
import jmathexpr.arithmetic.ANumber;
import jmathexpr.arithmetic.Polynomial;
import jmathexpr.arithmetic.func.Sqrt;
import jmathexpr.arithmetic.integer.Integers;
import jmathexpr.arithmetic.natural.Naturals;
import jmathexpr.arithmetic.real.Reals;
import jmathexpr.op.BinaryOperation;
import jmathexpr.op.Operation;
import jmathexpr.op.Sign;
import jmathexpr.set.Set;
import jmathexpr.set.op.CartesianProduct;
import jmathexpr.util.logging.Logger;
import jmathexpr.util.pattern.ExpressionPattern;
/**
*
* @author Elemér Furka
*/
public class Multiplication extends BinaryOperation {
public Multiplication(Expression lhs, Expression rhs) {
super(lhs, rhs, Sign.Multiplication);
}
@Override
public Expression evaluate() {
Expression simplified = simplify();
if (!(simplified instanceof Multiplication)) return simplified;
Expression l = ((Multiplication) simplified).lhs;
Expression r = ((Multiplication) simplified).rhs;
if (l instanceof ANumber && r instanceof ANumber) {
return ((ANumber) l).multiply((ANumber) r);
} else if (l instanceof Polynomial) {
if (r.isConstant()) {
return ((Polynomial) l).multiply(r);
}
}
return new Multiplication(l, r);
}
private Expression simplify() {
Expression l = lhs.evaluate();
Expression r = rhs.evaluate();
if (l instanceof ANumber && ((ANumber)l).isZero()) {
return Naturals.zero();
} else if (r instanceof ANumber && ((ANumber)r).isZero()) {
return Naturals.zero();
} else if (l instanceof ANumber && ((ANumber)l).isOne()) {
return r;
} else if (r instanceof ANumber && ((ANumber)r).isOne()) {
return l;
}
Expression two = Naturals.getInstance().create(2);
if (l instanceof ANumber) {
ANumber a = (ANumber) l;
if (a.negate().isOne()) {
return new Negation(r).evaluate();
} else if (r instanceof Division) {
Division d = (Division) r;
if (d.rhs() instanceof ANumber) { // a x/b = a/b x
l = a.divide((ANumber) d.rhs());
r = d.lhs();
}
} else if (r instanceof Multiplication) {
Multiplication m = (Multiplication) r;
if (m.lhs instanceof ANumber) { // a (bx) = (ab) x
l = a.multiply((ANumber) m.lhs);
r = m.rhs;
} else if (m.rhs instanceof ANumber) { // a (xb) = (ab) x
l = a.multiply((ANumber) m.rhs);
r = m.lhs;
}
} else if (r instanceof Negation) { // a (-x) = -ax
l = a.negate();
r = ((Negation) r).getChild();
return new Multiplication(l, r).evaluate();
} else if (a.isNegative()) { // -a x = -(ax)
return new Negation(new Multiplication(a.negate(), r)).evaluate();
}
} else if (l instanceof Negation) { // (-a)b = -(ab)
return new Negation(new Multiplication(((Negation) l).getChild(), r)).evaluate();
} else if (l instanceof Multiplication) { // (ab) c
Multiplication m = (Multiplication) l;
if (m.rhs instanceof Sqrt && r instanceof Sqrt) {
l = m.lhs;
r = new Multiplication(m.rhs, r).evaluate();
} else if (r instanceof ANumber) { // (ax) c = (ac) x
l = new Multiplication(m.lhs, r).evaluate();
r = m.rhs;
}
} else if (r instanceof Division) {
Division d = (Division) r;
if (l.equals(d.rhs())) { // a x/a = x
if (d.rhs() instanceof ANumber && ((ANumber) d.rhs()).isZero()) {
throw new ArithmeticException("Division by zero: " + this);
}
return d.lhs();
} else {
return new Division(new Multiplication(l, d.lhs()), d.rhs()).evaluate();
}
} else if (r instanceof Multiplication) { // a (b c)
Multiplication m = (Multiplication) r;
if (l.equals(m.lhs)) { // a (a c) = a^2 c
l = new Exponentiation(l, two).evaluate();
r = m.rhs;
} else if (l.equals(m.rhs)) { // a (b a) = a^2 b
l = new Exponentiation(l, two).evaluate();
r = m.lhs;
} else if (l instanceof Negation) {
if (m.lhs instanceof ANumber) { // -a (nx) = -n * ax
r = new Multiplication(l, m.rhs).evaluate();
l = ((ANumber) m.lhs).negate();
}
} else if (m.lhs instanceof ANumber) {
r = new Multiplication(l, m.rhs).evaluate();
l = m.lhs;
}
} else if (r instanceof ANumber) {
ANumber b = (ANumber) r;
if (l instanceof Multiplication) {
Multiplication m = (Multiplication) l;
if (m.rhs instanceof ANumber) { // (xa) b = x (ab)
l = m.lhs;
r = ((ANumber) m.rhs).multiply(b);
} else if (m.lhs instanceof ANumber) { // (ax) b = (ab) x
l = ((ANumber) m.lhs).multiply(b);
r = m.rhs;
}
}
} else if (r instanceof Negation) { // a (-x) = -(ax)
return new Negation(new Multiplication(l, ((Negation) r).getChild())).evaluate();
}
if (r instanceof Operation && r.getPrecedence() == Precedence.Addition) { // a(b+c) = ab+ac
return applyDistribution(l, (Operation) r);
} else if (l instanceof Operation && l.getPrecedence() == Precedence.Addition) { // (a+b)c = ac+bc
return applyDistribution(r, (Operation) l);
}
if (l.equals(r)) { // a * a = a^2
return new Exponentiation(l, Naturals.getInstance().create(2)).evaluate();
}
if (l instanceof Sqrt && r instanceof Sqrt) {
return new Sqrt(new Multiplication(((Sqrt) l).argument(), ((Sqrt) r).argument())).evaluate();
}
return new Multiplication(l, r);
}
private Expression applyDistribution(Expression lhs, Operation rhs) {
if (rhs instanceof Addition) {
return ((Addition) rhs).multiply(lhs).evaluate();
} else if (rhs instanceof Subtraction) {
return ((Subtraction) rhs).multiply(lhs).evaluate();
} else if (rhs instanceof Sum) {
return ((Sum) rhs).multiply(lhs).evaluate();
} else {
throw new IllegalStateException(String.format("Unexpected operation: %s (%s)", rhs, rhs.getClass()));
}
}
@Override
public Precedence getPrecedence() {
return Precedence.Multiplication;
}
@Override
public String toString() {
if (rhs instanceof ANumber) {
return super.toString();
}
String l = lhs.getPrecedence().le(getPrecedence()) ? String.format("(%s)", lhs)
: lhs.toString();
String r = rhs.getPrecedence().le(getPrecedence()) ? String.format("(%s)", rhs)
: rhs.toString();
return l + r;
}
@Override
public Set domain() {
return new CartesianProduct(Reals.getInstance(), Reals.getInstance());
}
@Override
public Set codomain() {
return Reals.getInstance();
}
@Override
public boolean matches(Expression expr) {
if (super.matches(expr)) return true;
if (((ExpressionPattern) lhs).matches(Integers.getInstance().create(-1))) {
if (expr instanceof Negation) { // ax matches -x
Negation neg = (Negation) expr;
if (((ExpressionPattern) rhs).matches(neg.getChild())) {
return true;
}
}
}
//
// if (((ExpressionPattern) lhs).matches(Naturals.one())) {
// if (((ExpressionPattern) rhs).matches(expr)) { // ax matches x
// return true;
// }
// }
if (expr instanceof Polynomial) {
return ((Polynomial) expr).matches(this);
}
return false;
}
@Override
protected BinaryOperation create(Expression lhs, Expression rhs) {
return new Multiplication(lhs, rhs);
}
@Override
public boolean isCommutative() {
return true;
}
}