From 1cbb8bee2f95a2de628bfd4ea74ec1de8e4d7064 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mika=C3=ABl=20Mayer?= <a-mikmay@microsoft.com>
Date: Wed, 25 Nov 2015 17:15:01 +0100
Subject: [PATCH] Better support for splitting at word boundaries. Added
 corresponding test case

---
 .../leon/solvers/string/StringSolver.scala    | 38 +++++++++-
 .../solvers/StringSolverSuite.scala           | 69 ++++++++++---------
 2 files changed, 72 insertions(+), 35 deletions(-)

diff --git a/src/main/scala/leon/solvers/string/StringSolver.scala b/src/main/scala/leon/solvers/string/StringSolver.scala
index 2ac5e49e6..b1b93568a 100644
--- a/src/main/scala/leon/solvers/string/StringSolver.scala
+++ b/src/main/scala/leon/solvers/string/StringSolver.scala
@@ -120,7 +120,7 @@ object StringSolver {
         }
       case sentence::q => obtainAssignments(q, assignments)
     }
-    val simplifiedOpt = mergeConstants(p)
+    val simplifiedOpt = mergeConstants(p.distinct)
     .flatMap(simplifyConstants(_))
     
     simplifiedOpt match {
@@ -338,6 +338,8 @@ object StringSolver {
         val pos = prioritizedPositions(rhs)
         val numBestVars = ids.count { x => x == bestVar }
         
+        
+        
         if(worstScore == bestScore) {
           for{
             i <- pos // Prioritization on positions which are separators.
@@ -348,9 +350,33 @@ object StringSolver {
             if !remaining_split.contains(x) || remaining_split(x) == xvalue
           } yield (remaining_split + (x -> xvalue))
         } else { // A variable appears more than others in the same equation, so its changes are going to propagate more.
-          val strings = (for{ i <- pos
+          val indexBestVar = ids.indexOf(bestVar)
+          val strings = if(indexBestVar == 0) { // Test only string prefixes or empty string
+             (for{j <- (rhs.length to 1 by -1).toStream
+                  if pos contains j} yield rhs.substring(0, j)) #:::
+             (for{j <- (rhs.length to 1 by -1).toStream
+                  if !(pos contains j)} yield rhs.substring(0, j)) #:::
+              Stream("")
+          } else {
+            val lastIndexBestVar = ids.lastIndexOf(bestVar)
+            if(lastIndexBestVar == ids.length - 1) {
+               (for{ i <- pos.toStream // Try to maximize the size of the string from the start
+                     if i != rhs.length
+               } yield rhs.substring(i)) #:::
+               Stream("")
+            } else { // Inside, can be anything.
+              (for{ i <- pos.toStream // Try to maximize the size of the string from the start
+               if i != rhs.length
+               j <- rhs.length to (i+1) by -1
+               if pos contains j} yield rhs.substring(i, j)) #:::
+               (for{ i <- pos.toStream
                if i != rhs.length
-               j <- (i + 1) to rhs.length} yield rhs.substring(i, j)) #::: Stream("")
+               j <- rhs.length to (i+1) by -1
+               if !(pos contains j)} yield rhs.substring(i, j)) #:::
+               Stream("")
+            }
+          }
+          //println("Best variable:" + bestVar + " going to test " + strings.toList)
           
           (for(str <- strings.distinct
                if java.util.regex.Pattern.quote(str).r.findAllMatchIn(rhs).length >= numBestVars
@@ -433,6 +459,12 @@ object StringSolver {
   /** Solves the problem and returns all possible satisfying assignment */
   def solve(p: Problem): Stream[Assignment] = {
     val realProblem = forwardStrategy(p, Map())
+    /*if(realProblem.nonEmpty) {
+      println("Problem:\n"+renderProblem(p))
+      println("Solutions:\n"+realProblem.get._2)
+      println("Real problem:\n"+renderProblem(realProblem.get._1))
+    }*/
+    
     realProblem match {
       case None => 
         Stream.Empty
diff --git a/src/test/scala/leon/integration/solvers/StringSolverSuite.scala b/src/test/scala/leon/integration/solvers/StringSolverSuite.scala
index dc34901da..8d5530578 100644
--- a/src/test/scala/leon/integration/solvers/StringSolverSuite.scala
+++ b/src/test/scala/leon/integration/solvers/StringSolverSuite.scala
@@ -6,6 +6,7 @@ import leon.test.helpers.ExpressionsDSL
 import leon.solvers.string.StringSolver
 import leon.purescala.Common.FreshIdentifier
 import leon.purescala.Common.Identifier
+import scala.collection.mutable.{HashMap => MMap}
 
 /**
  * @author Mikael
@@ -74,9 +75,9 @@ class StringSolverSuite extends FunSuite with Matchers {
     simpleSplit(List(x, y, x), "aba").toList should equal (List(Map(x -> "", y -> "aba"), Map(x -> "a", y -> "b")))
   }
   
-  test("simpleSplitPriority") {
+  test("simpleSplitPriority") { // Just guesses some values for y.
     implicit val stats = Map(x -> 1, y -> 2)
-    simpleSplit(List(x, y, y, y), "a121212").toList should equal (List(Map(y -> "1"), Map(y -> "12"), Map(y -> "2"), Map(y -> "")))
+    simpleSplit(List(x, y, y, y), "a121212").toList should equal (List(Map(y -> "12"), Map(y -> "2"), Map(y -> "")))
   }
 
   
@@ -141,23 +142,23 @@ class StringSolverSuite extends FunSuite with Matchers {
                  x -> complexString2,
                  z -> complexString3)))
   }
-  
-  test("ListInt") {
-    var idMap = Map[String, Identifier]()
-    def makeSf(lhs: String): StringForm = {
-      for(elem <- lhs.split("\\+").toList) yield {
-        if(elem.startsWith("\"")) {
-          Left(elem.substring(1, elem.length - 1))
-        } else {
-          val id = idMap.getOrElse(elem, {
-            val res = FreshIdentifier(elem)
-            idMap += elem -> res
-            res
-          })
-          Right(id)
-        }
+  def makeSf(lhs: String)(implicit idMap: MMap[String, Identifier]): StringForm = {
+    for(elem <- lhs.split("\\+").toList) yield {
+      if(elem.startsWith("\"")) {
+        Left(elem.substring(1, elem.length - 1))
+      } else {
+        val id = idMap.getOrElse(elem, {
+          val res = FreshIdentifier(elem)
+          idMap += elem -> res
+          res
+        })
+        Right(id)
       }
     }
+  }
+  
+  test("ListInt") {
+    implicit val idMap = MMap[String, Identifier]()
     val lhs1 = makeSf("""const8+const4+"12"+const5+const+"-1"+const1+const3+const2+const6+const9""")
     val lhs2 = makeSf("""const8+const4+"1"+const5+const3+const6+const9""")
     val lhs3 = makeSf("""const8+const7+const9""")
@@ -168,21 +169,25 @@ class StringSolverSuite extends FunSuite with Matchers {
     solve(problem) should not be 'empty
   }
   
-  test("solveJadProblem") {
+  test("ListInt as List(...)") {
+    implicit val idMap = MMap[String, Identifier]()
+    val lhs1 = makeSf("const8+const7+const9")
+    val rhs1 = "List()"
+    val lhs2 = makeSf("""const8+const4+"1"+const5+const3+const6+const9""")
+    val rhs2 = "List(1)"
+    val lhs3 = makeSf("""const8+const4+"12"+const5+const+"-1"+const1+const3+const2+const6+const9""")
+    val rhs3 = "List(12, -1)"
+    val problem = List((lhs1, rhs1), (lhs2, rhs2), (lhs3, rhs3))
+    val solution = solve(problem) 
+    solution should not be 'empty
+    val firstSolution = solution(0)
+    firstSolution(idMap("const8")) should equal("List(")
+  }
+  
+  /*test("solveJadProblem") {
     val lhs = """const26+const22+const7+const+const8+const3+const9+const5+"5"+const6+const10+const23+const18+const11+const+const12+const19+const18+const7+const1+const8+const2+const9+const4+const10+const19+const18+const13+const+const14+const3+const15+const5+"5"+const6+const16+const4+const17+const19+const18+const11+const1+const12+const19+const18+const13+const1+const14+const2+const15+const4+const16+const5+"5"+const6+const17+const19+const21+const20+const20+const20+const20+const20+const24+const27"""
-    var idMap = Map[String, Identifier]()
-    val lhsSf = for(elem <- lhs.split("\\+")) yield {
-      if(elem.startsWith("\"")) {
-        Left(elem.substring(1, elem.length - 1))
-      } else {
-        val id = idMap.getOrElse(elem, {
-          val res = FreshIdentifier(elem)
-          idMap += elem -> res
-          res
-        })
-        Right(id)
-      }
-    }
+    implicit val idMap = MMap[String, Identifier]()
+    val lhsSf = makeSf(lhs)
     
     val expected = """T1: call Push(5)
 T1: internal
@@ -195,5 +200,5 @@ T2: ret Pop() -> 5"""
     println("Problem to solve:" + StringSolver.renderProblem(problem))
     
     solve(problem) should not be 'empty
-  }
+  }*/
 }
\ No newline at end of file
-- 
GitLab