diff --git a/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala b/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala index c724f05b37aa67f0df165af330e37f6b9bb5d9de..70941a6861c2dd01c46da80e016894cd746684c1 100644 --- a/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala +++ b/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala @@ -134,7 +134,7 @@ object InferInvariantsPhase extends LeonPhase[Program, InferenceReport] { enumerationRelation = LessEquals, modularlyAnalyze, targettedUnroll, autoInference, dumpStats, tightBounds, withmult, usereals, inferTemp, useCegis, timeout, maxCegisBound, statsSuff) val report = (new InferenceEngine(inferctx)).run() - println("Final Program: \n" +PrettyPrinter.apply(report.finalProgramWoInstrumentation)) + //println("Final Program: \n" +PrettyPrinter.apply(report.finalProgramWoInstrumentation)) report } diff --git a/src/main/scala/leon/invariant/engine/InferenceReport.scala b/src/main/scala/leon/invariant/engine/InferenceReport.scala index 15c1a1ffef4bbbc0d011431c3e915db8e3cf54e9..26337c6fff8111ba59f11dfd25213ee61610ac8b 100644 --- a/src/main/scala/leon/invariant/engine/InferenceReport.scala +++ b/src/main/scala/leon/invariant/engine/InferenceReport.scala @@ -20,13 +20,13 @@ class InferenceCondition(val invariant: Option[Expr], funDef: FunDef) extends VC(BooleanLiteral(true), funDef, null) { var time: Option[Double] = None + lazy val prettyInv = invariant.map(inv => + simplifyArithmetic(InstUtil.replaceInstruVars(Util.multToTimes(inv), fd))) - def status: String = invariant match { + def status: String = prettyInv match { case None => "unknown" - case Some(inv) => { - val prettyInv = simplifyArithmetic(InstUtil.replaceInstruVars(Util.multToTimes(inv), fd)) - PrettyPrinter(prettyInv) - } + case Some(inv) => + PrettyPrinter(inv) } } @@ -97,14 +97,13 @@ class InferenceReport(fvcs: Map[FunDef, List[VC]])(implicit ctx: InferenceContex val uninstFdOpt = if (uninstFunName.isEmpty) None else Util.functionByName(uninstFunName, ctx.uninstrumentedProgram) - if (uninstFdOpt.isDefined) { + if (uninstFdOpt.isDefined) { acc + (fd -> uninstFdOpt.get) - } - else acc + } else acc } val funToPost = conditions.collect { case cd if cd.invariant.isDefined && funToUninstFun.contains(cd.fd) => - funToUninstFun(cd.fd) -> cd.invariant.get + funToUninstFun(cd.fd) -> cd.prettyInv.get }.toMap //println("Function to template: " + funToTmpl.map { case (k, v) => s"${k.id.name} --> $v" }.mkString("\n")) Util.assignTemplateAndCojoinPost(Map(), ctx.uninstrumentedProgram, funToPost, uniqueIdDisplay = false) diff --git a/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala b/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala index 5d6f7c0e728104475e2e29b5a92196f2ee6aca49..71531eeef02649facfbc439c95bf3c81e5b00ebb 100644 --- a/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala +++ b/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala @@ -24,7 +24,7 @@ class OrbRegressionSuite extends LeonRegressionSuite { val beginPipe = leon.frontends.scalac.ExtractionPhase andThen new leon.utils.PreprocessingPhase val program = beginPipe.run(ctx)(f.getAbsolutePath :: Nil) - val processPipe = InstrumentationPhase andThen InferInvariantsPhase + val processPipe = InferInvariantsPhase val report = processPipe.run(ctx)(program) val fails = report.conditions.filterNot(_.invariant.isDefined) if (!fails.isEmpty) diff --git a/testcases/web/invariant/01_Binary_Trie.scala b/testcases/web/invariant/01_Binary_Trie.scala new file mode 100644 index 0000000000000000000000000000000000000000..38ff37b950b4229ba1ae17c959f75574ec00853b --- /dev/null +++ b/testcases/web/invariant/01_Binary_Trie.scala @@ -0,0 +1,116 @@ +import leon.invariant._ +import leon.instrumentation._ + +object BinaryTrie { + sealed abstract class Tree + case class Leaf() extends Tree + case class Node(nvalue: BigInt, left: Tree, right: Tree) extends Tree + + sealed abstract class IList + case class Cons(head: BigInt, tail: IList) extends IList + case class Nil() extends IList + + def listSize(l: IList): BigInt = (l match { + case Nil() => 0 + case Cons(x, xs) => 1 + listSize(xs) + }) + + def height(t: Tree): BigInt = { + t match { + case Leaf() => 0 + case Node(x, l, r) => { + val hl = height(l) + val hr = height(r) + if (hl > hr) hl + 1 else hr + 1 + } + } + } + + def find(inp: IList, t: Tree): Tree = { + inp match { + case Nil() => t + case Cons(x, Nil()) => t + case Cons(x, xs @ Cons(y, _)) => { + t match { + case Leaf() => t + case Node(v, l, r) => { + if (y > 0) find(xs, l) else find(xs, r) + } + } + } + case _ => t + } + } ensuring (_ => time <= ? * listSize(inp) + ?) + + def insert(inp: IList, t: Tree): Tree = { + t match { + case Leaf() => { + inp match { + case Nil() => t + case Cons(x, xs) => { + val newch = insert(xs, Leaf()) + newch match { + case Leaf() => Node(x, Leaf(), Leaf()) + case Node(y, _, _) => if (y > 0) Node(x, newch, Leaf()) else Node(y, Leaf(), newch) + } + } + } + + } + case Node(v, l, r) => { + inp match { + case Nil() => t + case Cons(x, Nil()) => t + case Cons(x, xs @ Cons(y, _)) => { + val ch = if (y > 0) l else r + if (y > 0) + Node(v, insert(xs, ch), r) + else + Node(v, l, insert(xs, ch)) + } + case _ => t + } + } + } + } ensuring (_ => time <= ? * listSize(inp) + ?) + + def create(inp: IList): Tree = { + insert(inp, Leaf()) + } ensuring (res => time <= ? * listSize(inp) + ?) + + def delete(inp: IList, t: Tree): Tree = { + t match { + case Leaf() => { + inp match { + case Nil() => Leaf() + case Cons(x ,xs) => { + Leaf() + } + } + } + case Node(v, l, r) => { + inp match { + case Nil() => { + t + } + case Cons(x, Nil()) => { + if(l == Leaf() && r == Leaf()) Leaf() + else t + } + case Cons(x ,xs@Cons(y, _)) => { + val ch = if(y > 0) l else r + val newch = delete(xs, ch) + if(newch == Leaf() && ((y > 0 && r == Leaf()) || (y <= 0 && l == Leaf()))) Leaf() + else { + if(y > 0) + Node(v, newch, r) + else + Node(v, l, newch) + } + } + case _ => t + } + } + } + } ensuring (_ => time <= ? * listSize(inp) + ?) +} \ No newline at end of file diff --git a/testcases/web/invariant/02_Binomial_Heap.scala b/testcases/web/invariant/02_Binomial_Heap.scala new file mode 100644 index 0000000000000000000000000000000000000000..905ab01461e0c8b6fd2558a6573e8a53f0b57c84 --- /dev/null +++ b/testcases/web/invariant/02_Binomial_Heap.scala @@ -0,0 +1,154 @@ +import leon.invariant._ +import leon.instrumentation._ + +object BinomialHeap { + case class TreeNode(rank: BigInt, elem: Element, children: BinomialHeap) + case class Element(n: BigInt) + + sealed abstract class BinomialHeap + case class ConsHeap(head: TreeNode, tail: BinomialHeap) extends BinomialHeap + case class NilHeap() extends BinomialHeap + + sealed abstract class List + case class NodeL(head: BinomialHeap, tail: List) extends List + case class NilL() extends List + + sealed abstract class OptionalTree + case class Some(t : TreeNode) extends OptionalTree + case class None() extends OptionalTree + + private def leq(a: Element, b: Element) : Boolean = { + a match { + case Element(a1) => { + b match { + case Element(a2) => { + if(a1 <= a2) true + else false + } + } + } + } + } + + def isEmpty(t: BinomialHeap) = t match { + case ConsHeap(_,_) => false + case _ => true + } + + def rank(t: TreeNode) : BigInt = t.rank + + def root(t: TreeNode) : Element = t.elem + + def link(t1: TreeNode, t2: TreeNode): TreeNode = { + if (leq(t1.elem, t2.elem)) { + TreeNode(t1.rank + 1, t1.elem, ConsHeap(t2, t1.children)) + } else { + TreeNode(t1.rank + 1, t2.elem, ConsHeap(t1, t2.children)) + } + } + + def treeNum(h: BinomialHeap) : BigInt = { + h match { + case ConsHeap(head, tail) => 1 + treeNum(tail) + case _ => 0 + } + } + + def insTree(t: TreeNode, h: BinomialHeap) : BinomialHeap = { + h match { + case ConsHeap(head, tail) => { + if (rank(t) < rank(head)) { + ConsHeap(t, h) + } else if (rank(t) > rank(head)) { + ConsHeap(head, insTree(t,tail)) + } else { + insTree(link(t,head), tail) + } + } + case _ => ConsHeap(t, NilHeap()) + } + } ensuring(_ => time <= ? * treeNum(h) + ?) + + def merge(h1: BinomialHeap, h2: BinomialHeap): BinomialHeap = { + h1 match { + case ConsHeap(head1, tail1) => { + h2 match { + case ConsHeap(head2, tail2) => { + if (rank(head1) < rank(head2)) { + ConsHeap(head1, merge(tail1, h2)) + } else if (rank(head2) < rank(head1)) { + ConsHeap(head2, merge(h1, tail2)) + } else { + mergeWithCarry(link(head1, head2), tail1, tail2) + } + } + case _ => h1 + } + } + case _ => h2 + } + } ensuring(_ => time <= ? * treeNum(h1) + ? * treeNum(h2) + ?) + + def mergeWithCarry(t: TreeNode, h1: BinomialHeap, h2: BinomialHeap): BinomialHeap = { + h1 match { + case ConsHeap(head1, tail1) => { + h2 match { + case ConsHeap(head2, tail2) => { + if (rank(head1) < rank(head2)) { + if (rank(t) < rank(head1)) + ConsHeap(t, ConsHeap(head1, merge(tail1, h2))) + else + mergeWithCarry(link(t, head1), tail1, h2) + } else if (rank(head2) < rank(head1)) { + if (rank(t) < rank(head2)) + ConsHeap(t, ConsHeap(head2, merge(h1, tail2))) + else + mergeWithCarry(link(t, head2), h1, tail2) + } else { + ConsHeap(t, mergeWithCarry(link(head1, head2), tail1, tail2)) + } + } + case _ => { + insTree(t, h1) + } + } + } + case _ => insTree(t, h2) + } + } ensuring (_ => time <= ? * treeNum(h1) + ? * treeNum(h2) + ?) + + def removeMinTree(h: BinomialHeap): (OptionalTree, BinomialHeap) = { + h match { + case ConsHeap(head, NilHeap()) => (Some(head), NilHeap()) + case ConsHeap(head1, tail1) => { + val (opthead2, tail2) = removeMinTree(tail1) + opthead2 match { + case Some(head2) => + if (leq(root(head1), root(head2))) { + (Some(head1), tail1) + } else { + (Some(head2), ConsHeap(head1, tail2)) + } + case _ => (Some(head1), tail1) + } + } + case _ => (None(), NilHeap()) + } + } ensuring (res => treeNum(res._2) <= treeNum(h) && time <= ? * treeNum(h) + ?) + + def minTreeChildren(h: BinomialHeap) : BigInt = { + val (min, _) = removeMinTree(h) + min match { + case Some(TreeNode(_,_,ch)) => treeNum(ch) + case _ => 0 + } + } + + def deleteMin(h: BinomialHeap) : BinomialHeap = { + val (min, ts2) = removeMinTree(h) + min match { + case Some(TreeNode(_,_,ts1)) => merge(ts1, ts2) + case _ => h + } + } ensuring(_ => time <= ? * minTreeChildren(h) + ? * treeNum(h) + ?) +} diff --git a/testcases/web/invariant/03_List_Concat.scala b/testcases/web/invariant/03_List_Concat.scala new file mode 100644 index 0000000000000000000000000000000000000000..44b8e0fb239222cdf7f4263adcdfd25c40bcf076 --- /dev/null +++ b/testcases/web/invariant/03_List_Concat.scala @@ -0,0 +1,41 @@ +import leon.invariant._ +import leon.instrumentation._ + +object ConcatVariations { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List + + def size(l: List): BigInt = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) + + def genL(n: BigInt): List = { + require(n >= 0) + if (n == 0) Nil() + else + Cons(n, genL(n - 1)) + } ensuring (res => size(res) == n && time <= ? *n + ?) + + def append(l1: List, l2: List): List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => Cons(x, append(xs, l2)) + + }) ensuring (res => size(l1) + size(l2) == size(res) && time <= ? *size(l1) + ?) + + def f_good(m: BigInt, n: BigInt): List = { + require(0 <= m && 0 <= n) + if (m == 0) Nil() + else append(genL(n), f_good(m - 1, n)) + + } ensuring(res => size(res) == n*m && time <= ? *(n*m) + ? *n + ? *m + ?) + + def f_worst(m: BigInt, n: BigInt): List = { + require(0 <= m && 0 <= n) + + if (m == 0) Nil() + else append(f_worst(m - 1, n), genL(n)) + + } ensuring(res => size(res) == n*m && time <= ? *((n*m)*m)+ ? *(n*m)+ ? *n+ ? *m+ ?) +} diff --git a/testcases/web/invariant/04_Red_Black_Tree.scala b/testcases/web/invariant/04_Red_Black_Tree.scala new file mode 100644 index 0000000000000000000000000000000000000000..43f3c7248bdc5a551d44fb5e6186109c7b1b3b4a --- /dev/null +++ b/testcases/web/invariant/04_Red_Black_Tree.scala @@ -0,0 +1,108 @@ +import leon.invariant._ +import leon.instrumentation._ +import scala.collection.immutable.Set + +object RedBlackTree { + sealed abstract class Color + case class Red() extends Color + case class Black() extends Color + + sealed abstract class Tree + case class Empty() extends Tree + case class Node(color: Color, left: Tree, value: BigInt, right: Tree) extends Tree + + def twopower(x: BigInt) : BigInt = { + require(x >= 0) + if(x < 1) 1 + else + 2* twopower(x - 1) + } + + def size(t: Tree): BigInt = { + require(blackBalanced(t)) + (t match { + case Empty() => BigInt(0) + case Node(_, l, v, r) => size(l) + 1 + size(r) + }) + } ensuring (res => twopower(blackHeight(t)) <= ? *res + ?) + + def blackHeight(t : Tree) : BigInt = { + t match { + case Node(Black(), l, _, _) => blackHeight(l) + 1 + case Node(Red(), l, _, _) => blackHeight(l) + case _ => 0 + } + } + + def isBlack(t: Tree) : Boolean = t match { + case Empty() => true + case Node(Black(),_,_,_) => true + case _ => false + } + + def redNodesHaveBlackChildren(t: Tree) : Boolean = t match { + case Empty() => true + case Node(Black(), l, _, r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + case Node(Red(), l, _, r) => isBlack(l) && isBlack(r) && redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + case _ => false + } + + def redDescHaveBlackChildren(t: Tree) : Boolean = t match { + case Node(_,l,_,r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + case _ => true + } + + def blackBalanced(t : Tree) : Boolean = t match { + case Node(_,l,_,r) => blackBalanced(l) && blackBalanced(r) && blackHeight(l) == blackHeight(r) + case _ => true + } + + def ins(x: BigInt, t: Tree): Tree = { + require(redNodesHaveBlackChildren(t) && blackBalanced(t)) + + t match { + case Empty() => Node(Red(),Empty(),x,Empty()) + case Node(c,a,y,b) => + if(x < y) { + val t1 = ins(x, a) + balance(c, t1, y, b) + } + else if (x == y){ + Node(c,a,y,b) + } + else{ + val t1 = ins(x, b) + balance(c,a,y,t1) + } + } + } ensuring(res => time <= ? *blackHeight(t) + ?) + + def makeBlack(n: Tree): Tree = { + n match { + case Node(Red(),l,v,r) => Node(Black(),l,v,r) + case _ => n + } + } + + def add(x: BigInt, t: Tree): Tree = { + require(redNodesHaveBlackChildren(t) && blackBalanced(t) ) + val t1 = ins(x, t) + makeBlack(t1) + + } ensuring(res => time <= ? *blackHeight(t) + ?) + + def balance(co: Color, l: Tree, x: BigInt, r: Tree): Tree = { + Node(co,l,x,r) + match { + case Node(Black(),Node(Red(),Node(Red(),a,xV,b),yV,c),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),Node(Red(),a,xV,Node(Red(),b,yV,c)),zV,d) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),Node(Red(),b,yV,c),zV,d)) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case Node(Black(),a,xV,Node(Red(),b,yV,Node(Red(),c,zV,d))) => + Node(Red(),Node(Black(),a,xV,b),yV,Node(Black(),c,zV,d)) + case _ => Node(co,l,x,r) + } + } +} diff --git a/testcases/web/invariant/05_Leftist_Heap.scala b/testcases/web/invariant/05_Leftist_Heap.scala new file mode 100644 index 0000000000000000000000000000000000000000..b938b3807fc4fcaf35c45bf43f74730aa8c565ca --- /dev/null +++ b/testcases/web/invariant/05_Leftist_Heap.scala @@ -0,0 +1,82 @@ +import leon.invariant._ +import leon.instrumentation._ +import leon.annotation._ + +object LeftistHeap { + sealed abstract class Heap + case class Leaf() extends Heap + case class Node(rk : BigInt, value: BigInt, left: Heap, right: Heap) extends Heap + + private def rightHeight(h: Heap) : BigInt = h match { + case Leaf() => 0 + case Node(_,_,_,r) => rightHeight(r) + 1 + } + + private def rank(h: Heap) : BigInt = h match { + case Leaf() => 0 + case Node(rk,_,_,_) => rk + } + + private def hasLeftistProperty(h: Heap) : Boolean = (h match { + case Leaf() => true + case Node(_,_,l,r) => hasLeftistProperty(l) && hasLeftistProperty(r) && rightHeight(l) >= rightHeight(r) && (rank(h) == rightHeight(h)) + }) + + @monotonic + def twopower(x: BigInt) : BigInt = { + require(x >= 0) + if(x < 1) 1 + else + 2* twopower(x - 1) + } + + def size(t: Heap): BigInt = { + require(hasLeftistProperty(t)) + (t match { + case Leaf() => BigInt(0) + case Node(_,v, l, r) => size(l) + 1 + size(r) + }) + } ensuring (res => twopower(rightHeight(t)) <= ? *res + ?) + + def leftRightHeight(h: Heap) : BigInt = {h match { + case Leaf() => 0 + case Node(_,_,l,r) => rightHeight(l) + }} + + def removeMax(h: Heap) : Heap = { + require(hasLeftistProperty(h)) + h match { + case Node(_,_,l,r) => merge(l, r) + case l @ Leaf() => l + } + } ensuring(res => time <= ? *leftRightHeight(h) + ?) + + private def merge(h1: Heap, h2: Heap) : Heap = { + require(hasLeftistProperty(h1) && hasLeftistProperty(h2)) + h1 match { + case Leaf() => h2 + case Node(_, v1, l1, r1) => h2 match { + case Leaf() => h1 + case Node(_, v2, l2, r2) => + if(v1 > v2) + makeT(v1, l1, merge(r1, h2)) + else + makeT(v2, l2, merge(h1, r2)) + } + } + } ensuring(res => time <= ? *rightHeight(h1) + ? *rightHeight(h2) + ?) + + private def makeT(value: BigInt, left: Heap, right: Heap) : Heap = { + if(rank(left) >= rank(right)) + Node(rank(right) + 1, value, left, right) + else + Node(rank(left) + 1, value, right, left) + } + + def insert(element: BigInt, heap: Heap) : Heap = { + require(hasLeftistProperty(heap)) + + merge(Node(1, element, Leaf(), Leaf()), heap) + + } ensuring(res => time <= ? *rightHeight(heap) + ?) +} diff --git a/testcases/web/invariant/06_String_Buffer.scala b/testcases/web/invariant/06_String_Buffer.scala new file mode 100644 index 0000000000000000000000000000000000000000..c5f7dcf9015c5f576ae9d9a59a997f8e45ec9f32 --- /dev/null +++ b/testcases/web/invariant/06_String_Buffer.scala @@ -0,0 +1,63 @@ +import leon.invariant._ +import leon.instrumentation._ + +object StringBuffer { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List + + def size(l: List): BigInt = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) + + sealed abstract class StringBuffer + case class Chunk(str: List, next: StringBuffer) extends StringBuffer + case class Empty() extends StringBuffer + + def length(sb: StringBuffer) : BigInt = sb match { + case Chunk(_, next) => 1 + length(next) + case _ => 0 + } + + def sizeBound(sb: StringBuffer, k: BigInt) : Boolean ={ + sb match { + case Chunk(str, next) => size(str) <= k && sizeBound(next, k) + case _ => 0 <= k + } + } + + def Equals(str1: List, str2: List, s1: StringBuffer, s2: StringBuffer, k: BigInt) : Boolean = { + require(sizeBound(s1, k) && sizeBound(s2, k) && size(str1) <= k && size(str2) <= k && k >= 0) + + (str1, str2) match { + case (Cons(h1,t1), Cons(h2,t2)) => { + + if(h1 != h2) false + else Equals(t1,t2, s1,s2, k) + } + case (Cons(_,_), Nil()) => { + s2 match { + case Chunk(str, next) => Equals(str1, str, s1, next, k) + case Empty() => false + } + } + case (Nil(), Cons(_,_)) => { + s1 match { + case Chunk(str, next) => Equals(str, str2, next, s2, k) + case Empty() => false + } + } + case _ =>{ + (s1,s2) match { + case (Chunk(nstr1, next1),Chunk(nstr2, next2)) => Equals(nstr1, nstr2, next1, next2, k) + case (Empty(),Chunk(nstr2, next2)) => Equals(str1, nstr2, s1, next2, k) + case (Chunk(nstr1, next1), Empty()) => Equals(nstr1, str2, next1, s2, k) + case _ => true + } + } + } + } ensuring(res => true && tmpl((a,b,c,d,e) => time <= a*((k+1)*(length(s1) + length(s2))) + b*size(str1) + e)) + + def max(x: BigInt, y: BigInt) : BigInt = if(x >= y) x else y +}