/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jmathexpr.op;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import jmathexpr.AbstractExpression;
import jmathexpr.Expression;
import jmathexpr.util.pattern.ExpressionPattern;
import jmathexpr.util.rule.Rule;
/**
*
* @author Elemér Furka
*
* @param <L> the left hand side operand
* @param <R> the right hand side operand
*/
public abstract class BinaryOperation<L extends Expression, R extends Expression>
extends AbstractExpression implements Operation, ExpressionPattern {
protected final L lhs;
protected final R rhs;
protected final Sign sign;
protected BinaryOperation(L lhs, R rhs, Sign sign) {
if (lhs == null || rhs == null) throw new IllegalArgumentException(
String.format("new BinaryOperation(%s, %s)", lhs, rhs));
this.lhs = lhs;
this.rhs = rhs;
this.sign = sign;
}
protected abstract Operation create(Expression lhs, Expression rhs);
public L lhs() {
return lhs;
}
public R rhs () {
return rhs;
}
@Override
public List<Expression> operands() {
List<Expression> operands = new ArrayList();
operands.add(lhs);
operands.add(rhs);
return Collections.unmodifiableList(operands);
}
@Override
public List<Expression> getChildren() {
return operands();
}
/**
* Tests if this binary operation is commutative. A binary operation o is
* commutative if for all possible operands a and b: a o b = b o a.
*
* @return if this operation is commutative
*/
public abstract boolean isCommutative();
@Override
public boolean equals(Object object) {
if (object == null) return false;
if (this == object) return true;
if (object instanceof BinaryOperation && getClass().equals(object.getClass())) {
BinaryOperation other = (BinaryOperation) object;
if (sign != other.sign) return false;
return lhs.equals(other.lhs) && rhs.equals(other.rhs);
}
return false;
}
@Override
public int hashCode() {
int hash = 7;
hash = 37 * hash + Objects.hashCode(this.lhs);
hash = 37 * hash + Objects.hashCode(this.rhs);
hash = 37 * hash + Objects.hashCode(this.sign);
return hash;
}
@Override
public String 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 String.format("%s %s %s", l, sign, r);
}
@Override
public String toUnicode() {
return String.format("%s %s %s", lhs, sign.toUnicode(), rhs);
}
@Override
public int getArity() {
return 2;
}
@Override
public boolean isConstant() {
return lhs.isConstant() && rhs.isConstant();
}
@Override
public boolean contains(ExpressionPattern pattern) {
if (getClass().isAssignableFrom(pattern.getClass())) {
BinaryOperation p = (BinaryOperation) pattern;
if (lhs.contains((ExpressionPattern) p.lhs)
&& rhs.contains((ExpressionPattern) p.rhs)) {
return true;
}
}
return lhs.contains(pattern) || rhs.contains(pattern);
}
@Override
public boolean isApplicable(Rule rule) {
boolean isApplicable = false;
Expression transformed = this;
if (rule.isRecursive()) {
Expression l = lhs, r = rhs;
if (lhs.isApplicable(rule)) {
isApplicable = true;
if (rule.isRecursive()) l = rule.subapply();
}
if (rhs.isApplicable(rule)) {
isApplicable = true;
if (rule.isRecursive()) r = rule.subapply();
}
transformed = isApplicable ? create(l, r) : this;
}
if (rule.matches(transformed)) {
return true;
} else if (isApplicable) {
rule.register(transformed);
}
return isApplicable;
}
@Override
public boolean matches(Expression expr) {
if (expr.getClass().isAssignableFrom(getClass())) {
BinaryOperation other = (BinaryOperation) expr;
if (((ExpressionPattern) lhs).matches(other.lhs)
&& ((ExpressionPattern) rhs).matches(other.rhs)) {
return true;
} else if (isCommutative() && ((ExpressionPattern) lhs).matches(other.rhs)
&& ((ExpressionPattern) rhs).matches(other.lhs)) {
return true;
}
}
return false;
}
}