diff --git a/src/main/scala/leon/solvers/string/StringSolver.scala b/src/main/scala/leon/solvers/string/StringSolver.scala
index 55ad7cf6c705735946b0744af1973192955a840f..64ee55a93e36b52f7388c1df907736af622cff0d 100644
--- a/src/main/scala/leon/solvers/string/StringSolver.scala
+++ b/src/main/scala/leon/solvers/string/StringSolver.scala
@@ -90,52 +90,15 @@ object StringSolver {
     rec(s)
   }
   
-  /** returns a simplified version of the problem. If it is not satisfiable, returns None. */
-  @tailrec def simplifyProblem(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
-    // Invariant: Every assigned var does not appear in the problem.
-    // 1. Merge constant in string forms
-    @tailrec def mergeConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
-      case Nil => Some(acc.toList)
-      case (sf, rhs)::q => mergeConstants(q, acc += ((reduceStringForm(sf), rhs)))
-    }
-    
-    // 2. Unsat if Const1 = Const2 but constants are different.
-    // 2bis.    if Const1 = Const2 and constants are same, remove equality.
-    @tailrec def simplifyConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
-      case Nil => Some(acc.toList)
-      case (Nil, rhs)::q => if("" != rhs) None else simplifyConstants(q, acc)
-      case (List(Left(c)), rhs)::q => if(c != rhs) None else simplifyConstants(q, acc)
-      case sentence::q => simplifyConstants(q, acc += sentence)
-    }
-    
-    // 3. Get new assignments from equations such as id = Const.
-    @tailrec def obtainAssignments(p: Problem, assignments: Assignment = Map()): Option[Assignment] = p match {
-      case Nil => Some(assignments)
-      case (List(Right(id)), rhs)::q =>
-        assignments.get(id) match { // It was assigned already.
-          case Some(v) =>
-            if(rhs != v) None else obtainAssignments(q, assignments)
-          case None => 
-            obtainAssignments(q, assignments + (id -> rhs))
-        }
-      case sentence::q => obtainAssignments(q, assignments)
-    }
-    val simplifiedOpt = mergeConstants(p.distinct)
-    .flatMap(simplifyConstants(_))
-    
-    simplifiedOpt match {
-      case None => None
-      case Some(simplified) =>
-        // 4. If there are new assignments, forward them to the equation and relaunch the simplification.
-        val newAssignmentsOpt = obtainAssignments(simplified)
-        newAssignmentsOpt match {
-          case Some(newAssignments) if newAssignments.nonEmpty =>
-             simplifyProblem(reduceProblem(newAssignments)(simplified), s ++ newAssignments)
-          case _ => Some((simplified, s))
-        }
-    }
+  /** Split the stringFrom at the last constant.
+    * @param s The concatenation of constants and variables  
+    * @return (a, b, c) such that a ++ Seq(b) ++ c == s and b is the last constant of s */
+  def splitAtLastConstant(s: StringForm): (StringForm, StringFormToken, StringForm) = {
+    val i = s.lastIndexWhere(x => x.isInstanceOf[Left[_, _]])
+    (s.take(i), s(i), s.drop(i+1))
   }
   
+  /** Use `val ConsReverse(init, last) = list` to retrieve the n-1 elements and the last element directly. */
   object ConsReverse {
     def unapply[A](l: List[A]): Option[(List[A], A)] = {
       if(l.nonEmpty) Some((l.init, l.last)) else None
@@ -143,143 +106,6 @@ object StringSolver {
     def apply[A](q: List[A], a: A): List[A] = q :+ a
   }
   
-  // Removes all constants from the left and right of the equations
-  def noLeftRightConstants(p: Problem): Option[Problem] = {
-    @tailrec def removeLeftconstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
-      case Nil => Some(acc.toList)
-      case ((Left(constant)::q, rhs))::ps =>
-        if(rhs.startsWith(constant)) {
-          removeLeftconstants(ps, acc += ((q, rhs.substring(constant.length))))
-        } else None
-      case (t@(q, rhs))::ps =>
-        removeLeftconstants(ps, acc += t)
-    }
-    
-    @tailrec def removeRightConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
-      case Nil => Some(acc.toList)
-      case (ConsReverse(q, Left(constant)), rhs)::ps =>
-        if(rhs.endsWith(constant)) {
-          removeRightConstants(ps, acc += ((q, rhs.substring(0, rhs.length - constant.length))))
-        } else None
-      case (t@(q, rhs))::ps =>
-        removeRightConstants(ps, acc += t)
-    }
-    
-    removeLeftconstants(p).flatMap(removeRightConstants(_))
-  }
-  
-  def splitAtLastConstant(s: StringForm): (StringForm, StringFormToken, StringForm) = {
-    val i = s.lastIndexWhere(x => x.isInstanceOf[Left[_, _]])
-    (s.take(i), s(i), s.drop(i+1))
-  }
-  
-  /** If constants have only one position in an equation, split the equation */
-  @tailrec def constantPropagate(p: Problem, assignments: Assignment = Map(), newProblem: ListBuffer[Equation] = ListBuffer()): Option[(Problem, Assignment)] = {
-    p match {
-      case Nil => 
-        Some((newProblem.toList, assignments))
-      case (ids, "")::q => // All identifiers should be empty.
-        val constants = ids.find { 
-          case Left(s) if s != "" => true
-          case Right(_) => false
-        }
-        (constants match {
-          case Some(_) => None
-          case None => // Ok now let's assign all variables to empty string.
-            val newMap = (ids.collect{ case Right(id) => id -> ""})
-            val newAssignments = (Option(assignments) /: newMap) {
-              case (None, (id, rhs)) => None 
-              case (Some(a), (id, rhs)) => 
-                a.get(id) match { // It was assigned already.
-                  case Some(v) =>
-                    if(rhs != v) None else Some(a)
-                  case None => 
-                    Some(a + (id -> rhs))
-                }
-            }
-            newAssignments
-        }) match {
-          case None => None
-          case Some(a) => 
-            constantPropagate(q, a, newProblem)
-        }
-      case (sentence@(ids, rhs))::q => // If the constants have an unique position, we should split the equation.
-        val constants = ids.collect { 
-          case l@Left(s) => s
-        }
-        // Check if constants form a partition in the string, i.e. getting the .indexOf() the first time is the only way to split the string.
-        
-        if(constants.length > 0) {
-          
-          var pos = -2
-          var lastPos = -2
-          var lastConst = ""
-          var invalid = false
-          
-          for(c <- constants) {
-            val i = rhs.indexOfSlice(c, pos)
-            lastPos = i
-            lastConst = c
-            if(i == -1) invalid = true
-            pos = i + c.length
-          }
-          if(invalid) None else {
-            val i = rhs.indexOfSlice(lastConst, lastPos + 1)
-            if(i == -1) { // OK it's the smallest position possible, hence we can split at the last position.
-              val (before, constant, after) = splitAtLastConstant(ids)
-              val firstConst = rhs.substring(0, lastPos)
-              val secondConst = rhs.substring(lastPos + lastConst.length)
-              constantPropagate((before, firstConst)::(after, secondConst)::q, assignments, newProblem)
-            } else {
-              // Other splitting strategy: independent constants ?
-              
-              
-              val constantsSplit = constants.flatMap{ c => {
-                val i = rhs.indexOfSlice(c, -1)
-                val j = rhs.indexOfSlice(c, i + 1)
-                if(j == -1 && i != -1) {
-                  List((c, i))
-                } else Nil
-              }}
-              
-              constantsSplit match {
-                case Nil =>
-                 constantPropagate(q, assignments, newProblem += sentence)
-                case ((c, i)::_) =>
-                  val (before, after) = ids.splitAt(ids.indexOf(Left(c)))
-                  val firstconst = rhs.substring(0, i)
-                  val secondConst = rhs.substring(i+c.length)
-                  constantPropagate((before, firstconst)::(after.tail, secondConst)::q, assignments, newProblem)
-              }
-              
-            }
-          }
-        } else {
-          constantPropagate(q, assignments, newProblem += sentence)
-        }
-      case sentence::q => constantPropagate(q, assignments, newProblem += sentence)
-    }
-  }
-  
-  /** Composition of simplifyProble and noLeftRightConstants */
-  def forwardStrategy(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
-    leon.utils.fixpoint[Option[(Problem, Assignment)]]{
-        case None => None
-        case Some((p: Problem, assignment: Assignment)) =>
-          val simplified = simplifyProblem(p, Map())
-          if(simplified == None) None else {
-            val simplified_problem = reduceProblem(simplified.get._2)(simplified.get._1)
-            val simplified2_problem = noLeftRightConstants(simplified_problem)
-            if(simplified2_problem == None) None else {
-              val simplified3_assignment = constantPropagate(simplified2_problem.get, simplified.get._2)
-              if(simplified3_assignment == None) None else {
-                val simplified3_problem = reduceProblem(simplified3_assignment.get._2)(simplified3_assignment.get._1)
-                Some((simplified3_problem, assignment ++ simplified3_assignment.get._2))
-              }
-            }
-          }
-    }(Option((p, s)))
-  }
   
   /** Returns all start positions of the string s in text.*/
   def occurrences(s: String, text: String): List[Int] = {
@@ -320,6 +146,230 @@ object StringSolver {
   }
   
   
+  /** A single pass simplification process. Converts as much as equations to assignments if possible. */
+  trait ProblemSimplicationPhase { self =>
+    def apply(p: Problem, s: Assignment) = run(p, s)
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)]
+    def andThen(other: ProblemSimplicationPhase): ProblemSimplicationPhase = new ProblemSimplicationPhase {
+      def run(p: Problem, s: Assignment) = {
+        ProblemSimplicationPhase.this.run(p, s) match {
+          case Some((p, s)) => other.run(p, s)
+          case None => None
+        }
+      }
+    }
+    /** Applies a phase until the output does not change anymore */
+    def loopUntilConvergence = new ProblemSimplicationPhase {
+      def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+        leon.utils.fixpoint[Option[(Problem, Assignment)]]{
+          case None => None
+          case Some((problem: Problem, assignment: Assignment)) => self.run(problem, assignment)
+        }(Option((p, s)))
+      }
+    }
+  }
+  def loopUntilConvergence(psp: ProblemSimplicationPhase) = psp.loopUntilConvergence 
+  
+  /** Removes duplicate equations from the problem */
+  object DistinctEquation extends ProblemSimplicationPhase {
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+      Some((p.distinct, s))
+    }
+  }
+  
+  /** Merge constant in string forms */
+  object MergeConstants extends ProblemSimplicationPhase {
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+      @tailrec def mergeConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
+        case Nil => Some(acc.toList)
+        case (sf, rhs)::q => mergeConstants(q, acc += ((reduceStringForm(sf), rhs)))
+      }
+      mergeConstants(p).map((_, s))
+    }
+  }
+  
+  /** Unsat if Const1 = Const2 but constants are different.
+    * if Const1 = Const2 and constants are same, remove equality. */
+  object SimplifyConstants extends ProblemSimplicationPhase {
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+      @tailrec def simplifyConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
+        case Nil => Some(acc.toList)
+        case (Nil, rhs)::q => if("" != rhs) None else simplifyConstants(q, acc)
+        case (List(Left(c)), rhs)::q => if(c != rhs) None else simplifyConstants(q, acc)
+        case sentence::q => simplifyConstants(q, acc += sentence)
+      }
+      simplifyConstants(p).map((_, s))
+    }
+  }
+  
+  /** . Get new assignments from equations such as id = Const, and propagate them */
+  object PropagateAssignments extends ProblemSimplicationPhase {
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+      @tailrec def obtainAssignments(p: Problem, assignments: Assignment = Map()): Option[Assignment] = p match {
+        case Nil => Some(assignments)
+        case (List(Right(id)), rhs)::q =>
+          assignments.get(id) match { // It was assigned already.
+            case Some(v) =>
+              if(rhs != v) None else obtainAssignments(q, assignments)
+            case None => 
+              obtainAssignments(q, assignments + (id -> rhs))
+          }
+        case sentence::q => obtainAssignments(q, assignments)
+      }
+      obtainAssignments(p, s).map(newAssignments => {
+        val newProblem = if(newAssignments.nonEmpty) reduceProblem(newAssignments)(p) else p
+        (newProblem, s ++ newAssignments)
+      })
+    }
+  }
+
+  /** Removes all constants from the left of the equations (i.e. "ab" x ... = "abcd" ==> x ... = "cd" ) */
+  object RemoveLeftConstants extends ProblemSimplicationPhase {
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+      @tailrec def removeLeftconstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
+        case Nil => Some(acc.toList)
+        case ((Left(constant)::q, rhs))::ps =>
+          if(rhs.startsWith(constant)) {
+            removeLeftconstants(ps, acc += ((q, rhs.substring(constant.length))))
+          } else None
+        case (t@(q, rhs))::ps =>
+          removeLeftconstants(ps, acc += t)
+      }
+      removeLeftconstants(p).map((_, s))
+    }
+  }
+  
+  /** Removes all constants from the right of the equations (i.e. ... x "cd" = "abcd" ==> ... x = "ab" ) */
+  object RemoveRightConstants extends ProblemSimplicationPhase {
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+      @tailrec def removeRightConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match {
+        case Nil => Some(acc.toList)
+        case (ConsReverse(q, Left(constant)), rhs)::ps =>
+          if(rhs.endsWith(constant)) {
+            removeRightConstants(ps, acc += ((q, rhs.substring(0, rhs.length - constant.length))))
+          } else None
+        case (t@(q, rhs))::ps =>
+          removeRightConstants(ps, acc += t)
+      }
+      removeRightConstants(p).map((_, s))
+    }
+  }
+   
+  /** If constants can have only one position in an equation, splits the equation.
+    * If an equation is empty, treats all left-hand-side identifiers as empty.
+    * Requires MergeConstants so that empty constants have been removed. */
+  object PropagateMiddleConstants extends ProblemSimplicationPhase {
+    def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = {
+      @tailrec def constantPropagate(p: Problem, assignments: Assignment = Map(), newProblem: ListBuffer[Equation] = ListBuffer()): Option[(Problem, Assignment)] = {
+        p match {
+          case Nil => 
+            Some((newProblem.toList, assignments))
+          case (ids, "")::q => // All identifiers should be empty.
+            val constants = ids.find { 
+              case Left(s) if s != "" => true
+              case Right(_) => false
+            }
+            (constants match {
+              case Some(_) => None
+              case None => // Ok now let's assign all variables to empty string.
+                val newMap = (ids.collect{ case Right(id) => id -> ""})
+                val newAssignments = (Option(assignments) /: newMap) {
+                  case (None, (id, rhs)) => None 
+                  case (Some(a), (id, rhs)) => 
+                    a.get(id) match { // It was assigned already.
+                      case Some(v) =>
+                        if(rhs != v) None else Some(a)
+                      case None => 
+                        Some(a + (id -> rhs))
+                    }
+                }
+                newAssignments
+            }) match {
+              case None => None
+              case Some(a) => 
+                constantPropagate(q, a, newProblem)
+            }
+          case (sentence@(ids, rhs))::q => // If the constants have an unique position, we should split the equation.
+            val constants = ids.collect { 
+              case l@Left(s) => s
+            }
+            // Check if constants form a partition in the string, i.e. getting the .indexOf() the first time is the only way to split the string.
+            
+            if(constants.length > 0) {
+              
+              var pos = -2
+              var lastPos = -2
+              var lastConst = ""
+              var invalid = false
+              
+              for(c <- constants) {
+                val i = rhs.indexOfSlice(c, pos)
+                lastPos = i
+                lastConst = c
+                if(i == -1) invalid = true
+                pos = i + c.length
+              }
+              if(invalid) None else {
+                val i = rhs.indexOfSlice(lastConst, lastPos + 1)
+                if(i == -1) { // OK it's the smallest position possible, hence we can split at the last position.
+                  val (before, constant, after) = splitAtLastConstant(ids)
+                  val firstConst = rhs.substring(0, lastPos)
+                  val secondConst = rhs.substring(lastPos + lastConst.length)
+                  constantPropagate((before, firstConst)::(after, secondConst)::q, assignments, newProblem)
+                } else {
+                  // Other splitting strategy: independent constants ?
+                  
+                  val constantsSplit = constants.flatMap{ c => {
+                    val i = rhs.indexOfSlice(c, -1)
+                    val j = rhs.indexOfSlice(c, i + 1)
+                    if(j == -1 && i != -1) {
+                      List((c, i))
+                    } else Nil
+                  }}
+                  
+                  constantsSplit match {
+                    case Nil =>
+                     constantPropagate(q, assignments, newProblem += sentence)
+                    case ((c, i)::_) =>
+                      val (before, after) = ids.splitAt(ids.indexOf(Left(c)))
+                      val firstconst = rhs.substring(0, i)
+                      val secondConst = rhs.substring(i+c.length)
+                      constantPropagate((before, firstconst)::(after.tail, secondConst)::q, assignments, newProblem)
+                  }
+                  
+                }
+              }
+            } else {
+              constantPropagate(q, assignments, newProblem += sentence)
+            }
+          case sentence::q => constantPropagate(q, assignments, newProblem += sentence)
+        }
+      }
+      constantPropagate(p).map(ps => {
+        val newP = if(ps._2.nonEmpty) reduceProblem(ps._2)(p) else p
+        (ps._1, s ++ ps._2)
+      })
+    }
+  }
+  
+  /** returns a simplified version of the problem. If it is not satisfiable, returns None. */
+  val simplifyProblem: ProblemSimplicationPhase = {
+    loopUntilConvergence(DistinctEquation andThen
+    MergeConstants andThen
+    SimplifyConstants andThen
+    PropagateAssignments)
+  }
+  
+  /** Removes all constants from the left and right of the equations */
+  val noLeftRightConstants: ProblemSimplicationPhase = {
+    RemoveLeftConstants andThen RemoveRightConstants
+  }
+  
+  /** Composition of simplifyProblem and noLeftRightConstants */
+  val forwardStrategy =
+    loopUntilConvergence(simplifyProblem andThen noLeftRightConstants andThen PropagateMiddleConstants)
+  
+  
   /** Solves the equation   x1x2x3...xn = CONSTANT
     * Prioritizes split positions in the CONSTANT that are word boundaries
     * Prioritizes variables which have a higher frequency */
@@ -337,9 +387,7 @@ object StringSolver {
         }).get
         val pos = prioritizedPositions(rhs)
         val numBestVars = ids.count { x => x == bestVar }
-        
-        
-        
+
         if(worstScore == bestScore) {
           for{
             i <- pos // Prioritization on positions which are separators.
@@ -458,7 +506,7 @@ object StringSolver {
   
   /** Solves the problem and returns all possible satisfying assignment */
   def solve(p: Problem): Stream[Assignment] = {
-    val realProblem = forwardStrategy(p, Map())
+    val realProblem = forwardStrategy.run(p, Map())
     /*if(realProblem.nonEmpty && realProblem.get._1.nonEmpty) {
       println("Problem:\n"+renderProblem(p))
       println("Solutions:\n"+realProblem.get._2)
diff --git a/src/test/scala/leon/integration/solvers/StringSolverSuite.scala b/src/test/scala/leon/integration/solvers/StringSolverSuite.scala
index 3045ea0dc4f407e78553ade6fd5a289b9780cbc3..f7def0be36f864e9bd8666b03d0a9764a57ad728 100644
--- a/src/test/scala/leon/integration/solvers/StringSolverSuite.scala
+++ b/src/test/scala/leon/integration/solvers/StringSolverSuite.scala
@@ -44,10 +44,10 @@ class StringSolverSuite extends FunSuite with Matchers {
   }
   
   test("noLeftRightConstants") {
-    noLeftRightConstants(List("x" === "1")) should equal (Some(List("x" === "1")))
-    noLeftRightConstants(List("y2" === "12")) should equal (Some(List("y" === "1")))
-    noLeftRightConstants(List("3z" === "31")) should equal (Some(List("z" === "1")))
-    noLeftRightConstants(List("1u2" === "1, 2")) should equal (Some(List("u" === ", ")))
+    noLeftRightConstants(List("x" === "1"), Map()) should equal (Some((List("x" === "1"), Map())))
+    noLeftRightConstants(List("y2" === "12"), Map()) should equal (Some((List("y" === "1"), Map())))
+    noLeftRightConstants(List("3z" === "31"), Map()) should equal (Some((List("z" === "1"), Map())))
+    noLeftRightConstants(List("1u2" === "1, 2"), Map()) should equal (Some((List("u" === ", "), Map())))
   }
   test("forwardStrategy") {
     forwardStrategy(List("x3" === "123", "xy" === "1245"), Map()) should equal (Some((Nil, Map(x -> "12", y -> "45"))))