using System; using System.Numerics; using System.Collections.Generic; using System.Diagnostics.Contracts; using Microsoft.Basetypes; namespace Microsoft.Boogie.AbstractInterpretation { class NativeIntervallDomain : NativeLattice { abstract class E_Common : NativeLattice.Element { } class E_Bottom : E_Common { public override Expr ToExpr() { return Expr.False; } } class E : E_Common { public readonly Node N; public E() { } public E(Node n) { N = n; } public override Expr ToExpr() { Expr expr = Expr.True; for (var n = N; n != null; n = n.Next) { expr = BplAnd(expr, n.ToExpr()); } return expr; } } public class Node { public readonly Variable V; // variable has type bool or int // For an integer variable (Lo,Hi) indicates Lo <= V < Hi, where Lo==null means no lower bound and Hi==null means no upper bound. // For a real variable (Lo,Hi) indicates Lo <= V <= Hi, where Lo==null means no lower bound and Hi==null means no upper bound. // For a boolean variable, (Lo,Hi) is one of: (null,null) for {false,true}, (null,1) for {false}, and (1,null) for {true}. public readonly BigInteger? Lo; public readonly BigInteger? Hi; public Node Next; // always sorted according to StrictlyBefore; readonly after full initialization [Pure] public static bool StrictlyBefore(Variable a, Variable b) { Contract.Assert(a.UniqueId != b.UniqueId || a == b); return a.UniqueId < b.UniqueId; } Node(Variable v, BigInteger? lo, BigInteger? hi, Node next) { Contract.Requires(lo != null || hi != null); // don't accept empty constraints Contract.Requires(next == null || StrictlyBefore(v, next.V)); V = v; Lo = lo; Hi = hi; Next = next; } /// /// This constructor leaves Next as null, allowing the caller to fill in Next to finish off the construction. /// public Node(Variable v, BigInteger? lo, BigInteger? hi) { Contract.Requires(lo != null || hi != null); // don't accept empty constraints V = v; Lo = lo; Hi = hi; } /// /// Returns a Node that has the constraints head.{V,Lo,Hi} plus /// all the constraints entailed by Nodes reachable from tail. /// Requires that "head" sorts no later than anything in "tail". /// Create either returns "head" itself or returns a new Node. /// public static Node Create(Node head, Node tail) { Contract.Requires(head != null); Contract.Requires(tail == null || !StrictlyBefore(tail.V, head.V)); Contract.Requires(head != tail); if (head.Next == tail) { return head; } else if (tail != null && head.V == tail.V) { // incorporate both constraints into one Node return new Node(head.V, Max(head.Lo, tail.Lo, true), Min(head.Lo, tail.Lo, true), tail.Next); } else { return new Node(head.V, head.Lo, head.Hi, tail); } } public static void GetBounds(Node n, Variable v, out BigInteger? lo, out BigInteger? hi) { for (; n != null; n = n.Next) { if (n.V == v) { lo = n.Lo; hi = n.Hi; return; } else if (StrictlyBefore(v, n.V)) { break; } } lo = null; hi = null; } /// /// Return the minimum of "a" and "b". If treatNullAsUnit==true, then "null" is /// interpreted as positive infinity (the unit element of min); otherwise, it is /// treated as negative infinity (the zero element of min). /// public static BigInteger? Min(BigInteger? a, BigInteger? b, bool treatNullAsUnit) { if (a == null) { return treatNullAsUnit ? b : a; } else if (b == null) { return treatNullAsUnit ? a : b; } else { return BigInteger.Min((BigInteger)a, (BigInteger)b); } } /// /// Return the maximum of "a" and "b". If treatNullAsUnit==true, then "null" is /// interpreted as negative infinity (the unit element of max); otherwise, it is /// treated as positive infinity (the zero element of max). /// public static BigInteger? Max(BigInteger? a, BigInteger? b, bool treatNullAsUnit) { if (a == null) { return treatNullAsUnit ? b : a; } else if (b == null) { return treatNullAsUnit ? a : b; } else { return BigInteger.Max((BigInteger)a, (BigInteger)b); } } public static IEnumerable> Merge(Node a, Node b) { while (true) { if (a == null && b == null) { yield break; } else if (a == null || b == null) { yield return new Tuple(a, b); if (a != null) { a = a.Next; } else { b = b.Next; } } else if (a.V == b.V) { yield return new Tuple(a, b); a = a.Next; b = b.Next; } else if (StrictlyBefore(a.V, b.V)) { yield return new Tuple(a, null); a = a.Next; } else { yield return new Tuple(null, b); b = b.Next; } } } public Expr ToExpr() { if (!V.IsMutable && CommandLineOptions.Clo.InstrumentInfer != CommandLineOptions.InstrumentationPlaces.Everywhere) { // omit invariants about readonly variables return Expr.True; } else if (V.TypedIdent.Type.IsBool) { if (Lo == null && Hi == null) { return Expr.True; } else { Contract.Assert((Lo == null && (BigInteger)Hi == 1) || (Hi == null && (BigInteger)Lo == 1)); var ide = new IdentifierExpr(Token.NoToken, V); return Hi == null ? ide : Expr.Not(ide); } } else if (V.TypedIdent.Type.IsInt) { Expr e = Expr.True; if (Lo != null && Hi != null && Lo + 1 == Hi) { // produce an equality var ide = new IdentifierExpr(Token.NoToken, V); e = Expr.And(e, BplEq(ide, NumberToExpr((BigInteger)Lo, V.TypedIdent.Type))); } else { // produce a (possibly empty) conjunction of inequalities if (Lo != null) { var ide = new IdentifierExpr(Token.NoToken, V); e = Expr.And(e, BplLe(NumberToExpr((BigInteger)Lo, V.TypedIdent.Type), ide)); } if (Hi != null) { var ide = new IdentifierExpr(Token.NoToken, V); e = Expr.And(e, BplLt(ide, NumberToExpr((BigInteger)Hi, V.TypedIdent.Type))); } } return e; } else { Contract.Assert(V.TypedIdent.Type.IsReal); Expr e = Expr.True; if (Lo != null && Hi != null && Lo == Hi) { // produce an equality var ide = new IdentifierExpr(Token.NoToken, V); e = Expr.And(e, BplEq(ide, NumberToExpr((BigInteger)Lo, V.TypedIdent.Type))); } else { // produce a (possibly empty) conjunction of inequalities if (Lo != null) { var ide = new IdentifierExpr(Token.NoToken, V); e = Expr.And(e, BplLe(NumberToExpr((BigInteger)Lo, V.TypedIdent.Type), ide)); } if (Hi != null) { var ide = new IdentifierExpr(Token.NoToken, V); e = Expr.And(e, BplLe(ide, NumberToExpr((BigInteger)Hi, V.TypedIdent.Type))); } } return e; } } } static Expr NumberToExpr(BigInteger n, Type ty) { if (n == null) { return null; } else if (ty.IsReal) { return Expr.Literal(Basetypes.BigDec.FromBigInt(n)); } else { Contract.Assume(ty.IsInt); return Expr.Literal(Basetypes.BigNum.FromBigInt(n)); } } List upThresholds; // invariant: thresholds are sorted List downThresholds; // invariant: thresholds are sorted /// /// Requires "thresholds" to be sorted. /// public NativeIntervallDomain() { upThresholds = new List(); downThresholds = new List(); } public override void Specialize(Implementation impl) { if (impl == null) { // remove thresholds upThresholds = new List(); downThresholds = new List(); } else { var tf = new ThresholdFinder(impl); tf.Find(out downThresholds, out upThresholds); #if DEBUG_PRINT Console.Write("DEBUG: for implementation '{0}', setting downs to [", impl.Name); foreach (var i in downThresholds) { Console.Write(" {0}", i); } Console.Write(" ] and ups to ["); foreach (var i in upThresholds) { Console.Write(" {0}", i); } Console.WriteLine(" ]"); #endif } base.Specialize(impl); } private E_Common top = new E(); private E_Common bottom = new E_Bottom(); public override Element Top { get { return top; } } public override Element Bottom { get { return bottom; } } public override bool IsTop(Element element) { var e = element as E; return e != null && e.N == null; } public override bool IsBottom(Element element) { return element is E_Bottom; } public override bool Below(Element a, Element b) { if (a is E_Bottom) { return true; } else if (b is E_Bottom) { return false; } else { var aa = (E)a; var bb = (E)b; // check if every constraint in 'bb' is implied by constraints in 'aa' foreach (var t in Node.Merge(aa.N, bb.N)) { var x = t.Item1; var y = t.Item2; if (x == null) { // bb constrains a variable that aa does not return false; } else if (y == null) { // aa constrains a variable that bb does not; that's fine } else if (y.Lo != null && (x.Lo == null || x.Lo < y.Lo)) { // bb has a Lo constraint, and either aa has no Lo constraint or it has a weaker Lo constraint return false; } else if (y.Hi != null && (x.Hi == null || y.Hi < x.Hi)) { // bb has a Hi o constraint, and either aa has no Hi constraint or it has a weaker Hi constraint return false; } } return true; } } public override Element Meet(Element a, Element b) { if (a is E_Bottom) { return a; } else if (b is E_Bottom) { return b; } else { var aa = (E)a; var bb = (E)b; Node head = null; Node prev = null; foreach (var t in Node.Merge(aa.N, bb.N)) { var x = t.Item1; var y = t.Item2; Node n; if (x == null) { n = new Node(y.V, y.Lo, y.Hi); } else if (y == null) { n = new Node(x.V, x.Lo, x.Hi); } else { var lo = Node.Max(x.Lo, y.Lo, true); var hi = Node.Min(x.Hi, y.Hi, true); // if hi<=lo (or hi /// For a proof of correctness of this method, see Test/dafny2/Intervals.dfy. /// A difference is that the this method returns: /// let d = Dafny_RoundDown(k); /// return d == -1 ? null : downThresholds[d]; /// BigInteger? RoundDown(BigInteger k) { if (downThresholds.Count == 0 || k < downThresholds[0]) { return null; } var i = 0; var j = downThresholds.Count - 1; while (i < j) { var mid = i + (j - i + 1) / 2; if (downThresholds[mid] <= k) { i = mid; } else { j = mid - 1; } } return downThresholds[i]; } /// /// For a proof of correctness of this method, see Test/dafny2/Intervals.dfy. /// A difference is that the this method returns: /// let d = Dafny_RoundUp(k); /// return d == thresholds.Count ? null : upThresholds[d]; /// BigInteger? RoundUp(BigInteger k) { if (upThresholds.Count == 0 || upThresholds[upThresholds.Count - 1] < k) { return null; } var i = 0; var j = upThresholds.Count - 1; while (i < j) { var mid = i + (j - i) / 2; if (upThresholds[mid] < k) { i = mid + 1; } else { j = mid; } } return upThresholds[i]; } public override Element Constrain(Element element, Expr expr) { if (element is E_Bottom) { return element; } else { var e = (E)element; var c = Constraint(expr, e.N); return c == null ? element : Meet(element, c); } } /// /// Returns an Element that corresponds to the constraints implied by "expr" in the /// state "state". /// Return "null" to indicate no constraints. /// E_Common Constraint(Expr expr, Node state) { Variable v; if (IsVariable(expr, out v)) { var n = new Node(v, BigInteger.One, null); return new E(n); } else if (expr is LiteralExpr) { var e = (LiteralExpr)expr; return (bool)e.Val ? null : new E_Bottom(); } else if (expr is NAryExpr) { var e = (NAryExpr)expr; if (e.Fun is UnaryOperator) { if (((UnaryOperator)e.Fun).Op == UnaryOperator.Opcode.Not) { if (IsVariable(e.Args[0], out v)) { var n = new Node(v, null, BigInteger.One); return new E(n); } } } else if (e.Fun is BinaryOperator) { var op = ((BinaryOperator)e.Fun).Op; var arg0 = e.Args[0]; var arg1 = e.Args[1]; switch (op) { case BinaryOperator.Opcode.Eq: case BinaryOperator.Opcode.Iff: { E_Common c = null; if (IsVariable(arg0, out v)) { BigInteger? lo, hi; if (PartiallyEvaluate(arg1, state, out lo, out hi)) { var n = new Node(v, lo, hi); c = new E(n); } } if (IsVariable(arg1, out v)) { BigInteger? lo, hi; if (PartiallyEvaluate(arg1, state, out lo, out hi)) { var n = new Node(v, lo, hi); c = c == null ? new E(n) : (E_Common)Meet(c, new E(n)); } } return c; } case BinaryOperator.Opcode.Neq: { E_Common c = null; if (IsVariable(arg0, out v)) { c = ConstrainNeq(state, v, arg1); } if (IsVariable(arg1, out v)) { var cc = ConstrainNeq(state, v, arg0); if (cc != null) { c = c == null ? cc : (E_Common)Meet(c, cc); } } return c; } case BinaryOperator.Opcode.Le: { E_Common c = null; if (IsVariable(arg1, out v)) { BigInteger? lo, hi; PartiallyEvaluate(arg0, state, out lo, out hi); if (lo != null) { var n = new Node(v, lo, null); c = new E(n); } } if (IsVariable(arg0, out v)) { BigInteger? lo, hi; PartiallyEvaluate(arg1, state, out lo, out hi); if (hi != null) { var n = new Node(v, null, hi); c = c == null ? new E(n) : (E_Common)Meet(c, new E(n)); } } return c; } case BinaryOperator.Opcode.Lt: { E_Common c = null; if (IsVariable(arg1, out v)) { BigInteger? lo, hi; PartiallyEvaluate(arg0, state, out lo, out hi); if (lo != null) { var n = new Node(v, v.TypedIdent.Type.IsReal ? lo : lo + 1, null); c = new E(n); } } if (IsVariable(arg0, out v)) { BigInteger? lo, hi; PartiallyEvaluate(arg1, state, out lo, out hi); if (hi != null) { var n = new Node(v, null, v.TypedIdent.Type.IsReal ? hi : hi - 1); c = c == null ? new E(n) : (E_Common)Meet(c, new E(n)); } } return c; } case BinaryOperator.Opcode.Ge: { var tmp = arg0; arg0 = arg1; arg1 = tmp; goto case BinaryOperator.Opcode.Le; } case BinaryOperator.Opcode.Gt: { var tmp = arg0; arg0 = arg1; arg1 = tmp; goto case BinaryOperator.Opcode.Lt; } default: break; } } } return null; // approximation } private E ConstrainNeq(Node state, Variable v, Expr arg) { BigInteger? lo, hi; if (PartiallyEvaluate(arg, state, out lo, out hi)) { if (!v.TypedIdent.Type.IsReal && lo != null && hi != null && lo + 1 == hi) { var exclude = lo; // If the partially evaluated arg (whose value is "exclude") is an end-point of // the interval known for "v", then produce a constraint that excludes that bound. Node.GetBounds(state, v, out lo, out hi); if (lo != null && lo == exclude) { var n = new Node(v, lo + 1, null); return new E(n); } else if (hi != null && exclude + 1 == hi) { var n = new Node(v, null, exclude); return new E(n); } } } return null; } bool IsVariable(Expr expr, out Variable v) { var e = expr as IdentifierExpr; if (e == null) { v = null; return false; } else { v = e.Decl; return true; } } public override Element Update(Element element, AssignCmd cmd) { if (element is E_Bottom) { return element; } var e = (E)element; var nn = e.N; Contract.Assert(cmd.Lhss.Count == cmd.Rhss.Count); for (int i = 0; i < cmd.Lhss.Count; i++) { var lhs = cmd.Lhss[i]; var rhs = cmd.Rhss[i]; BigInteger? lo; BigInteger? hi; PartiallyEvaluate(rhs, e.N, out lo, out hi); nn = UpdateOne(nn, lhs.DeepAssignedVariable, lo, hi); } return new E(nn); } bool PartiallyEvaluate(Expr rhs, Node node, out BigInteger? lo, out BigInteger? hi) { var pe = new PEVisitor(node); pe.VisitExpr(rhs); lo = pe.Lo; hi = pe.Hi; return lo != null || hi != null; } class PEVisitor : ReadOnlyVisitor { public BigInteger? Lo; public BigInteger? Hi; readonly BigInteger one = BigInteger.One; Node N; public PEVisitor(Node n) { N = n; } // Override visitors for all expressions that can return a boolean, integer, or real result public override Expr VisitExpr(Expr node) { Lo = Hi = null; return base.VisitExpr(node); } public override Expr VisitLiteralExpr(LiteralExpr node) { if (node.Val is BigNum) { var n = ((BigNum)node.Val).ToBigInteger; Lo = n; Hi = n + 1; } else if (node.Val is BigDec) { BigInteger floor, ceiling; ((BigDec)node.Val).FloorCeiling(out floor, out ceiling); Lo = floor; Hi = ceiling; } else if (node.Val is bool) { if ((bool)node.Val) { // true Lo = one; Hi = null; } else { // false Lo = null; Hi = one; } } return node; } public override Expr VisitIdentifierExpr(IdentifierExpr node) { if (node.Type.IsBool || node.Type.IsInt || node.Type.IsReal) { Node.GetBounds(N, node.Decl, out Lo, out Hi); } return node; } public override Expr VisitNAryExpr(NAryExpr node) { if (node.Fun is UnaryOperator) { var op = (UnaryOperator)node.Fun; Contract.Assert(node.Args.Count == 1); if (op.Op == UnaryOperator.Opcode.Neg) { BigInteger? lo, hi; VisitExpr(node.Args[0]); lo = Lo; hi = Hi; if (hi != null) { Lo = node.Type.IsReal ? -hi : 1 - hi; } else { Lo = null; } if (lo != null) { Hi = node.Type.IsReal ? -lo : 1 - lo; } else { Hi = null; } } else if (op.Op == UnaryOperator.Opcode.Not) { VisitExpr(node.Args[0]); Contract.Assert((Lo == null && Hi == null) || (Lo == null && (BigInteger)Hi == 1) || (Hi == null && (BigInteger)Lo == 1)); var tmp = Lo; Lo = Hi; Hi = tmp; } } else if (node.Fun is BinaryOperator) { var op = (BinaryOperator)node.Fun; Contract.Assert(node.Args.Count == 2); BigInteger? lo0, hi0, lo1, hi1; VisitExpr(node.Args[0]); lo0 = Lo; hi0 = Hi; VisitExpr(node.Args[1]); lo1 = Lo; hi1 = Hi; Lo = Hi = null; var isReal = node.Args[0].Type.IsReal; switch (op.Op) { case BinaryOperator.Opcode.And: if (hi0 != null || hi1 != null) { // one operand is definitely false, thus so is the result Lo = null; Hi = one; } else if (lo0 != null && lo1 != null) { // both operands are definitely true, thus so is the result Lo = one; Hi = null; } break; case BinaryOperator.Opcode.Or: if (lo0 != null || lo1 != null) { // one operand is definitely true, thus so is the result Lo = one; Hi = null; } else if (hi0 != null && hi1 != null) { // both operands are definitely false, thus so is the result Lo = null; Hi = one; } break; case BinaryOperator.Opcode.Imp: if (hi0 != null || lo1 != null) { // either arg0 false or arg1 is true, so the result is true Lo = one; Hi = null; } else if (lo0 != null && hi1 != null) { // arg0 is true and arg1 is false, so the result is false Lo = null; Hi = one; } break; case BinaryOperator.Opcode.Iff: if (lo0 != null && lo1 != null) { Lo = one; Hi = null; } else if (hi0 != null && hi1 != null) { Lo = one; Hi = null; } else if (lo0 != null && hi1 != null) { Lo = null; Hi = one; } else if (hi0 != null && lo1 != null) { Lo = null; Hi = one; } if (op.Op == BinaryOperator.Opcode.Neq) { var tmp = Lo; Lo = Hi; Hi = tmp; } break; case BinaryOperator.Opcode.Eq: case BinaryOperator.Opcode.Neq: if (node.Args[0].Type.IsBool) { goto case BinaryOperator.Opcode.Iff; } // For Eq: // If the (lo0,hi0) and (lo1,hi1) ranges do not overlap, the answer is false. // If both ranges are the same unit range, then the answer is true. if (hi0 != null && lo1 != null && (isReal ? hi0 < lo1 : hi0 <= lo1)) { // no overlap Lo = null; Hi = one; } else if (lo0 != null && hi1 != null && (isReal ? hi1 < lo0 : hi1 <= lo0)) { Lo = null; Hi = one; // no overlaop } else if (lo0 != null && hi0 != null && lo1 != null && hi1 != null && lo0 == lo1 && hi0 == hi1 && // ranges are the same (isReal ? lo0 == hi0 : lo0 + 1 == hi0)) { // unit range // both ranges are the same unit range Lo = one; Hi = null; } if (op.Op == BinaryOperator.Opcode.Neq) { var tmp = Lo; Lo = Hi; Hi = tmp; } break; case BinaryOperator.Opcode.Le: if (isReal) { // If hi0 <= lo1, then the answer is true. // If hi1 < lo0, then the answer is false. if (hi0 != null && lo1 != null && hi0 <= lo1) { Lo = one; Hi = null; } else if (hi1 != null && lo0 != null && hi1 < lo0) { Lo = null; Hi = one; } } else { // If hi0 - 1 <= lo1, then the answer is true. // If hi1 <= lo0, then the answer is false. if (hi0 != null && lo1 != null && hi0 - 1 <= lo1) { Lo = one; Hi = null; } else if (lo0 != null && hi1 != null && hi1 <= lo0) { Lo = null; Hi = one; } } break; case BinaryOperator.Opcode.Lt: if (isReal) { // If hi0 < lo1, then the answer is true. // If hi1 <= lo0, then the answer is false. if (hi0 != null && lo1 != null && hi0 < lo1) { Lo = one; Hi = null; } else if (hi1 != null && lo0 != null && hi1 <= lo0) { Lo = null; Hi = one; } } else { // If hi0 <= lo1, then the answer is true. // If hi1 - 1 <= lo0, then the answer is false. if (hi0 != null && lo1 != null && hi0 <= lo1) { Lo = one; Hi = null; } else if (lo0 != null && hi1 != null && hi1 - 1 <= lo0) { Lo = null; Hi = one; } } break; case BinaryOperator.Opcode.Gt: // swap the operands and then continue as Lt { var tmp = lo0; lo0 = lo1; lo1 = tmp; tmp = hi0; hi0 = hi1; hi1 = tmp; } goto case BinaryOperator.Opcode.Lt; case BinaryOperator.Opcode.Ge: // swap the operands and then continue as Le { var tmp = lo0; lo0 = lo1; lo1 = tmp; tmp = hi0; hi0 = hi1; hi1 = tmp; } goto case BinaryOperator.Opcode.Le; case BinaryOperator.Opcode.Add: if (lo0 != null && lo1 != null) { Lo = lo0 + lo1; } if (hi0 != null && hi1 != null) { Hi = isReal ? hi0 + hi1 : hi0 + hi1 - 1; } break; case BinaryOperator.Opcode.Sub: if (lo0 != null && hi1 != null) { Lo = isReal ? lo0 - hi1 : lo0 - hi1 + 1; } if (hi0 != null && lo1 != null) { Hi = hi0 - lo1; } break; case BinaryOperator.Opcode.Mul: // this uses an incomplete approximation that could be tightened up if (lo0 != null && lo1 != null) { if (0 <= (BigInteger)lo0 && 0 <= (BigInteger)lo1) { Lo = lo0 * lo1; Hi = hi0 == null || hi1 == null ? null : isReal ? hi0 * hi1 : (hi0 - 1) * (hi1 - 1) + 1; } else if ((BigInteger)lo0 < 0 && (BigInteger)lo1 < 0) { Lo = null; // approximation Hi = isReal ? lo0 * lo1 : lo0 * lo1 + 1; } } break; case BinaryOperator.Opcode.Div: // this uses an incomplete approximation that could be tightened up if (lo0 != null && lo1 != null && 0 <= (BigInteger)lo0 && 0 <= (BigInteger)lo1) { Lo = BigInteger.Zero; Hi = hi0; } break; case BinaryOperator.Opcode.Mod: // this uses an incomplete approximation that could be tightened up if (lo0 != null && lo1 != null && 0 <= (BigInteger)lo0 && 0 <= (BigInteger)lo1) { Lo = BigInteger.Zero; Hi = hi1; if (lo0 < lo1 && hi0 != null && hi0 < lo1) { Lo = lo0; Hi = hi0; } } break; case BinaryOperator.Opcode.RealDiv: // this uses an incomplete approximation that could be tightened up if (lo0 != null && lo1 != null && 0 <= (BigInteger)lo0 && 0 <= (BigInteger)lo1) { Lo = BigInteger.Zero; Hi = 1 <= (BigInteger)lo1 ? hi0 : null; } break; case BinaryOperator.Opcode.Pow: // this uses an incomplete approximation that could be tightened up if (lo0 != null && lo1 != null && 0 <= (BigInteger)lo0 && 0 <= (BigInteger)lo1) { Lo = 1 <= (BigInteger)lo1 ? BigInteger.One : BigInteger.Zero; Hi = hi1; } break; default: break; } } else if (node.Fun is IfThenElse) { var op = (IfThenElse)node.Fun; Contract.Assert(node.Args.Count == 3); BigInteger? guardLo, guardHi, lo0, hi0, lo1, hi1; VisitExpr(node.Args[0]); guardLo = Lo; guardHi = Hi; VisitExpr(node.Args[1]); lo0 = Lo; hi0 = Hi; VisitExpr(node.Args[2]); lo1 = Lo; hi1 = Hi; Contract.Assert(guardLo == null || guardHi == null); // this is a consequence of the guard being boolean if (guardLo != null) { // guard is always true Lo = lo0; Hi = hi0; } else if (guardHi != null) { // guard is always false Lo = lo1; Hi = hi1; } else { // we don't know which branch will be taken, so join the information from the two branches Lo = Node.Min(lo0, lo1, false); Hi = Node.Max(hi0, hi1, false); } } else if (node.Fun is FunctionCall) { var call = (FunctionCall)node.Fun; // See if this is an identity function, which we do by checking: that the function has // exactly one argument and the function has been marked by the user with the attribute {:identity} bool claimsToBeIdentity = false; if (call.ArgumentCount == 1 && call.Func.CheckBooleanAttribute("identity", ref claimsToBeIdentity) && claimsToBeIdentity && node.Args[0].Type.Equals(node.Type)) { VisitExpr(node.Args[0]); } } return node; } public override BinderExpr VisitBinderExpr(BinderExpr node) { // don't recurse on subexpression return node; } public override Expr VisitOldExpr(OldExpr node) { // don't recurse on subexpression return node; } public override Expr VisitCodeExpr(CodeExpr node) { // don't recurse on subexpression return node; } public override Expr VisitBvConcatExpr(BvConcatExpr node) { // don't recurse on subexpression return node; } public override Expr VisitBvExtractExpr(BvExtractExpr node) { // don't recurse on subexpression return node; } } public override Element Eliminate(Element element, Variable v) { if (element is E_Bottom) { return element; } var e = (E)element; var nn = UpdateOne(e.N, v, null, null); if (nn == e.N) { return element; } else { return new E(nn); } } Node UpdateOne(Node nn, Variable v, BigInteger? lo, BigInteger? hi) { var orig = nn; Node head = null; Node prev = null; var foundV = false; for (; nn != null && !Node.StrictlyBefore(v, nn.V); nn = nn.Next) { if (nn.V == v) { foundV = true; nn = nn.Next; break; // we found the place where the new node goes } else { var n = new Node(nn.V, nn.Lo, nn.Hi); // copy this Node if (head == null) { head = n; } else { prev.Next = n; } prev = n; } } Node rest; if (lo == null && hi == null) { // eliminate all information about "v" if (!foundV) { return orig; } rest = nn; } else { rest = new Node(v, lo, hi); rest.Next = nn; } if (head == null) { head = rest; } else { prev.Next = rest; } return head; } /// /// Return a resolved/type-checked expression that represents the conjunction of a and b. /// Requires a and b to be resolved and type checked already. /// public static Expr BplAnd(Expr a, Expr b) { if (a == Expr.True) { return b; } else if (b == Expr.True) { return a; } else { var nary = Expr.Binary(BinaryOperator.Opcode.And, a, b); nary.Type = Type.Bool; nary.TypeParameters = SimpleTypeParamInstantiation.EMPTY; return nary; } } /// /// Return a resolved/type-checked expression that represents a EQUALS b. /// Requires a and b to be resolved and type checked already. /// public static Expr BplEq(Expr a, Expr b) { var e = Expr.Eq(a, b); e.Type = Type.Bool; return e; } /// /// Return a resolved/type-checked expression that represents a LESS-EQUAL b. /// Requires a and b to be resolved and type checked already. /// public static Expr BplLe(Expr a, Expr b) { var e = Expr.Le(a, b); e.Type = Type.Bool; return e; } /// /// Return a resolved/type-checked expression that represents a LESS b. /// Requires a and b to be resolved and type checked already. /// public static Expr BplLt(Expr a, Expr b) { var e = Expr.Lt(a, b); e.Type = Type.Bool; return e; } } public class ThresholdFinder : ReadOnlyVisitor { readonly Implementation Impl; public ThresholdFinder(Implementation impl) { Contract.Requires(impl != null); Impl = impl; } HashSet downs = new HashSet(); HashSet ups = new HashSet(); public void Find(out List downThresholds, out List upThresholds) { // always include -1, 0, 1 as down-thresholds downs.Clear(); downs.Add(-1); downs.Add(0); downs.Add(1); // always include 0 and 1 as up-thresholds ups.Clear(); ups.Add(0); ups.Add(1); foreach (Requires p in Impl.Proc.Requires) { Visit(p.Condition); } foreach (Ensures p in Impl.Proc.Ensures) { Visit(p.Condition); } foreach (var b in Impl.Blocks) { foreach (Cmd c in b.Cmds) { Visit(c); } } // convert the HashSets to sorted Lists and return downThresholds = new List(); foreach (var i in downs) { downThresholds.Add(i); } downThresholds.Sort(); upThresholds = new List(); foreach (var i in ups) { upThresholds.Add(i); } upThresholds.Sort(); } public override Expr VisitNAryExpr(NAryExpr node) { if (node.Fun is BinaryOperator) { var op = (BinaryOperator)node.Fun; Contract.Assert(node.Args.Count == 2); var arg0 = node.Args[0]; var arg1 = node.Args[1]; var offset = arg0.Type.IsReal ? 0 : 1; BigInteger? k; switch (op.Op) { case BinaryOperator.Opcode.Eq: case BinaryOperator.Opcode.Neq: k = AsIntLiteral(arg0); if (k != null) { var i = (BigInteger)k; downs.Add(i - 1); downs.Add(i); ups.Add(i + 1); ups.Add(i + 2); } k = AsIntLiteral(arg1); if (k != null) { var i = (BigInteger)k; downs.Add(i - 1); downs.Add(i); ups.Add(i + 1); ups.Add(i + 2); } break; case BinaryOperator.Opcode.Le: k = AsIntLiteral(arg0); if (k != null) { var i = (BigInteger)k; downs.Add(i - 1); downs.Add(i); } k = AsIntLiteral(arg1); if (k != null) { var i = (BigInteger)k; ups.Add(i + offset); ups.Add(i + 1 + offset); } break; case BinaryOperator.Opcode.Lt: k = AsIntLiteral(arg0); if (k != null) { var i = (BigInteger)k; downs.Add(i ); downs.Add(i + 1); } k = AsIntLiteral(arg1); if (k != null) { var i = (BigInteger)k; ups.Add(i - 1 + offset); ups.Add(i + offset); } break; case BinaryOperator.Opcode.Ge: { var tmp = arg0; arg0 = arg1; arg1 = tmp; } goto case BinaryOperator.Opcode.Le; case BinaryOperator.Opcode.Gt: { var tmp = arg0; arg0 = arg1; arg1 = tmp; } goto case BinaryOperator.Opcode.Lt; default: break; } } return base.VisitNAryExpr(node); } BigInteger? AsIntLiteral(Expr e) { var lit = e as LiteralExpr; if (lit != null && lit.isBigNum) { BigNum bn = lit.asBigNum; return bn.ToBigInteger; } return null; } } }