package inference.equivalence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import exceptions.CoefficientNotOneException;
import exceptions.SubstituteFailedException;
import exceptions.TooManyVariablesException;
import lombok.Getter;
import models.algebra.Expression;
import models.algebra.Variable;
import models.terms.RDLTerm;
import models.terms.meta.MetaRDLTerm;
import models.terms.meta.MetaVariable;
import models.terms.meta.OrderVariableConstraint;
import utils.ExpressionUitls;
import utils.Permutation;
@Getter
public class MetaSemanticEquivalenceRelation {
private MetaRDLTerm leftSideHand;
private MetaRDLTerm rightSideHand;
private Expression order;
private Variable orderVariable;
private int orderConstant = 0;
public MetaSemanticEquivalenceRelation(MetaRDLTerm lsh, MetaRDLTerm rsh, Expression order) {
leftSideHand = lsh;
rightSideHand = rsh;
this.order = order;
Map<Variable, Integer> coefficients = new HashMap<>();
orderConstant = ExpressionUitls.getCoefficientAndConstantsFromExpression(order, coefficients, 1);
if (coefficients.size() > 1) {
// todo: create exception
throw new TooManyVariablesException("Too many variables");
}
if (coefficients.size() == 1) {
orderVariable = coefficients.keySet().iterator().next();
if (coefficients.get(orderVariable) != 1) {
throw new CoefficientNotOneException();
}
} else {
orderVariable = null;
}
}
public boolean isMatchedBy(SemanticEquivalenceRelation relation) {
Map<Variable, RDLTerm> binding = new HashMap<>();
Map<Variable, OrderVariableConstraint> orderConstraint = new HashMap<>();
return isMatchedBy(relation, binding, orderConstraint);
}
public boolean isMatchedBy(SemanticEquivalenceRelation relation, Map<Variable, RDLTerm> binding, Map<Variable, OrderVariableConstraint> orderConstraint) {
return leftSideHand.isMatchedBy(relation.getLeftSideHand(), binding, orderConstraint)
&& rightSideHand.isMatchedBy(relation.getRightSideHand(), binding, orderConstraint);
}
public Set<SemanticEquivalenceRelation> substitute(Map<Variable, RDLTerm> binding, Map<Variable, OrderVariableConstraint> orderConstraint, Collection<RDLTerm> existedTerms) {
Set<SemanticEquivalenceRelation> result = new HashSet<>();
Map<Variable, MetaVariable> allVariables = new HashMap<>();
for(MetaVariable variable : leftSideHand.getAllVariables()) {
allVariables.put(variable.getVariableName(), variable);
}
for(MetaVariable variable : rightSideHand.getAllVariables()) {
allVariables.put(variable.getVariableName(), variable);
}
for (Variable variable : binding.keySet()) {
allVariables.remove(variable);
}
List<Variable> leftVariables = new ArrayList<>(allVariables.keySet());
for(List<RDLTerm> useTerms: Permutation.permutation(existedTerms, leftVariables.size())) {
Map<Variable, RDLTerm> tempBinding = new HashMap<>();
tempBinding.putAll(binding);
boolean flg = false;
for (int i = 0; i < leftVariables.size(); i++) {
if (!allVariables.get(leftVariables.get(i)).isMatchedBy(useTerms.get(i))) {
flg = true;
break;
}
tempBinding.put(leftVariables.get(i), useTerms.get(i));
}
if (flg) {
continue;
}
RDLTerm leftTerm = leftSideHand.substitute(tempBinding);
RDLTerm rightTerm = rightSideHand.substitute(tempBinding);
Map<Variable, OrderVariableConstraint> tempOrderConstraint = new HashMap<>();
for(Variable key : orderConstraint.keySet()) {
tempOrderConstraint.put(key, (OrderVariableConstraint)orderConstraint.get(key).clone());
}
if (leftSideHand.isMatchedBy(leftTerm, binding, tempOrderConstraint) && rightSideHand.isMatchedBy(rightTerm, binding, tempOrderConstraint)) {
if (! tempOrderConstraint.containsKey(orderVariable)) {
continue;
}
int order = tempOrderConstraint.get(this.orderVariable).getOrder();
if (order == -1) {
continue;
}
order += orderConstant;
result.add(new SemanticEquivalenceRelation(leftTerm, rightTerm, order));
}
for(Variable variable: leftVariables) {
binding.remove(variable);
}
}
return result;
}
public Set<SemanticEquivalenceRelation> substitute(RDLTerm term, Collection<RDLTerm> existedTerms) {
Set<SemanticEquivalenceRelation> result = new HashSet<>();
Set<SemanticEquivalenceRelation> leftSub = leftSubstitute(term, existedTerms);
if (leftSub != null) {
result.addAll(leftSub);
}
Set<SemanticEquivalenceRelation> rightSub = rightSubstitute(term, existedTerms);
if (rightSub != null) {
result.addAll(rightSub);
}
return result;
}
private Set<SemanticEquivalenceRelation> leftSubstitute(RDLTerm term, Collection<RDLTerm> existedTerms) {
Set<SemanticEquivalenceRelation> result = new HashSet<>();
Map<Variable, RDLTerm> binding = new HashMap<>();
Map<Variable, OrderVariableConstraint> orderConstraint = new HashMap<>();
if (! leftSideHand.isMatchedBy(term, binding, orderConstraint)) {
return null;
}
if (! orderConstraint.containsKey(orderVariable)) {
return null;
}
int order = orderConstraint.get(this.orderVariable).getOrder();
if (order == -1) {
return null;
}
order += orderConstant;
Set<Variable> allVariables = new HashSet<>(rightSideHand.getVariables().values());
allVariables.removeAll(binding.keySet());
List<Variable> leftVariables = new ArrayList<>(allVariables);
for (List<RDLTerm> useVariables: Permutation.permutation(existedTerms, leftVariables.size())) {
Map<Variable, RDLTerm> tempBinding = new HashMap<>();
tempBinding.putAll(binding);
for (int i = 0; i < leftVariables.size(); i++) {
tempBinding.put(leftVariables.get(i), useVariables.get(i));
}
RDLTerm tempTerm = rightSideHand.substitute(tempBinding);
if (! rightSideHand.isMatchedBy(tempTerm, binding, orderConstraint)) {
continue;
}
result.add(new SemanticEquivalenceRelation(leftSideHand.substitute(binding), rightSideHand.substitute(binding), order));
for (int i = 0; i < leftVariables.size(); i++) {
binding.remove(leftVariables.get(i));
}
}
try {
result.add(new SemanticEquivalenceRelation(leftSideHand.substitute(binding), rightSideHand.substitute(binding), order));
} catch (SubstituteFailedException e){}
return result;
}
private Set<SemanticEquivalenceRelation> rightSubstitute(RDLTerm term, Collection<RDLTerm> existedTerms) {
Set<SemanticEquivalenceRelation> result = new HashSet<>();
Map<Variable, RDLTerm> binding = new HashMap<>();
Map<Variable, OrderVariableConstraint> orderConstraint = new HashMap<>();
if (! rightSideHand.isMatchedBy(term, binding, orderConstraint)) {
return null;
};
if (! orderConstraint.containsKey(orderVariable)) {
return null;
}
int order = orderConstraint.get(this.orderVariable).getOrder();
if (order == -1) {
return null;
}
order += orderConstant;
Set<Variable> allVariables = new HashSet<>();
for (MetaVariable vari : leftSideHand.getAllVariables()) {
allVariables.add(vari.getVariableName());
}
allVariables.removeAll(binding.keySet());
List<Variable> leftVariables = new ArrayList<>(allVariables);
for (List<RDLTerm> useVariables: Permutation.permutation(existedTerms, leftVariables.size())) {
Map<Variable, RDLTerm> tempBinding = new HashMap<>();
tempBinding.putAll(binding);
for (int i = 0; i < leftVariables.size(); i++) {
tempBinding.put(leftVariables.get(i), useVariables.get(i));
}
RDLTerm tempTerm = leftSideHand.substitute(tempBinding);
if (! leftSideHand.isMatchedBy(tempTerm, binding, orderConstraint)) {
for (int i = 0; i < leftVariables.size(); i++) {
binding.remove(leftVariables.get(i));
}
continue;
}
result.add(new SemanticEquivalenceRelation(leftSideHand.substitute(binding), rightSideHand.substitute(binding), order));
for (int i = 0; i < leftVariables.size(); i++) {
binding.remove(leftVariables.get(i));
}
}
try {
result.add(new SemanticEquivalenceRelation(leftSideHand.substitute(binding), rightSideHand.substitute(binding), order));
} catch (SubstituteFailedException e){}
return result;
}
@Override
public String toString() {
return leftSideHand.toString() + " ===(" + order + ") " + rightSideHand.toString();
}
@Override
public boolean equals(Object another) {
if (! (another instanceof SemanticEquivalenceRelation)) {
return false;
}
SemanticEquivalenceRelation relation = (SemanticEquivalenceRelation) another;
return leftSideHand.equals(relation.getLeftSideHand()) && rightSideHand.equals(relation.getRightSideHand());
}
@Override
public int hashCode() {
return toString().hashCode();
}
}