/*
* 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.integer.IntegerNumber;
import jmathexpr.arithmetic.natural.NaturalNumber;
import jmathexpr.arithmetic.natural.Naturals;
import jmathexpr.arithmetic.natural.PrimeFactorization;
import jmathexpr.arithmetic.real.Reals;
import jmathexpr.op.BinaryOperation;
import jmathexpr.op.Sign;
import jmathexpr.set.FiniteSet;
import jmathexpr.set.Set;
import jmathexpr.set.op.CartesianProduct;
import jmathexpr.set.op.Difference;
import jmathexpr.util.logging.Logger;
/**
*
* @author Elemér Furka
*/
public class Division extends BinaryOperation {
public Division(Expression lhs, Expression rhs) {
super(lhs, rhs, Sign.Division);
}
@Override
public Expression evaluate() {
Expression simplified = simplify();
if (!(simplified instanceof Division)) return simplified;
Expression l = ((Division) simplified).lhs;
Expression r = ((Division) simplified).rhs;
if (l instanceof ANumber && r instanceof ANumber) {
if (((ANumber) r).isZero()) {
throw new IllegalArgumentException("Division by 0");
} else {
return ((ANumber) l).divide((ANumber) r);
}
} else if (l instanceof Multiplication && r instanceof ANumber) { // simplification of a*b/c
Multiplication dividend = (Multiplication) l;
if (dividend.lhs() instanceof ANumber) {
return new Multiplication(new Division(dividend.lhs(), r), dividend.rhs()).evaluate();
} else if (dividend.rhs() instanceof ANumber) {
return new Multiplication(dividend.lhs(), new Division(dividend.rhs(), r)).evaluate();
}
} else if (l instanceof Multiplication) {
Multiplication dividend = (Multiplication) l;
if (dividend.lhs().equals(r)) { // simplification of a*b/a
return dividend.rhs();
} else if (dividend.rhs().equals(r)) { // simplification of b*a/a
return dividend.lhs();
}
} else if (l instanceof Polynomial) {
return ((Polynomial) l).divide(r);
}
return new Division(l, r);
}
private Expression simplify() {
Expression l = lhs.evaluate();
Expression r = rhs.evaluate();
if (l.equals(r) && !(r instanceof ANumber && ((ANumber) r).isZero())) { // a/a = 1 (a != 0)
return Naturals.one();
}
if (l instanceof Multiplication) {
Multiplication dividend = (Multiplication) l;
if (dividend.lhs().equals(r)) { // simplification of a*b/a
return dividend.rhs();
} else if (dividend.rhs().equals(r)) { // simplification of b*a/a
return dividend.lhs();
}
} else if (l instanceof Division) { // (a / b) / c = a / (bc)
Expression a = ((Division) l).lhs();
Expression b = ((Division) l).rhs();
return new Division(a, new Multiplication(b, r)).evaluate();
} else if (l instanceof Negation) { // -(a)/b = -(a/b)
return new Negation(new Division(((Negation) l).getChild(), r)).evaluate();
} else if (l instanceof Addition || l instanceof Subtraction) { // simplifying (a +- b) / r
if (r instanceof NaturalNumber) {
return simplifyAdditive((BinaryOperation) l, (NaturalNumber) r);
} else if (r instanceof IntegerNumber) {
if (!((IntegerNumber) r).isNegative()) {
NaturalNumber n = ((IntegerNumber) r).toNatural();
return simplifyAdditive((BinaryOperation) l, n);
}
}
}
return new Division(l, r);
}
private Expression simplifyAdditive(BinaryOperation sum, NaturalNumber denominator) {
PrimeFactorization factors = denominator.factorize();
Expression l = sum.lhs();
Expression r = sum.rhs();
NaturalNumber divisor = Naturals.one();
for (NaturalNumber p : factors.primeFactors()) {
if (isDivisibleBy(l, p) && isDivisibleBy(r, p)) {
l = new Division(l, p).evaluate();
r = new Division(r, p).evaluate();
divisor = (NaturalNumber) divisor.multiply(p);
}
}
ANumber simplifiedDenominator = denominator.divide(divisor);
Expression numerator;
if (sum instanceof Addition) {
numerator = new Addition(l, r).evaluate();
} else if (sum instanceof Subtraction) {
numerator = new Subtraction(l, r).evaluate();
} else {
throw new IllegalStateException(String.format(
"Unexpected operation type: %s (%s)", sum, sum.getClass()));
}
if (simplifiedDenominator.isOne()) {
return numerator;
} else {
return new Division(numerator, simplifiedDenominator);
}
}
private boolean isDivisibleBy(Expression expr, Expression divisor) {
if (divisor instanceof NaturalNumber) {
if (expr instanceof IntegerNumber) {
return ((IntegerNumber) expr).mod((NaturalNumber) divisor).isZero();
} else if (expr instanceof Multiplication) {
Multiplication m = (Multiplication) expr;
if (m.lhs() instanceof IntegerNumber) {
return ((IntegerNumber) m.lhs()).mod((NaturalNumber) divisor).isZero();
} else if (m.rhs() instanceof IntegerNumber) {
return ((IntegerNumber) m.rhs()).mod((NaturalNumber) divisor).isZero();
}
}
}
return false;
}
@Override
public Precedence getPrecedence() {
return Precedence.Multiplication;
}
@Override
public Set domain() {
return new CartesianProduct(Reals.getInstance(), new Difference(Reals.getInstance(), new FiniteSet(Naturals.zero())));
}
@Override
public Set codomain() {
return Reals.getInstance();
}
@Override
protected BinaryOperation create(Expression lhs, Expression rhs) {
return new Division(lhs, rhs);
}
@Override
public String toString() {
if (lhs.getPrecedence().lt(getPrecedence())) {
return String.format("(%s) %s %s", lhs, sign, rhs);
} else if (rhs.getPrecedence().lt(getPrecedence())) {
return String.format("%s %s (%s)", lhs, sign, rhs);
} else {
return super.toString();
}
}
@Override
public boolean isCommutative() {
return false;
}
}