/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jmathexpr.relation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import jmathexpr.AbstractExpression;
import jmathexpr.Expression;
import jmathexpr.set.Set;
import jmathexpr.set.op.CartesianProduct;
import jmathexpr.util.pattern.ExpressionPattern;
import jmathexpr.util.rule.Rule;
/**
* This class represents a binary relation R that connects two expressions l and
* r. Binary relations are used in their infix form l R r. Example l = r for equality.
*
* @author Elemér Furka
* @param <L> the left hand side expression
* @param <R> the right hand side expression
*/
public abstract class BinaryRelation<L extends Expression, R extends Expression>
extends AbstractExpression implements ExpressionPattern {
protected final L lhs;
protected final R rhs;
protected final RelationSymbol symbol;
protected BinaryRelation(L lhs, R rhs, RelationSymbol symbol) {
this.lhs = lhs;
this.rhs = rhs;
this.symbol = symbol;
}
/**
* Tests whether this binary relation R is symmetric.
*
* @return true iff for every (a, b): aRb -> bRa
*/
public abstract boolean isSymmetric();
protected abstract BinaryRelation create(Expression lhs, Expression rhs);
public L lhs() {
return lhs;
}
public R rhs() {
return rhs;
}
@Override
public boolean equals(Object object) {
if (object == null) return false;
if (this == object) return true;
if (object instanceof BinaryRelation && getClass().equals(object.getClass())) {
BinaryRelation other = (BinaryRelation) object;
if (symbol != other.symbol) return false;
return lhs.equals(other.lhs) && rhs.equals(other.rhs);
}
return false;
}
@Override
public Set domain() {
return new CartesianProduct(lhs.domain(), rhs.domain());
}
@Override
public Set codomain() {
if (lhs.codomain().equals(rhs.codomain())) {
return lhs.codomain();
} else {
throw new IllegalStateException("Missing operation: " + this);
}
}
@Override
public List<Expression> getChildren() {
List<Expression> children = new ArrayList();
children.add(lhs);
children.add(rhs);
return Collections.unmodifiableList(children);
}
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + Objects.hashCode(this.lhs);
hash = 67 * hash + Objects.hashCode(this.rhs);
hash = 67 * hash + Objects.hashCode(this.symbol);
return hash;
}
@Override
public String toString() {
return toString(lhs.toString(), symbol.toString(), rhs.toString());
}
@Override
public String toUnicode() {
return toString(lhs.toUnicode(), symbol.toUnicode(), rhs.toUnicode());
}
@Override
public String toAsciiMath() {
return toString(lhs.toAsciiMath(), symbol.toAsciiMath(), rhs.toAsciiMath());
}
private static String toString(String lhs, String symbol, String rhs) {
return String.format("%s %s %s", lhs, symbol, rhs);
}
@Override
public boolean isConstant() {
return lhs.isConstant() && rhs.isConstant();
}
@Override
public boolean contains(ExpressionPattern pattern) {
if (getClass().isAssignableFrom(pattern.getClass())) {
BinaryRelation p = (BinaryRelation) 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;
l = rule.subapply();
}
if (rhs.isApplicable(rule)) {
isApplicable = true;
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 (getClass().isAssignableFrom(expr.getClass())) {
BinaryRelation other = (BinaryRelation) expr;
if (((ExpressionPattern) lhs).matches(other.lhs)
&& ((ExpressionPattern) rhs).matches(other.rhs)) {
return true;
} else if (isSymmetric()) {
return ((ExpressionPattern) lhs).matches(other.rhs)
&& ((ExpressionPattern) rhs).matches(other.lhs);
}
}
return false;
}
}