package janala.interpreters;
import java.util.Map;
import java.util.HashMap;
public final class SymbolicInt extends Constraint {
private COMPARISON_OPS op;
public COMPARISON_OPS getOp() {
return op;
}
public void setOp(COMPARISON_OPS op) {
this.op = op;
}
private final Map<Integer, Long> linear; // coefficients
public Map<Integer, Long> getLinear() {
return linear;
}
private final long constant; // nominal value
public long getConstant() {
return constant;
}
@Override
public void accept(ConstraintVisitor v) {
v.visitSymbolicInt(this);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null) {
return false;
} else if (o instanceof SymbolicInt) {
SymbolicInt e = (SymbolicInt) o;
return (linear.equals(e.linear) && (constant == e.constant) && (op == e.op));
} else {
return false;
}
}
public int hashCode() {
int ret = 37;
ret = 71 * ret + linear.hashCode();
ret = 71 * ret + (int) constant;
ret = 71 * ret + op.hashCode();
return ret;
}
// Construct a symbolic int i := x
public SymbolicInt(int i) {
linear = new HashMap<Integer, Long>();
linear.put(i, 1L);
constant = 0;
op = COMPARISON_OPS.UN;
}
public SymbolicInt(Map<Integer, Long> linear, long constant) {
this.linear = linear;
this.constant = constant;
op = COMPARISON_OPS.UN;
}
public SymbolicInt(Map<Integer, Long> linear, long constant, COMPARISON_OPS op) {
this.linear = linear;
this.constant = constant;
this.op = op;
}
private SymbolicInt(SymbolicInt e) {
this.linear = new HashMap<Integer, Long>(e.linear);
constant = e.constant;
op = e.op;
}
public SymbolicInt negate() {
Map<Integer, Long> tmpMap = new HashMap<Integer, Long>(getLinear());
for (Map.Entry<Integer, Long> it : tmpMap.entrySet()) {
it.setValue(-it.getValue());
}
return new SymbolicInt(tmpMap, -constant);
}
public SymbolicInt add(long l) {
return add(l, true);
}
private SymbolicInt add(long l, boolean add) {
long tmpConstant;
if (add) {
tmpConstant = constant + l;
} else {
tmpConstant = constant - l;
}
return new SymbolicInt(linear, tmpConstant);
}
public SymbolicInt add(SymbolicInt l) {
return add(l, true);
}
private SymbolicInt add(SymbolicInt l, boolean add) {
Map<Integer, Long> tmpLinear = new HashMap<Integer, Long>(linear);
SymbolicInt e = (SymbolicInt) l;
for (Map.Entry<Integer, Long> it : e.linear.entrySet()) {
int integer = it.getKey();
Long coeff = linear.get(integer); // 0 is default value
if (coeff == null) {
coeff = 0L;
}
long toadd = add ? coeff + it.getValue() : coeff - it.getValue();
if (toadd == 0L) {
tmpLinear.remove(integer);
} else {
tmpLinear.put(integer, toadd);
}
}
if (tmpLinear.isEmpty()) {
return null; // Shouldn't this returns the constant value?
}
long tmpConstant = add ? (constant + e.constant) : (constant - e.constant);
return new SymbolicInt(tmpLinear, tmpConstant);
}
public SymbolicInt subtractFrom(long l) {
SymbolicInt e = (SymbolicInt) negate();
return new SymbolicInt(e.linear, l + e.constant);
}
public SymbolicInt subtract(long l) {
return add(l, false);
}
public SymbolicInt subtract(SymbolicInt l) {
return add(l, false);
}
public SymbolicInt multiply(long l) {
if (l == 0) return null;
if (l == 1) return this;
Map<Integer, Long> tmpMap = new HashMap<Integer, Long>();
for (Map.Entry<Integer, Long> it : linear.entrySet()) {
int integer = it.getKey();
tmpMap.put(integer, l * it.getValue());
}
return new SymbolicInt(tmpMap, l * constant);
}
public SymbolicInt setop(COMPARISON_OPS op) {
if (op == COMPARISON_OPS.UN) {
throw new RuntimeException("Cannot unset an operator");
}
SymbolicInt ret = new SymbolicInt(this);
if (ret.op != COMPARISON_OPS.UN) {
if (op == COMPARISON_OPS.EQ) { // (x op 0)==0 is same as !(x op 0)
// ret = ret.not();
ret.op = op;
} else if (op != COMPARISON_OPS.NE) {
throw new RuntimeException("Cannot process non-logical constraint.");
}
} else {
ret.op = op;
}
return ret;
}
public SymbolicInt not() {
COMPARISON_OPS retOp = COMPARISON_OPS.UN;
if (op == COMPARISON_OPS.EQ) {
retOp = COMPARISON_OPS.NE;
} else if (op == COMPARISON_OPS.NE) {
retOp = COMPARISON_OPS.EQ;
} else if (op == COMPARISON_OPS.GT) {
retOp = COMPARISON_OPS.LE;
} else if (op == COMPARISON_OPS.GE) {
retOp = COMPARISON_OPS.LT;
} else if (op == COMPARISON_OPS.LT) {
retOp = COMPARISON_OPS.GE;
} else if (op == COMPARISON_OPS.LE) {
retOp = COMPARISON_OPS.GT;
}
return new SymbolicInt(linear, constant, retOp);
}
public Constraint substitute(Map<String, Long> assignments) {
long val = 0;
Map<Integer, Long> retLinear = null;
long retConstant = 0L;
boolean isSymbolic = false;
for (Map.Entry<Integer, Long> it : linear.entrySet()) {
int key = it.getKey();
long l = it.getValue();
if (assignments.containsKey("x" + key)) {
val += assignments.get("x" + key) * l;
} else {
isSymbolic = true;
if (retLinear == null) {
retLinear = new HashMap<Integer, Long>();
}
retLinear.put(key, l);
}
}
if (retLinear != null) {
retConstant = val + constant;
}
Constraint ret2 = null;
if (!isSymbolic) {
if (this.op == COMPARISON_OPS.EQ) {
ret2 =
(val == -this.constant)
? SymbolicTrueConstraint.instance
: SymbolicFalseConstraint.instance;
} else if (this.op == COMPARISON_OPS.NE) {
ret2 =
(val != -this.constant)
? SymbolicTrueConstraint.instance
: SymbolicFalseConstraint.instance;
} else if (this.op == COMPARISON_OPS.LE) {
ret2 =
(val <= -this.constant)
? SymbolicTrueConstraint.instance
: SymbolicFalseConstraint.instance;
} else if (this.op == COMPARISON_OPS.LT) {
ret2 =
(val < -this.constant)
? SymbolicTrueConstraint.instance
: SymbolicFalseConstraint.instance;
} else if (this.op == COMPARISON_OPS.GE) {
ret2 =
(val >= -this.constant)
? SymbolicTrueConstraint.instance
: SymbolicFalseConstraint.instance;
} else if (this.op == COMPARISON_OPS.GT) {
ret2 =
(val > -this.constant)
? SymbolicTrueConstraint.instance
: SymbolicFalseConstraint.instance;
} else {
return null;
}
return ret2;
} else {
return new SymbolicInt(retLinear, retConstant, op);
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Map.Entry<Integer, Long> it : linear.entrySet()) {
int integer = it.getKey(); // Index of variable
long l = it.getValue();
if (first) {
first = false;
} else {
sb.append('+');
}
if (l < 0) {
sb.append('(');
sb.append(l);
sb.append(")*x");
sb.append(integer);
} else if (l == 1) {
sb.append("x");
sb.append(integer);
} else if (l > 0) {
sb.append(l);
sb.append("*x");
sb.append(integer);
}
}
if (constant != 0) {
if (constant > 0) sb.append('+');
sb.append(constant);
}
if (op == COMPARISON_OPS.EQ) {
sb.append("==");
sb.append('0');
} else if (op == COMPARISON_OPS.NE) {
sb.append("!=");
sb.append('0');
} else if (op == COMPARISON_OPS.LE) {
sb.append("<=");
sb.append('0');
} else if (op == COMPARISON_OPS.LT) {
sb.append("<");
sb.append('0');
} else if (op == COMPARISON_OPS.GE) {
sb.append(">=");
sb.append('0');
} else if (op == COMPARISON_OPS.GT) {
sb.append(">");
sb.append('0');
}
sb.append(" at iid ");
sb.append(iid);
sb.append(" and index ");
sb.append(index);
return sb.toString();
}
}