package org.batfish.z3.node;
import java.util.Set;
import org.batfish.datamodel.SubRange;
import org.batfish.z3.NodProgram;
import com.microsoft.z3.BoolExpr;
import com.microsoft.z3.Z3Exception;
public class RangeMatchExpr extends BooleanExpr {
public static BooleanExpr bitvectorGEExpr(String bv, long lb, int numBits) {
// these masks refer to nested conditions, not to bitwise and, or
int numBitsLeft = numBits;
BooleanExpr finalExpr = null;
BooleanExpr currentExpr = null;
OrExpr currentOrExpr = null;
AndExpr currentAndExpr = null;
while (numBitsLeft > 0) {
// find largest remaining 'subnet mask' not overlapping with lowerbound
int orSpread = -1;
long orMask = 0;
int orStartPos;
int orEndPos;
orEndPos = numBitsLeft - 1;
for (int i = orEndPos; i >= 0; i--) {
orMask |= (1L << i);
if ((lb & orMask) != 0) {
orMask ^= (1L << i);
break;
}
else {
orSpread++;
numBitsLeft--;
}
}
if (orSpread >= 0) {
orStartPos = orEndPos - orSpread;
LitIntExpr zeroExpr = new LitIntExpr(0L, orStartPos, orEndPos);
IntExpr extractExpr = newExtractExpr(bv, numBits, orStartPos,
orEndPos);
EqExpr eqExpr = new EqExpr(extractExpr, zeroExpr);
NotExpr notExpr = new NotExpr(eqExpr);
OrExpr oldOrExpr = currentOrExpr;
currentOrExpr = new OrExpr();
currentOrExpr.addDisjunct(notExpr);
if (currentExpr != null) {
if (currentExpr == currentAndExpr) {
currentAndExpr.addConjunct(currentOrExpr);
}
else if (currentExpr == oldOrExpr) {
oldOrExpr.addDisjunct(currentOrExpr);
}
}
else {
finalExpr = currentOrExpr;
}
currentExpr = currentOrExpr;
}
// find largest remaining 'subnet mask' not overlapping with lowerbound
int andSpread = -1;
long andMask = 0;
int andStartPos;
int andEndPos;
andEndPos = numBitsLeft - 1;
for (int i = andEndPos; i >= 0; i--) {
andMask |= (1L << i);
if ((lb & andMask) != andMask) {
andMask ^= (1L << i);
break;
}
else {
andSpread++;
numBitsLeft--;
}
}
if (andSpread >= 0) {
andStartPos = andEndPos - andSpread;
LitIntExpr andMaskExpr = new LitIntExpr(andMask, andStartPos,
andEndPos);
IntExpr extractExpr = newExtractExpr(bv, numBits, andStartPos,
andEndPos);
EqExpr eqExpr = new EqExpr(extractExpr, andMaskExpr);
AndExpr oldAndExpr = currentAndExpr;
currentAndExpr = new AndExpr();
currentAndExpr.addConjunct(eqExpr);
if (currentExpr != null) {
if (currentExpr == currentOrExpr) {
currentOrExpr.addDisjunct(currentAndExpr);
}
else if (currentExpr == oldAndExpr) {
oldAndExpr.addConjunct(currentAndExpr);
}
}
else {
finalExpr = currentAndExpr;
}
currentExpr = currentAndExpr;
}
}
return finalExpr;
}
public static BooleanExpr bitvectorLEExpr(String bv, long lb, int numBits) {
OrExpr leExpr = new OrExpr();
LitIntExpr upperBound = new LitIntExpr(lb, numBits);
VarIntExpr var = new VarIntExpr(bv);
EqExpr exactMatch = new EqExpr(var, upperBound);
BooleanExpr ge = bitvectorGEExpr(bv, lb, numBits);
NotExpr lessThan = new NotExpr(ge);
leExpr.addDisjunct(exactMatch);
leExpr.addDisjunct(lessThan);
return leExpr;
}
private static IntExpr newExtractExpr(String var, int varSize, int low,
int high) {
if (low == 0 && high == varSize - 1) {
return new VarIntExpr(var);
}
else {
return new ExtractExpr(var, low, high);
}
}
private BooleanExpr _expr;
public RangeMatchExpr(String var, int bits, Set<SubRange> range) {
long max = (1l << bits) - 1;
OrExpr or = new OrExpr();
for (SubRange subRange : range) {
long low = subRange.getStart();
long high = subRange.getEnd();
if (low == high) {
VarIntExpr portVarExpr = new VarIntExpr(var);
LitIntExpr portLitExpr = new LitIntExpr(low, bits);
EqExpr exactMatch = new EqExpr(portVarExpr, portLitExpr);
or.addDisjunct(exactMatch);
}
else {
boolean doLE = (high < max);
boolean doGE = (low > 0);
AndExpr and = new AndExpr();
if (doGE) {
BooleanExpr geExpr = bitvectorGEExpr(var, low, bits);
and.addConjunct(geExpr);
}
if (doLE) {
BooleanExpr leExpr = bitvectorLEExpr(var, high, bits);
and.addConjunct(leExpr);
}
if (!doGE && !doLE) {
// any value in range matches
_expr = TrueExpr.INSTANCE;
}
and.addConjunct(TrueExpr.INSTANCE);
or.addDisjunct(and);
}
}
or.addDisjunct(FalseExpr.INSTANCE);
_expr = or;
}
@Override
public void print(StringBuilder sb, int indent) {
_expr.print(sb, indent);
}
@Override
public BoolExpr toBoolExpr(NodProgram nodProgram) throws Z3Exception {
return _expr.toBoolExpr(nodProgram);
}
@Override
public String toString() {
return _expr.toString();
}
}