package models.terms.meta;
import java.util.HashMap;
import java.util.Map;
import exceptions.CoefficientNotOneException;
import exceptions.NonLinearExpressionException;
import exceptions.TooManyVariablesException;
import lombok.Getter;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Variable;
import models.dataConstraintModel.DataConstraintModel;
import models.terms.RDLTerm;
@Getter
public abstract class MetaVariable extends MetaRDLTerm {
protected Variable variableName;
protected OrderConstraint constraint;
protected Variable orderVariable;
protected int orderConstant;
protected Expression orderExpression;
protected MetaVariable(Symbol symbol, TermType termType, Variable name, OrderConstraint constraint, Expression order) {
super(symbol, termType);
this.variableName = name;
this.constraint = constraint;
this.orderExpression = order;
Map<Variable, Integer> coefficients = new HashMap<>();
int constant = 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;
}
orderConstant = constant;
}
@Override
public boolean isVariable() {
return true;
}
@Override
public boolean isMatchedBy(RDLTerm another, Map<Variable, RDLTerm> binding,
Map<Variable, OrderVariableConstraint> orderConstraint) {
if (! this.termType.getBaseTermClass().isAssignableFrom(another.getClass())) {
return false;
}
if (! orderConstraintCheck(another, orderConstraint)) {
return false;
}
if (! binding.containsKey(this.variableName)) {
binding.put(this.variableName, another);
return true;
}
return binding.get(this.variableName).equals(another);
}
private boolean orderConstraintCheck(RDLTerm another, Map<Variable, OrderVariableConstraint> orderConstraints) {
if (orderVariable == null) {
switch (constraint) {
case ANY:
return true;
case EQ:
return another.getOrder() == orderConstant;
case GE:
return another.getOrder() >= orderConstant;
case GT:
return another.getOrder() > orderConstant;
case LE:
return another.getOrder() <= orderConstant;
case LT:
return another.getOrder() < orderConstant;
}
} else {
if (! orderConstraints.containsKey(orderVariable)) {
orderConstraints.put(orderVariable, new OrderVariableConstraint());
}
var orderVarConst = orderConstraints.get(orderVariable);
return orderVarConst.setConstraint(another.getOrder() - orderConstant, constraint);
}
return false;
}
private int getCoefficientAndConstantsFromExpression(Expression expression, Map<Variable, Integer> coefficients, int curWeight) {
int res = 0;
if(expression instanceof Constant) {
return getConstantValue((Constant) expression) * curWeight;
} else if(expression instanceof Variable) {
coefficients.put((Variable) expression, coefficients.getOrDefault((Variable) expression, 0) + curWeight);
return 0;
}
Term term = (Term) expression;
Symbol symbol = term.getSymbol();
if(symbol.equals(DataConstraintModel.add)) {
Expression c1 = term.getChild(0);
Expression c2 = term.getChild(1);
res += getCoefficientAndConstantsFromExpression(c1, coefficients, curWeight);
res += getCoefficientAndConstantsFromExpression(c2, coefficients, curWeight);
} else if(symbol.equals(DataConstraintModel.sub)) {
Expression c1 = term.getChild(0);
Expression c2 = term.getChild(1);
res += getCoefficientAndConstantsFromExpression(c1, coefficients, curWeight);
res += getCoefficientAndConstantsFromExpression(c2, coefficients, -curWeight);
} else if(symbol.equals(DataConstraintModel.mul)) {
Expression c1 = term.getChild(0);
Expression c2 = term.getChild(1);
if(c1.getVariables().size() == 0 && c2.getVariables().size() == 0) {
res += getCoefficientAndConstantsFromExpression(c1, coefficients, curWeight) *
getCoefficientAndConstantsFromExpression(c2, coefficients, curWeight);
} else if(c1.getVariables().size() == 0) {
res += getCoefficientAndConstantsFromExpression(
c2,
coefficients,
getCoefficientAndConstantsFromExpression(c1, coefficients, curWeight)
);
} else if(c2.getVariables().size() == 0){
res += getCoefficientAndConstantsFromExpression(
c1,
coefficients,
getCoefficientAndConstantsFromExpression(c2, coefficients, curWeight)
);
} else {
throw new NonLinearExpressionException("Order expression must be linear expression.");
}
} else if(symbol.equals(DataConstraintModel.minus)) {
Expression c1 = term.getChild(0);
res += getCoefficientAndConstantsFromExpression(c1, coefficients, -curWeight);
} else {
throw new NonLinearExpressionException("Order expression must be linear expression.");
}
return res;
}
private int getConstantValue(Constant constant) {
return Integer.parseInt((String) constant.getValue());
}
@Override
public String toString() {
return variableName.toString();
}
@Override
public String toStringWithOrder() {
return toString() +getOrderString();
}
protected String getOrderString() {
switch(constraint) {
case ANY:
return "(*)";
case EQ:
return "(= " + orderExpression.toString() + ")";
case GE:
return "(>= " + orderExpression.toString() + ")";
case GT:
return "(> " + orderExpression.toString() + ")";
case LE:
return "(<= " + orderExpression.toString() + ")";
case LT:
return "(< " + orderExpression.toString() + ")";
}
return "";
}
@Override
public boolean equals(Object another) {
if (another instanceof MetaVariable) {
return false;
}
MetaVariable anotherVar = (MetaVariable) another;
return super.equals(another)
&& variableName.equals(another)
&& constraint == anotherVar.getConstraint()
&& orderVariable.equals(anotherVar.getOrderVariable())
&& orderConstant == anotherVar.getOrderConstant();
}
@Override
public int hashCode() {
return (termType.name() + toString()).hashCode();
}
@Override
public Object clone() {
// TODO 自動生成されたメソッド・スタブ
return super.clone();
}
}