From 99fb25b5bd5fbc4bdc0acb87413021845e8855b2 Mon Sep 17 00:00:00 2001
From: Philippe Suter <philippe.suter@gmail.com>
Date: Fri, 21 Jan 2011 13:01:38 +0000
Subject: [PATCH] code for "I'm feeling lucky"

---
 cav2011-testcases/Debug.scala        |  43 +++++
 cav2011-testcases/RedBlackTree.scala | 112 +++++++++++
 funcheck-etaps                       |   2 -
 src/purescala/FairZ3Solver.scala     | 278 ++++++++-------------------
 4 files changed, 236 insertions(+), 199 deletions(-)
 create mode 100644 cav2011-testcases/Debug.scala
 create mode 100644 cav2011-testcases/RedBlackTree.scala
 delete mode 100755 funcheck-etaps

diff --git a/cav2011-testcases/Debug.scala b/cav2011-testcases/Debug.scala
new file mode 100644
index 000000000..4fc8c01f5
--- /dev/null
+++ b/cav2011-testcases/Debug.scala
@@ -0,0 +1,43 @@
+import funcheck.Utils._
+import funcheck.Annotations._
+import scala.annotation.tailrec
+
+object Debug {
+  sealed abstract class List
+  case class Nil() extends List
+  case class Cons(head: Int, tail: List) extends List
+
+  def size1(list: List) : Int = {
+    list match {
+      case Nil() => 0
+      case Cons(_, xs) => 1 + size1(xs)
+    }
+  } ensuring(_ >= 0)
+
+  def size2(list: List) : Int = {
+    sizeTailRec(list, 0)
+  }
+
+  def identity(list: List) : List = list
+  
+  def zero() : Int = 0
+
+  def sizeTailRec(list: List, acc: Int) : Int = {
+    require(acc >= 0)
+    list match {
+      case Nil() => acc
+      case Cons(_, xs) => sizeTailRec(xs, acc + 1)
+    }
+  } ensuring(_ == acc + size1(list))
+
+  def sizesAreTheSame(list: List) : Boolean = {
+    size1(list) == size2(list)
+  } holds
+
+  def bug(list: List) : Boolean = {
+    list match {
+      case Nil() => true
+      case Cons(_,_) => size1(list) == size2(list) + 1
+    }
+  } holds
+}
diff --git a/cav2011-testcases/RedBlackTree.scala b/cav2011-testcases/RedBlackTree.scala
new file mode 100644
index 000000000..d6d447e65
--- /dev/null
+++ b/cav2011-testcases/RedBlackTree.scala
@@ -0,0 +1,112 @@
+import scala.collection.immutable.Set
+import funcheck.Annotations._
+
+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: Int, right: Tree) extends Tree
+
+  def content(t: Tree) : Set[Int] = t match {
+    case Empty() => Set.empty
+    case Node(_, l, v, r) => content(l) ++ Set(v) ++ content(r)
+  }
+
+  def size(t: Tree) : Int = t match {
+    case Empty() => 0
+    case Node(_, l, v, r) => size(l) + 1 + size(r)
+  }
+
+  /* We consider leaves to be black by definition */
+  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)
+  }
+
+  def redDescHaveBlackChildren(t: Tree) : Boolean = t match {
+    case Empty() => true
+    case Node(_,l,_,r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r)
+  }
+
+  def ins(x: Int, t: Tree): Tree = {
+    require(redNodesHaveBlackChildren(t))
+    t match {
+      case Empty() => Node(Red(),Empty(),x,Empty())
+      case Node(c,a,y,b) =>
+        if      (x < y)  balance(c, ins(x, a), y, b)
+        else if (x == y) Node(c,a,y,b)
+        else             balance(c,a,y,ins(x, b))
+    }
+  } ensuring (res => content(res) == content(t) ++ Set(x) 
+                   && size(t) <= size(res) && size(res) <= size(t) + 1
+                   && redDescHaveBlackChildren(res)
+                   )
+
+  def makeBlack(n: Tree): Tree = {
+    require(redDescHaveBlackChildren(n))
+    n match {
+      case Node(Red(),l,v,r) => Node(Black(),l,v,r)
+      case _ => n
+    }
+  } ensuring(redNodesHaveBlackChildren(_))
+
+  def add(x: Int, t: Tree): Tree = {
+    require(redNodesHaveBlackChildren(t))
+    makeBlack(ins(x, t))
+  } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res))
+  
+  def buggyAdd(x: Int, t: Tree): Tree = {
+    require(redNodesHaveBlackChildren(t))
+    //makeBlack(ins(x, t))
+    ins(x, t)
+  } ensuring (res => content(res) == content(t) ++ Set(x) && redNodesHaveBlackChildren(res))
+  
+  def balance(c: Color, a: Tree, x: Int, b: Tree): Tree = {
+    //require(
+    //  Node(c,a,x,b) match {
+    //    case Node(Black(),Node(Red(),Node(Red(),a,_,b),_,c),_,d) =>
+    //      redNodesHaveBlackChildren(a) &&
+    //      redNodesHaveBlackChildren(b) &&
+    //      redNodesHaveBlackChildren(c) &&
+    //      redNodesHaveBlackChildren(d)
+    //    case Node(Black(),Node(Red(),a,_,Node(Red(),b,_,c)),_,d) =>
+    //      redNodesHaveBlackChildren(a) &&
+    //      redNodesHaveBlackChildren(b) &&
+    //      redNodesHaveBlackChildren(c) &&
+    //      redNodesHaveBlackChildren(d)
+    //    case Node(Black(),a,_,Node(Red(),Node(Red(),b,_,c),_,d)) => 
+    //      redNodesHaveBlackChildren(a) &&
+    //      redNodesHaveBlackChildren(b) &&
+    //      redNodesHaveBlackChildren(c) &&
+    //      redNodesHaveBlackChildren(d)
+    //    case Node(Black(),a,_,Node(Red(),b,_,Node(Red(),c,_,d))) => 
+    //      redNodesHaveBlackChildren(a) &&
+    //      redNodesHaveBlackChildren(b) &&
+    //      redNodesHaveBlackChildren(c) &&
+    //      redNodesHaveBlackChildren(d)
+    //    case t => redDescHaveBlackChildren(t)
+    //  }
+    //)
+    Node(c,a,x,b) 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(c,a,xV,b) => Node(c,a,xV,b)
+    }
+  } ensuring (res => content(res) == content(Node(c,a,x,b)) )//&& redDescHaveBlackChildren(res))
+}
diff --git a/funcheck-etaps b/funcheck-etaps
deleted file mode 100755
index 6e6f2d534..000000000
--- a/funcheck-etaps
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-./scalac-funcheck -P:funcheck:nodefaults -P:funcheck:extensions=orderedsets.Main "$@"
diff --git a/src/purescala/FairZ3Solver.scala b/src/purescala/FairZ3Solver.scala
index 87cd67786..9dbdfd178 100644
--- a/src/purescala/FairZ3Solver.scala
+++ b/src/purescala/FairZ3Solver.scala
@@ -33,6 +33,8 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac
     program = prog
   }
 
+  var partialEvaluator : PartialEvaluator = null
+
   private def restartZ3: Unit = {
     if (neverInitialized) {
       neverInitialized = false
@@ -40,6 +42,7 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac
       z3.delete
     }
     z3 = new Z3Context(z3cfg)
+    partialEvaluator = new PartialEvaluator(program)
     // z3.traceToStdout
 
     exprToZ3Id = Map.empty
@@ -278,32 +281,19 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac
         }
         //case (Some(true), m, _) => { // SAT
         case (Some(true), m) => { // SAT
-          import Evaluator._
-
-          val asMap = modelToMap(m, varsInVC)
-          lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n")
-          reporter.info("A candidate counter-example was found. It should be valid, but let's check anyway.")
-          reporter.info(modelAsString)
-          val evalResult = eval(asMap, toCheckAgainstModels)
-
-          evalResult match {
-            case OK(BooleanLiteral(true)) => {
-              reporter.error("Counter-example found and confirmed:")
-              reporter.error(modelAsString)
-              foundDefinitiveAnswer = true
-              definitiveAnswer = Some(false)
-              definitiveModel = asMap
-            }
-            case other => {
-              //println(z3)
-              reporter.error("Something went wrong. The model should have been valid, yet we got this: " + other)
-              reporter.error(m)
-              foundDefinitiveAnswer = true
-              definitiveAnswer = None
-              definitiveModel = asMap
-            }
+          val (trueModel, model) = validateAndDeleteModel(m, toCheckAgainstModels, varsInVC)
+
+          if (trueModel) {
+            foundDefinitiveAnswer = true
+            definitiveAnswer = Some(false)
+            definitiveModel = model
+          } else {
+            reporter.error("Something went wrong. The model should have been valid, yet we got this : ")
+            reporter.error(model)
+            foundDefinitiveAnswer = true
+            definitiveAnswer = None
+            definitiveModel = model
           }
-          m.delete
         }
         // case (Some(false), m, core) if core.isEmpty => {
         //   reporter.info("Empty core, definitively valid.")
@@ -324,42 +314,49 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac
 
           z3.pop(1)
           val (result2,m2) = z3.checkAndGetModel()
-          m2.delete
+
           if (result2 == Some(false)) {
             foundDefinitiveAnswer = true
             definitiveAnswer = Some(true)
             definitiveModel = Map.empty
           } else {
-            reporter.info("We need to keep going.")
-
-            val toRelease : Set[Expr] = if(USEUNSATCORE) {
-              blockingSet//core.map(fromZ3Formula(_)).toSet
+            // we might have been lucky :D
+            val (wereWeLucky, cleanModel) = validateAndDeleteModel(m2, toCheckAgainstModels, varsInVC)
+            if(wereWeLucky) {
+              foundDefinitiveAnswer = true
+              definitiveAnswer = Some(false)
+              definitiveModel = cleanModel
             } else {
-              blockingSet
-            }
+              reporter.info("We need to keep going.")
 
-            // println("Release set : " + toRelease)
+              val toRelease : Set[Expr] = if(USEUNSATCORE) {
+                blockingSet//core.map(fromZ3Formula(_)).toSet
+              } else {
+                blockingSet
+              }
 
-            blockingSet = blockingSet -- toRelease
+              // println("Release set : " + toRelease)
 
-            val toReleaseAsPairs : Set[(Identifier,Boolean)] = toRelease.map(b => b match {
-              case Variable(id) => (id, true)
-              case Not(Variable(id)) => (id, false)
-              case _ => scala.Predef.error("Impossible value in release set: " + b)
-            })
+              blockingSet = blockingSet -- toRelease
 
-            reporter.info(" - more unrollings")
-            for((id,polarity) <- toReleaseAsPairs) {
-              val (newClauses,newBlockers) = unrollingBank.unlock(id, !polarity)
-              // for(clause <- newClauses) {
-              //   println("we're getting a new clause " + clause)
-              //   z3.assertCnstr(toZ3Formula(z3, clause).get)
-              // }
+              val toReleaseAsPairs : Set[(Identifier,Boolean)] = toRelease.map(b => b match {
+                case Variable(id) => (id, true)
+                case Not(Variable(id)) => (id, false)
+                case _ => scala.Predef.error("Impossible value in release set: " + b)
+              })
 
-              z3.assertCnstr(toZ3Formula(z3, And(newClauses)).get)
-              blockingSet = blockingSet ++ Set(newBlockers.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*)
-            }
+              reporter.info(" - more unrollings")
+              for((id,polarity) <- toReleaseAsPairs) {
+                val (newClauses,newBlockers) = unrollingBank.unlock(id, !polarity)
+                // for(clause <- newClauses) {
+                //   println("we're getting a new clause " + clause)
+                //   z3.assertCnstr(toZ3Formula(z3, clause).get)
+                // }
 
+                z3.assertCnstr(toZ3Formula(z3, And(newClauses)).get)
+                blockingSet = blockingSet ++ Set(newBlockers.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*)
+              }
+            }
             // scala.Predef.error("...but not quite now.")
           }
         }
@@ -367,153 +364,37 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac
     }
 
     (definitiveAnswer, definitiveModel)
+  }
+
+  private def validateAndDeleteModel(model: Z3Model, formula: Expr, variables: Set[Identifier]) : (Boolean, Map[Identifier,Expr]) = {
+    import Evaluator._
 
-    //var toUnroll : Set[FunctionInvocation] = Set.empty
-    //var everythingEverUnrolled : Set[FunctionInvocation] = Set.empty
-    //var blockingClause : List[Expr] = Nil
-    //val (basis, cls, bools) = clausifyITE(toCheckAgainstModels)
-    //var clauses = cls
-
-    //toUnroll = toUnroll ++ topLevelFunctionCallsOf(basis)
-
-    //println("The basis, with function calls " + topLevelFunctionCallsOf(basis))
-    //println(basis)
-    //println("The clauses")
-    //for (clause <- clauses) {
-    //  println(clause)
-
-    //  clause match {
-    //    case Iff(Variable(switch), cond) => {
-    //      toUnroll = toUnroll ++ topLevelFunctionCallsOf(cond)
-    //    }
-    //    case Implies(v @ Variable(switch), then) => {
-    //      if(!topLevelFunctionCallsOf(then).isEmpty) {
-    //        blockingClause = Not(v) :: blockingClause
-    //      }          
-    //    }
-    //    case Implies(Not(v @ Variable(switch)), elze) => {
-    //      if(!topLevelFunctionCallsOf(elze).isEmpty) {
-    //        blockingClause = v :: blockingClause
-    //      }
-    //    }
-    //    case _ => scala.Predef.error("Something wrong happened. Who added this clause? " + clause)
-    //  }
-    //}
-
-    //// I'm conjecturing that this loop terminates :D
-    //while(!toUnroll.isEmpty) {
-    //  println("Still to unroll now:")
-    //  println(toUnroll)
-    //  val copy = toUnroll
-    //  toUnroll = Set.empty
-
-    //  for(tu <- copy) {
-    //    val unrolled = unroll(tu) // this is a list of formulas
-    //    for(formula <- unrolled) {
-    //      val (basis2, clauses2, _) = clausifyITE(formula)
-    //      println(tu + " unrolled gives us " + basis2 + "\n" + clauses2.map(_.toString).mkString("\n"))
-    //      toUnroll = toUnroll ++ topLevelFunctionCallsOf(basis2)
-    //      clauses = basis2 :: (clauses2 ::: clauses)
-    //      for (clause <- clauses2) {
-    //        clause match {
-    //          case Iff(Variable(switch), cond) => {
-    //            toUnroll = toUnroll ++ topLevelFunctionCallsOf(cond)
-    //          }
-    //          case Implies(v @ Variable(switch), then) => {
-    //            if(!topLevelFunctionCallsOf(then).isEmpty) {
-    //              blockingClause = Not(v) :: blockingClause
-    //            }          
-    //          }
-    //          case Implies(Not(v @ Variable(switch)), elze) => {
-    //            if(!topLevelFunctionCallsOf(elze).isEmpty) {
-    //              blockingClause = v :: blockingClause
-    //            }
-    //          }
-    //          case _ => scala.Predef.error("Something wrong happened. Who added this clause? " + clause)
-    //        }
-    //      }
-    //    }
-    //  }
-    //  everythingEverUnrolled = everythingEverUnrolled ++ copy
-    //  toUnroll = toUnroll -- everythingEverUnrolled
-    //}
-    //println("The booleans.")
-    //println(bools)
-
-    //println("The blocking clause.")
-    //println(And(blockingClause))
-
-
-    //val toConvert = And(basis, And(clauses))
-
-    //val result : (Option[Boolean],Map[Identifier,Expr]) = toZ3Formula(z3, toConvert) match {
-    //  case None => (None, Map.empty) // means it could not be translated
-    //  case Some(z3f) => {
-    //    z3.assertCnstr(z3f)
-    //    //println(z3f)
-
-    //    //z3.assertCnstr(toZ3Formula(z3, And(blockingClause)).get)
-
-    //    var foundDefinitiveSolution = false
-
-    //    val blockingClauseAsZ3 : Seq[Z3AST] = blockingClause.map(toZ3Formula(z3, _).get)
-
-    //    val actualResult : (Option[Boolean],Map[Identifier,Expr]) = (z3.checkAssumptions(blockingClauseAsZ3 : _*) match {
-    //      case (Some(true), m, _) => {
-    //        import Evaluator._
-
-    //        // WE HAVE TO CHECK THE COUNTER-EXAMPLE.
-    //        val asMap = modelToMap(m, varsInVC)
-    //        lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n")
-    //        reporter.info("  - A candidate counter-example was found... Examining...")
-    //        reporter.info(modelAsString)
-
-    //        val evalResult = eval(asMap, toCheckAgainstModels)
-    //        
-    //        evalResult match {
-    //          case OK(BooleanLiteral(true)) => {
-    //            reporter.error("Counter-example found and confirmed:")
-    //            reporter.error(modelAsString)
-    //            foundDefinitiveSolution = true
-    //            (Some(false), asMap)
-    //          }
-    //          case OK(BooleanLiteral(false)) => {
-    //            reporter.info("Not a valid counter-example. Must.. keep.. going..")
-    //            (None, asMap)
-    //          }
-    //          case InfiniteComputation() => {
-    //            reporter.info("Model seems to lead to divergent computation.")
-    //            reporter.error(modelAsString)
-    //            foundDefinitiveSolution = true
-    //            (None, asMap)
-    //          }
-    //          case RuntimeError(msg) => {
-    //            reporter.info("Model leads to runtime error: " + msg)
-    //            reporter.error(modelAsString)
-    //            foundDefinitiveSolution = true
-    //            (Some(false), asMap)
-    //          }
-    //          case t @ TypeError(_,_) => {
-    //            scala.Predef.error("Type error in model evaluation.\n" + t.msg)
-    //          }
-    //          case _ => {
-    //            scala.Predef.error("Why am I here?")
-    //          }
-    //        }
-    //      }
-    //      case (Some(false), _, core) => {
-    //        reporter.info("Here is the core : " + core.map(_.toString).mkString(", "))
-    //        (Some(true), Map.empty)
-    //      }
-    //      case (None, m, _) => {
-    //        reporter.warning("Z3 doesn't know because: " + z3.getSearchFailure.message)
-    //        (None, Map.empty)
-    //      }
-    //    })
-    //    actualResult
-    //  }
-    //}
-    //result
+    val asMap = modelToMap(model, variables)
+    model.delete
+
+    lazy val modelAsString = asMap.toList.map(p => p._1 + " -> " + p._2).mkString("\n")
+    val evalResult = eval(asMap, formula)
+
+    evalResult match {
+      case OK(BooleanLiteral(true)) => {
+        reporter.info("Model validated:")
+        reporter.info(modelAsString)
+        (true, asMap)
+      }
+      case RuntimeError(msg) => {
+        reporter.info("Model leads to runtime error: " + msg)
+        reporter.error(modelAsString)
+        (true, asMap)
+      }
+      case OK(BooleanLiteral(false)) => {
+        reporter.info("Invalid model.")
+        (false, Map.empty)
+      }
+      case other => {
+        reporter.error("Something went wrong. While evaluating the model, we got this: " + other)
+        (false, Map.empty)
+      }
+    }
   }
 
   // Returns between 0 and 2 tautologies with respect to the interpretation of
@@ -523,14 +404,16 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac
     val postExpr = if(fd.hasPostcondition) {
       val post = expandLets(matchToIfThenElse(fd.postcondition.get))
       val substMap = Map[Expr,Expr]((fd.args.map(_.toVariable) zip args) : _*) + (ResultVariable() -> functionInvocation)
-      val newBody = /*partialEvaluator(*/replace(substMap, post)/*)*/
+      //val newBody = partialEvaluator(replace(substMap, post))
+      val newBody = replace(substMap, post)
       List(newBody)
     } else Nil
   
     val bodyExpr = if(fd.hasBody) {
       val body = expandLets(matchToIfThenElse(fd.body.get))
       val substMap = Map[Expr,Expr]((fd.args.map(_.toVariable) zip args) : _*)
-      val newBody = /*partialEvaluator(*/replace(substMap, body)/*)*/
+      //val newBody = partialEvaluator(replace(substMap, body))
+      val newBody = replace(substMap, body)
       List(Equals(functionInvocation, newBody))
     } else Nil
 
@@ -855,6 +738,7 @@ class FairZ3Solver(val reporter: Reporter) extends Solver(reporter) with Abstrac
           stillToUnroll = Set.empty
 
           for(stu <- (copy -- unrolledNow) if !wasUnrolledBefore(stu)) {
+            // println("unrolling " + stu)
             val unrolled : Seq[Expr] = unroll(stu) // that's between 0 and two formulas
             registerAsUnrolled(stu)
             unrolledNow = unrolledNow + stu
-- 
GitLab