diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala
index fd8fa2ed0e404b7004ff14373e5495b75322e4c7..11e98e534e5f5f15b6b8e51ee322373469404ae5 100644
--- a/src/main/scala/leon/purescala/PrettyPrinter.scala
+++ b/src/main/scala/leon/purescala/PrettyPrinter.scala
@@ -1,494 +1,470 @@
 package leon
 package purescala
 
-/** This pretty-printer uses Unicode for some operators, to make sure we
- * distinguish PureScala from "real" Scala (and also because it's cute). */
-object PrettyPrinter {
-  import Common._
-  import Trees._
-  import TypeTrees._
-  import Definitions._
+import Common._
+import Trees._
+import TypeTrees._
+import Definitions._
 
-  import java.lang.StringBuffer
-
-  def apply(tree: Expr): String = {
-    val retSB = pp(tree, new StringBuffer, 0)
-    retSB.toString
-  }
+import java.lang.StringBuffer
 
-  def apply(tpe: TypeTree): String = {
-    val retSB = pp(tpe, new StringBuffer, 0)
-    retSB.toString
-  }
+/** This pretty-printer uses Unicode for some operators, to make sure we
+ * distinguish PureScala from "real" Scala (and also because it's cute). */
+class PrettyPrinter(sb: StringBuffer = new StringBuffer) {
+  override def toString = sb.toString
 
-  def apply(defn: Definition): String = {
-    val retSB = pp(defn, new StringBuffer, 0)
-    retSB.toString
+  def append(str: String) {
+    sb.append(str)
   }
 
-  private def ind(sb: StringBuffer, lvl: Int) : StringBuffer = {
+  def ind(lvl: Int) {
     sb.append("  " * lvl)
-    sb
   }
 
   // EXPRESSIONS
   // all expressions are printed in-line
-  private def ppUnary(sb: StringBuffer, expr: Expr, op1: String, op2: String, lvl: Int): StringBuffer = {
-    var nsb: StringBuffer = sb
-    nsb.append(op1)
-    nsb = pp(expr, nsb, lvl)
-    nsb.append(op2)
-    nsb
+  def ppUnary(expr: Expr, op1: String, op2: String, lvl: Int) {
+    sb.append(op1)
+    pp(expr, lvl)
+    sb.append(op2)
   }
 
-  private def ppBinary(sb: StringBuffer, left: Expr, right: Expr, op: String, lvl: Int): StringBuffer = {
-    var nsb: StringBuffer = sb
-    nsb.append("(")
-    nsb = pp(left, nsb, lvl)
-    nsb.append(op)
-    nsb = pp(right, nsb, lvl)
-    nsb.append(")")
-    nsb
+  def ppBinary(left: Expr, right: Expr, op: String, lvl: Int) {
+    sb.append("(")
+    pp(left, lvl)
+    sb.append(op)
+    pp(right, lvl)
+    sb.append(")")
   }
 
-  private def ppNary(sb: StringBuffer, exprs: Seq[Expr], pre: String, op: String, post: String, lvl: Int): StringBuffer = {
-    var nsb = sb
-    nsb.append(pre)
+  def ppNary(exprs: Seq[Expr], pre: String, op: String, post: String, lvl: Int) {
+    sb.append(pre)
     val sz = exprs.size
     var c = 0
 
     exprs.foreach(ex => {
-      nsb = pp(ex, nsb, lvl) ; c += 1 ; if(c < sz) nsb.append(op)
+      pp(ex, lvl) ; c += 1 ; if(c < sz) sb.append(op)
     })
 
-    nsb.append(post)
-    nsb
+    sb.append(post)
   }
 
-  private def pp(tree: Expr, sb: StringBuffer, lvl: Int): StringBuffer = tree match {
-    case Variable(id) => sb.append(id)
+  def idToString(id: Identifier): String = id.toString
+
+  def pp(tree: Expr, lvl: Int): Unit = tree match {
+    case Variable(id) => sb.append(idToString(id))
     case DeBruijnIndex(idx) => sb.append("_" + idx)
     case LetTuple(bs,d,e) => {
         //pp(e, pp(d, sb.append("(let (" + b + " := "), lvl).append(") in "), lvl).append(")")
-      sb.append("(let (" + bs.mkString(",") + " := ");
-      pp(d, sb, lvl)
+      sb.append("(let (" + bs.map(idToString _).mkString(",") + " := ");
+      pp(d, lvl)
       sb.append(") in\n")
-      ind(sb, lvl+1)
-      pp(e, sb, lvl+1)
+      ind(lvl+1)
+      pp(e, lvl+1)
       sb.append(")")
       sb
     }
     case Let(b,d,e) => {
         //pp(e, pp(d, sb.append("(let (" + b + " := "), lvl).append(") in "), lvl).append(")")
-      sb.append("(let (" + b + " := ");
-      pp(d, sb, lvl)
+      sb.append("(let (" + idToString(b) + " := ");
+      pp(d, lvl)
       sb.append(") in\n")
-      ind(sb, lvl+1)
-      pp(e, sb, lvl+1)
+      ind(lvl+1)
+      pp(e, lvl+1)
       sb.append(")")
       sb
     }
-    case And(exprs) => ppNary(sb, exprs, "(", " \u2227 ", ")", lvl)            // \land
-    case Or(exprs) => ppNary(sb, exprs, "(", " \u2228 ", ")", lvl)             // \lor
-    case Not(Equals(l, r)) => ppBinary(sb, l, r, " \u2260 ", lvl)    // \neq
-    case Iff(l,r) => ppBinary(sb, l, r, " <=> ", lvl)              
-    case Implies(l,r) => ppBinary(sb, l, r, " ==> ", lvl)              
-    case UMinus(expr) => ppUnary(sb, expr, "-(", ")", lvl)
-    case Equals(l,r) => ppBinary(sb, l, r, " == ", lvl)
+    case And(exprs) => ppNary(exprs, "(", " \u2227 ", ")", lvl)            // \land
+    case Or(exprs) => ppNary(exprs, "(", " \u2228 ", ")", lvl)             // \lor
+    case Not(Equals(l, r)) => ppBinary(l, r, " \u2260 ", lvl)    // \neq
+    case Iff(l,r) => ppBinary(l, r, " <=> ", lvl)              
+    case Implies(l,r) => ppBinary(l, r, " ==> ", lvl)              
+    case UMinus(expr) => ppUnary(expr, "-(", ")", lvl)
+    case Equals(l,r) => ppBinary(l, r, " == ", lvl)
     case IntLiteral(v) => sb.append(v)
     case BooleanLiteral(v) => sb.append(v)
     case StringLiteral(s) => sb.append("\"" + s + "\"")
     case UnitLiteral => sb.append("()")
-    case t@Tuple(exprs) => ppNary(sb, exprs, "(", ", ", ")", lvl)
+    case t@Tuple(exprs) => ppNary(exprs, "(", ", ", ")", lvl)
     case s@TupleSelect(t, i) => {
-      pp(t, sb, lvl)
+      pp(t, lvl)
       sb.append("._" + i)
       sb
     }
 
     case c@Choose(vars, pred) => {
-      var nsb = sb
-      nsb.append("choose("+vars.mkString(", ")+" => ")
-      nsb = pp(pred, nsb, lvl)
-      nsb.append(")")
-      nsb
+      sb.append("choose("+vars.map(idToString _).mkString(", ")+" => ")
+      pp(pred, lvl)
+      sb.append(")")
     }
 
     case CaseClass(cd, args) => {
-      var nsb = sb
-      nsb.append(cd.id)
+      sb.append(idToString(cd.id))
       if (cd.isCaseObject) {
-        nsb = ppNary(nsb, args, "", "", "", lvl)
+        ppNary(args, "", "", "", lvl)
       } else {
-        nsb = ppNary(nsb, args, "(", ", ", ")", lvl)
+        ppNary(args, "(", ", ", ")", lvl)
       }
-      nsb
+      sb
     }
     case CaseClassInstanceOf(cd, e) => {
-      var nsb = sb
-      nsb = pp(e, nsb, lvl)
-      nsb.append(".isInstanceOf[" + cd.id + "]")
-      nsb
+      pp(e, lvl)
+      sb.append(".isInstanceOf[" + idToString(cd.id) + "]")
+      sb
     }
-    case CaseClassSelector(_, cc, id) => pp(cc, sb, lvl).append("." + id)
+    case CaseClassSelector(_, cc, id) =>
+      pp(cc, lvl)
+      sb.append("." + idToString(id))
+
     case FunctionInvocation(fd, args) => {
-      var nsb = sb
-      nsb.append(fd.id)
-      nsb = ppNary(nsb, args, "(", ", ", ")", lvl)
-      nsb
+      sb.append(idToString(fd.id))
+      ppNary(args, "(", ", ", ")", lvl)
+      sb
     }
-    case Plus(l,r) => ppBinary(sb, l, r, " + ", lvl)
-    case Minus(l,r) => ppBinary(sb, l, r, " - ", lvl)
-    case Times(l,r) => ppBinary(sb, l, r, " * ", lvl)
-    case Division(l,r) => ppBinary(sb, l, r, " / ", lvl)
-    case Modulo(l,r) => ppBinary(sb, l, r, " % ", lvl)
-    case LessThan(l,r) => ppBinary(sb, l, r, " < ", lvl)
-    case GreaterThan(l,r) => ppBinary(sb, l, r, " > ", lvl)
-    case LessEquals(l,r) => ppBinary(sb, l, r, " \u2264 ", lvl)      // \leq
-    case GreaterEquals(l,r) => ppBinary(sb, l, r, " \u2265 ", lvl)   // \geq
-    case FiniteSet(rs) => if(rs.isEmpty) sb.append("\u2205") /* Ø */ else ppNary(sb, rs, "{", ", ", "}", lvl)
-    case FiniteMultiset(rs) => ppNary(sb, rs, "{|", ", ", "|}", lvl)
+    case Plus(l,r) => ppBinary(l, r, " + ", lvl)
+    case Minus(l,r) => ppBinary(l, r, " - ", lvl)
+    case Times(l,r) => ppBinary(l, r, " * ", lvl)
+    case Division(l,r) => ppBinary(l, r, " / ", lvl)
+    case Modulo(l,r) => ppBinary(l, r, " % ", lvl)
+    case LessThan(l,r) => ppBinary(l, r, " < ", lvl)
+    case GreaterThan(l,r) => ppBinary(l, r, " > ", lvl)
+    case LessEquals(l,r) => ppBinary(l, r, " \u2264 ", lvl)      // \leq
+    case GreaterEquals(l,r) => ppBinary(l, r, " \u2265 ", lvl)   // \geq
+    case FiniteSet(rs) => if(rs.isEmpty) sb.append("\u2205") /* Ø */ else ppNary(rs, "{", ", ", "}", lvl)
+    case FiniteMultiset(rs) => ppNary(rs, "{|", ", ", "|}", lvl)
     case EmptyMultiset(_) => sb.append("\u2205")                     // Ø
-    case Not(ElementOfSet(s,e)) => ppBinary(sb, s, e, " \u2209 ", lvl) // \notin
-    case ElementOfSet(s,e) => ppBinary(sb, s, e, " \u2208 ", lvl)    // \in
-    case SubsetOf(l,r) => ppBinary(sb, l, r, " \u2286 ", lvl)        // \subseteq
-    case Not(SubsetOf(l,r)) => ppBinary(sb, l, r, " \u2288 ", lvl)        // \notsubseteq
-    case SetMin(s) => pp(s, sb, lvl).append(".min")
-    case SetMax(s) => pp(s, sb, lvl).append(".max")
-    case SetUnion(l,r) => ppBinary(sb, l, r, " \u222A ", lvl)        // \cup
-    case MultisetUnion(l,r) => ppBinary(sb, l, r, " \u222A ", lvl)   // \cup
-    case MapUnion(l,r) => ppBinary(sb, l, r, " \u222A ", lvl)        // \cup
-    case SetDifference(l,r) => ppBinary(sb, l, r, " \\ ", lvl)       
-    case MultisetDifference(l,r) => ppBinary(sb, l, r, " \\ ", lvl)       
-    case SetIntersection(l,r) => ppBinary(sb, l, r, " \u2229 ", lvl) // \cap
-    case MultisetIntersection(l,r) => ppBinary(sb, l, r, " \u2229 ", lvl) // \cap
-    case SetCardinality(t) => ppUnary(sb, t, "|", "|", lvl)
-    case MultisetCardinality(t) => ppUnary(sb, t, "|", "|", lvl)
-    case MultisetPlus(l,r) => ppBinary(sb, l, r, " \u228E ", lvl)    // U+
-    case MultisetToSet(e) => pp(e, sb, lvl).append(".toSet")
+    case Not(ElementOfSet(s,e)) => ppBinary(s, e, " \u2209 ", lvl) // \notin
+    case ElementOfSet(s,e) => ppBinary(s, e, " \u2208 ", lvl)    // \in
+    case SubsetOf(l,r) => ppBinary(l, r, " \u2286 ", lvl)        // \subseteq
+    case Not(SubsetOf(l,r)) => ppBinary(l, r, " \u2288 ", lvl)        // \notsubseteq
+    case SetMin(s) => pp(s, lvl); sb.append(".min")
+    case SetMax(s) => pp(s, lvl); sb.append(".max")
+    case SetUnion(l,r) => ppBinary(l, r, " \u222A ", lvl)        // \cup
+    case MultisetUnion(l,r) => ppBinary(l, r, " \u222A ", lvl)   // \cup
+    case MapUnion(l,r) => ppBinary(l, r, " \u222A ", lvl)        // \cup
+    case SetDifference(l,r) => ppBinary(l, r, " \\ ", lvl)       
+    case MultisetDifference(l,r) => ppBinary(l, r, " \\ ", lvl)       
+    case SetIntersection(l,r) => ppBinary(l, r, " \u2229 ", lvl) // \cap
+    case MultisetIntersection(l,r) => ppBinary(l, r, " \u2229 ", lvl) // \cap
+    case SetCardinality(t) => ppUnary(t, "|", "|", lvl)
+    case MultisetCardinality(t) => ppUnary(t, "|", "|", lvl)
+    case MultisetPlus(l,r) => ppBinary(l, r, " \u228E ", lvl)    // U+
+    case MultisetToSet(e) => pp(e, lvl); sb.append(".toSet")
     case FiniteMap(rs) => {
       sb.append("{")
       val sz = rs.size
       var c = 0
       rs.foreach{case (k, v) => {
-        pp(k, sb, lvl); sb.append(" -> "); pp(v, sb, lvl); c += 1 ; if(c < sz) sb.append(", ")
+        pp(k, lvl); sb.append(" -> "); pp(v, lvl); c += 1 ; if(c < sz) sb.append(", ")
       }}
       sb.append("}")
     }
     case MapGet(m,k) => {
-      var nsb = sb
-      pp(m, nsb, lvl)
-      nsb = ppNary(nsb, Seq(k), "(", ",", ")", lvl)
-      nsb
+      pp(m, lvl)
+      ppNary(Seq(k), "(", ",", ")", lvl)
+      sb
     }
     case MapIsDefinedAt(m,k) => {
-      var nsb = sb
-      pp(m, nsb, lvl)
-      nsb.append(".isDefinedAt")
-      nsb = ppNary(nsb, Seq(k), "(", ",", ")", lvl)
-      nsb
+      pp(m, lvl)
+      sb.append(".isDefinedAt")
+      ppNary(Seq(k), "(", ",", ")", lvl)
+      sb
     }
     case ArrayLength(a) => {
-      pp(a, sb, lvl)
+      pp(a, lvl)
       sb.append(".length")
     }
     case ArrayClone(a) => {
-      pp(a, sb, lvl)
+      pp(a, lvl)
       sb.append(".clone")
     }
     case fill@ArrayFill(size, v) => {
       sb.append("Array.fill(")
-      pp(size, sb, lvl)
+      pp(size, lvl)
       sb.append(")(")
-      pp(v, sb, lvl)
+      pp(v, lvl)
       sb.append(")")
     }
     case am@ArrayMake(v) => {
       sb.append("Array.make(")
-      pp(v, sb, lvl)
+      pp(v, lvl)
       sb.append(")")    
     }
     case sel@ArraySelect(ar, i) => {
-      pp(ar, sb, lvl)
+      pp(ar, lvl)
       sb.append("(")
-      pp(i, sb, lvl)
+      pp(i, lvl)
       sb.append(")")
     }
     case up@ArrayUpdated(ar, i, v) => {
-      pp(ar, sb, lvl)
+      pp(ar, lvl)
       sb.append(".updated(")
-      pp(i, sb, lvl)
+      pp(i, lvl)
       sb.append(", ")
-      pp(v, sb, lvl)
+      pp(v, lvl)
       sb.append(")")
     }
     case FiniteArray(exprs) => {
-      ppNary(sb, exprs, "Array(", ", ", ")", lvl)
+      ppNary(exprs, "Array(", ", ", ")", lvl)
     }
 
     case Distinct(exprs) => {
-      var nsb = sb
-      nsb.append("distinct")
-      nsb = ppNary(nsb, exprs, "(", ", ", ")", lvl)
-      nsb
+      sb.append("distinct")
+      ppNary(exprs, "(", ", ", ")", lvl)
     }
     
     case IfExpr(c, t, e) => {
-      var nsb = sb
-      nsb.append("if (")
-      nsb = pp(c, nsb, lvl)
-      nsb.append(")\n")
-      ind(nsb, lvl+1)
-      nsb = pp(t, nsb, lvl+1)
-      nsb.append("\n")
-      ind(nsb, lvl)
-      nsb.append("else\n")
-      ind(nsb, lvl+1)
-      nsb = pp(e, nsb, lvl+1)
-      nsb
-      //nsb.append(") {\n")
-      //ind(nsb, lvl+1)
-      //nsb = pp(t, nsb, lvl+1)
-      //nsb.append("\n")
-      //ind(nsb, lvl)
-      //nsb.append("} else {\n")
-      //ind(nsb, lvl+1)
-      //nsb = pp(e, nsb, lvl+1)
-      //nsb.append("\n")
-      //ind(nsb, lvl)
-      //nsb.append("}")
-      //nsb
+      sb.append("if (")
+      pp(c, lvl)
+      sb.append(")\n")
+      ind(lvl+1)
+      pp(t, lvl+1)
+      sb.append("\n")
+      ind(lvl)
+      sb.append("else\n")
+      ind(lvl+1)
+      pp(e, lvl+1)
     }
 
     case mex @ MatchExpr(s, csc) => {
-      def ppc(sb: StringBuffer, p: Pattern): StringBuffer = p match {
+      def ppc(p: Pattern): Unit = p match {
         //case InstanceOfPattern(None,     ctd) =>
         //case InstanceOfPattern(Some(id), ctd) =>
         case CaseClassPattern(bndr,     ccd, subps) => {
-          var nsb = sb
-          bndr.foreach(b => nsb.append(b + " @ "))
-          nsb.append(ccd.id).append("(")
+          bndr.foreach(b => sb.append(idToString(b) + " @ "))
+          sb.append(idToString(ccd.id)).append("(")
           var c = 0
           val sz = subps.size
           subps.foreach(sp => {
-            nsb = ppc(nsb, sp)
+            ppc(sp)
             if(c < sz - 1)
-              nsb.append(", ")
+              sb.append(", ")
             c = c + 1
           })
-          nsb.append(")")
+          sb.append(")")
         }
         case WildcardPattern(None)     => sb.append("_")
-        case WildcardPattern(Some(id)) => sb.append(id)
+        case WildcardPattern(Some(id)) => sb.append(idToString(id))
         case TuplePattern(bndr, subPatterns) => {
           bndr.foreach(b => sb.append(b + " @ "))
           sb.append("(")
           subPatterns.init.foreach(p => {
-            ppc(sb, p)
+            ppc(p)
             sb.append(", ")
           })
-          ppc(sb, subPatterns.last)
+          ppc(subPatterns.last)
           sb.append(")")
         }
         case _ => sb.append("Pattern?")
       }
 
-      var nsb = sb
-      nsb == pp(s, nsb, lvl)
+      pp(s, lvl)
       // if(mex.posInfo != "") {
-      //   nsb.append(" match@(" + mex.posInfo + ") {\n")
+      //   sb.append(" match@(" + mex.posInfo + ") {\n")
       // } else {
-        nsb.append(" match {\n")
+        sb.append(" match {\n")
       // }
 
       csc.foreach(cs => {
-        ind(nsb, lvl+1)
-        nsb.append("case ")
-        nsb = ppc(nsb, cs.pattern)
+        ind(lvl+1)
+        sb.append("case ")
+        ppc(cs.pattern)
         cs.theGuard.foreach(g => {
-          nsb.append(" if ")
-          nsb = pp(g, nsb, lvl+1)
+          sb.append(" if ")
+          pp(g, lvl+1)
         })
-        nsb.append(" => ")
-        nsb = pp(cs.rhs, nsb, lvl+1)
-        nsb.append("\n")
+        sb.append(" => ")
+        pp(cs.rhs, lvl+1)
+        sb.append("\n")
       })
-      ind(nsb, lvl).append("}")
-      nsb
+      ind(lvl)
+      sb.append("}")
     }
 
     case ResultVariable() => sb.append("#res")
-    case Not(expr) => ppUnary(sb, expr, "\u00AC(", ")", lvl)               // \neg
+    case Not(expr) => ppUnary(expr, "\u00AC(", ")", lvl)               // \neg
 
     case e @ Error(desc) => {
-      var nsb = sb
-      nsb.append("error(\"" + desc + "\")[")
-      nsb = pp(e.getType, nsb, lvl)
-      nsb.append("]")
-      nsb
+      sb.append("error(\"" + desc + "\")[")
+      pp(e.getType, lvl)
+      sb.append("]")
     }
 
-    case (expr: PrettyPrintable) => expr.pp(sb, lvl, pp, pp, pp)
+    case (expr: PrettyPrintable) => expr.printWith(lvl, this)
 
     case _ => sb.append("Expr?")
   }
 
-  trait PrettyPrintable {
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer
-  }
-
   // TYPE TREES
   // all type trees are printed in-line
-  private def ppNaryType(sb: StringBuffer, tpes: Seq[TypeTree], pre: String, op: String, post: String, lvl: Int): StringBuffer = {
-    var nsb = sb
-    nsb.append(pre)
+  def ppNaryType(tpes: Seq[TypeTree], pre: String, op: String, post: String, lvl: Int): StringBuffer = {
+    sb.append(pre)
     val sz = tpes.size
     var c = 0
 
     tpes.foreach(t => {
-      nsb = pp(t, nsb, lvl) ; c += 1 ; if(c < sz) nsb.append(op)
+      pp(t, lvl) ; c += 1 ; if(c < sz) sb.append(op)
     })
 
-    nsb.append(post)
-    nsb
+    sb.append(post)
+    sb
   }
 
-  private def pp(tpe: TypeTree, sb: StringBuffer, lvl: Int): StringBuffer = tpe match {
+  def pp(tpe: TypeTree, lvl: Int): StringBuffer = tpe match {
     case Untyped => sb.append("???")
     case UnitType => sb.append("Unit")
     case Int32Type => sb.append("Int")
     case BooleanType => sb.append("Boolean")
-    case ArrayType(bt) => pp(bt, sb.append("Array["), lvl).append("]")
-    case SetType(bt) => pp(bt, sb.append("Set["), lvl).append("]")
-    case MapType(ft,tt) => pp(tt, pp(ft, sb.append("Map["), lvl).append(","), lvl).append("]")
-    case MultisetType(bt) => pp(bt, sb.append("Multiset["), lvl).append("]")
-    case TupleType(tpes) => ppNaryType(sb, tpes, "(", ", ", ")", lvl)
+    case ArrayType(bt) => sb.append("Array["); pp(bt, lvl); sb.append("]")
+    case SetType(bt) => sb.append("Set["); pp(bt, lvl); sb.append("]")
+    case MapType(ft,tt) =>  sb.append("Map["); pp(ft, lvl); sb.append(","); pp(tt, lvl); sb.append("]")
+    case MultisetType(bt) => sb.append("Multiset["); pp(bt, lvl); sb.append("]")
+    case TupleType(tpes) => ppNaryType(tpes, "(", ", ", ")", lvl)
     case c: ClassType => sb.append(c.classDef.id)
     case _ => sb.append("Type?")
   }
 
   // DEFINITIONS
   // all definitions are printed with an end-of-line
-  private def pp(defn: Definition, sb: StringBuffer, lvl: Int): StringBuffer = {
-
+  def pp(defn: Definition, lvl: Int) {
     defn match {
       case Program(id, mainObj) => {
         assert(lvl == 0)
         sb.append("package ")
-        sb.append(id)
+        sb.append(idToString(id))
         sb.append(" {\n")
-        pp(mainObj, sb, lvl+1).append("}\n")
+        pp(mainObj, lvl+1)
+        sb.append("}\n")
       }
 
       case ObjectDef(id, defs, invs) => {
-        var nsb = sb
-        ind(nsb, lvl)
-        nsb.append("object ")
-        nsb.append(id)
-        nsb.append(" {\n")
+        ind(lvl)
+        sb.append("object ")
+        sb.append(idToString(id))
+        sb.append(" {\n")
 
         var c = 0
         val sz = defs.size
 
         defs.foreach(df => {
-          nsb = pp(df, nsb, lvl+1)
+          pp(df, lvl+1)
           if(c < sz - 1) {
-            nsb.append("\n\n")
+            sb.append("\n\n")
           }
           c = c + 1
         })
 
-        nsb.append("\n")
-        ind(nsb, lvl).append("}\n")
+        sb.append("\n")
+        ind(lvl)
+        sb.append("}\n")
       }
 
       case AbstractClassDef(id, parent) => {
-        var nsb = sb
-        ind(nsb, lvl)
-        nsb.append("sealed abstract class ")
-        nsb.append(id)
-        parent.foreach(p => nsb.append(" extends " + p.id))
-        nsb
+        ind(lvl)
+        sb.append("sealed abstract class ")
+        sb.append(idToString(id))
+        parent.foreach(p => sb.append(" extends " + idToString(p.id)))
       }
 
       case CaseClassDef(id, parent, varDecls) => {
-        var nsb = sb
-        ind(nsb, lvl)
-        nsb.append("case class ")
-        nsb.append(id)
-        nsb.append("(")
+        ind(lvl)
+        sb.append("case class ")
+        sb.append(idToString(id))
+        sb.append("(")
         var c = 0
         val sz = varDecls.size
 
         varDecls.foreach(vd => {
-          nsb.append(vd.id)
-          nsb.append(": ")
-          nsb = pp(vd.tpe, nsb, lvl)
+          sb.append(idToString(vd.id))
+          sb.append(": ")
+          pp(vd.tpe, lvl)
           if(c < sz - 1) {
-            nsb.append(", ")
+            sb.append(", ")
           }
           c = c + 1
         })
-        nsb.append(")")
-        parent.foreach(p => nsb.append(" extends " + p.id))
-        nsb
+        sb.append(")")
+        parent.foreach(p => sb.append(" extends " + idToString(p.id)))
       }
 
       case fd @ FunDef(id, rt, args, body, pre, post) => {
-        var nsb = sb
-
         for(a <- fd.annotations) {
-          ind(nsb, lvl)
-          nsb.append("@" + a + "\n")
+          ind(lvl)
+          sb.append("@" + a + "\n")
         }
 
         pre.foreach(prec => {
-          ind(nsb, lvl)
-          nsb.append("@pre : ")
-          nsb = pp(prec, nsb, lvl)
-          nsb.append("\n")
+          ind(lvl)
+          sb.append("@pre : ")
+          pp(prec, lvl)
+          sb.append("\n")
         })
 
         post.foreach(postc => {
-          ind(nsb, lvl)
-          nsb.append("@post: ")
-          nsb = pp(postc, nsb, lvl)
-          nsb.append("\n")
+          ind(lvl)
+          sb.append("@post: ")
+          pp(postc, lvl)
+          sb.append("\n")
         })
 
-        ind(nsb, lvl)
-        nsb.append("def ")
-        nsb.append(id)
-        nsb.append("(")
+        ind(lvl)
+        sb.append("def ")
+        sb.append(idToString(id))
+        sb.append("(")
 
         val sz = args.size
         var c = 0
         
         args.foreach(arg => {
-          nsb.append(arg.id)
-          nsb.append(" : ")
-          nsb = pp(arg.tpe, nsb, lvl)
+          sb.append(arg.id)
+          sb.append(" : ")
+          pp(arg.tpe, lvl)
 
           if(c < sz - 1) {
-            nsb.append(", ")
+            sb.append(", ")
           }
           c = c + 1
         })
 
-        nsb.append(") : ")
-        nsb = pp(rt, nsb, lvl)
-        nsb.append(" = ")
+        sb.append(") : ")
+        pp(rt, lvl)
+        sb.append(" = ")
         if(body.isDefined)
-          pp(body.get, nsb, lvl)
+          pp(body.get, lvl)
         else
-          nsb.append("[unknown function implementation]")
+          sb.append("[unknown function implementation]")
       }
 
       case _ => sb.append("Defn?")
     }
   }
 }
+
+class EquivalencePrettyPrinter() extends PrettyPrinter() {
+  override def idToString(id: Identifier) = id.name
+}
+
+object PrettyPrinter {
+  def apply(tree: Expr): String = {
+    val printer = new PrettyPrinter()
+    printer.pp(tree, 0)
+    printer.toString
+  }
+
+  def apply(tpe: TypeTree): String = {
+    val printer = new PrettyPrinter()
+    printer.pp(tpe, 0)
+    printer.toString
+  }
+
+  def apply(defn: Definition): String = {
+    val printer = new PrettyPrinter()
+    printer.pp(defn, 0)
+    printer.toString
+  }
+}
+
+trait PrettyPrintable {
+  def printWith(lvl: Int, printer: PrettyPrinter): Unit
+}
+
diff --git a/src/main/scala/leon/verification/AnalysisPhase.scala b/src/main/scala/leon/verification/AnalysisPhase.scala
index 3809069403d45e2921a98969604f0d5ff31ef0c0..fe953a272b53085bc0c15935b32026b6854aa4f3 100644
--- a/src/main/scala/leon/verification/AnalysisPhase.scala
+++ b/src/main/scala/leon/verification/AnalysisPhase.scala
@@ -88,9 +88,11 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] {
           solverResult match {
             case _ if vctx.shouldStop.get() =>
               reporter.info("=== CANCELLED ===")
+              vcInfo.time = Some(dt)
               false
 
             case None =>
+              vcInfo.time = Some(dt)
               false
 
             case Some(true) =>
diff --git a/src/main/scala/leon/xlang/Trees.scala b/src/main/scala/leon/xlang/Trees.scala
index 0517d7ee3ae04aab8c889f4877410f87ae365b92..2773078e7faa0dadc8dd31f6579b4cf57aae5805 100644
--- a/src/main/scala/leon/xlang/Trees.scala
+++ b/src/main/scala/leon/xlang/Trees.scala
@@ -6,7 +6,7 @@ import leon.purescala.TypeTrees._
 import leon.purescala.Trees._
 import leon.purescala.Definitions._
 import leon.purescala.Extractors._
-import leon.purescala.PrettyPrinter._
+import leon.purescala.{PrettyPrinter, PrettyPrintable}
 import leon.purescala.ScalaPrinter._
 
 object Trees {
@@ -26,20 +26,15 @@ object Trees {
       Some((args :+ rest, exprs => Block(exprs.init, exprs.last)))
     }
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
-      sb.append("{\n")
+    def printWith(lvl: Int, printer: PrettyPrinter) {
+      printer.append("{\n")
       (exprs :+ last).foreach(e => {
-        ind(sb, lvl+1)
-        ep(e, sb, lvl+1)
-        sb.append("\n")
+        printer.ind(lvl+1)
+        printer.pp(e, lvl+1)
+        printer.append("\n")
       })
-      ind(sb, lvl)
-      sb.append("}\n")
-      sb
+      printer.ind(lvl)
+      printer.append("}\n")
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -65,18 +60,13 @@ object Trees {
     def extract: Option[(Expr, (Expr)=>Expr)] = {
       Some((expr, Assignment(varId, _)))
     }
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
-      var nsb: StringBuffer = sb
-      nsb.append("(")
-      nsb.append(varId.name)
-      nsb.append(" = ")
-      nsb = ep(expr, nsb, lvl)
-      nsb.append(")")
-      nsb
+
+    def printWith(lvl: Int, printer: PrettyPrinter) {
+      printer.append("(")
+      printer.append(varId.name)
+      printer.append(" = ")
+      printer.pp(expr,lvl)
+      printer.append(")")
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -105,28 +95,24 @@ object Trees {
       Some((cond, body, (t1, t2) => While(t1, t2).setInvariant(this.invariant).setPosInfo(this)))
     }
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
+    def printWith(lvl: Int, printer: PrettyPrinter) {
       invariant match {
         case Some(inv) => {
-          sb.append("\n")
-          ind(sb, lvl)
-          sb.append("@invariant: ")
-          ep(inv, sb, lvl)
-          sb.append("\n")
-          ind(sb, lvl)
+          printer.append("\n")
+          printer.ind(lvl)
+          printer.append("@invariant: ")
+          printer.pp(inv, lvl)
+          printer.append("\n")
+          printer.ind(lvl)
         }
         case None =>
       }
-      sb.append("while(")
-      ep(cond, sb, lvl)
-      sb.append(")\n")
-      ind(sb, lvl+1)
-      ep(body, sb, lvl+1)
-      sb.append("\n")
+      printer.append("while(")
+      printer.pp(cond, lvl)
+      printer.append(")\n")
+      printer.ind(lvl+1)
+      printer.pp(body, lvl+1)
+      printer.append("\n")
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -160,16 +146,10 @@ object Trees {
       Some((pred, (expr: Expr) => Epsilon(expr).setType(this.getType).setPosInfo(this)))
     }
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
-      var nsb = sb
-      nsb.append("epsilon(x" + this.posIntInfo._1 + "_" + this.posIntInfo._2 + ". ")
-      nsb = ep(pred, nsb, lvl)
-      nsb.append(")")
-      nsb
+    def printWith(lvl: Int, printer: PrettyPrinter) {
+      printer.append("epsilon(x" + this.posIntInfo._1 + "_" + this.posIntInfo._2 + ". ")
+      printer.pp(pred, lvl)
+      printer.append(")")
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -183,13 +163,9 @@ object Trees {
   }
   case class EpsilonVariable(pos: (Int, Int)) extends Expr with Terminal with PrettyPrintable with ScalaPrintable {
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
+    def printWith(lvl: Int, printer: PrettyPrinter) {
       val (row, col) = pos
-      sb.append("x" + row + "_" + col)
+      printer.append("x" + row + "_" + col)
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -214,19 +190,14 @@ object Trees {
       Some((expr, body, (e: Expr, b: Expr) => LetVar(binders, e, b)))
     }
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
+    def printWith(lvl: Int, printer: PrettyPrinter) {
       val LetVar(b,d,e) = this
-      sb.append("(letvar (" + b + " := ");
-      ep(d, sb, lvl)
-      sb.append(") in\n")
-      ind(sb, lvl+1)
-      ep(e, sb, lvl+1)
-      sb.append(")")
-      sb
+      printer.append("(letvar (" + b + " := ");
+      printer.pp(d, lvl)
+      printer.append(") in\n")
+      printer.ind(lvl+1)
+      printer.pp(e, lvl+1)
+      printer.append(")")
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -338,18 +309,13 @@ object Trees {
       }
     }
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
-      sb.append("\n")
-      dp(fd, sb, lvl+1)
-      sb.append("\n")
-      sb.append("\n")
-      ind(sb, lvl)
-      ep(body, sb, lvl)
-      sb
+    def printWith(lvl: Int, printer: PrettyPrinter) {
+      printer.append("\n")
+      printer.pp(fd, lvl+1)
+      printer.append("\n")
+      printer.append("\n")
+      printer.ind(lvl)
+      printer.pp(body, lvl)
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -374,14 +340,10 @@ object Trees {
       Some((expr, (e: Expr) => Waypoint(i, e)))
     }
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
-      sb.append("waypoint_" + i + "(")
-      ep(expr, sb, lvl)
-      sb.append(")")
+    def printWith(lvl: Int, printer: PrettyPrinter) {
+      printer.append("waypoint_" + i + "(")
+      printer.pp(expr, lvl)
+      printer.append(")")
     }
 
     def ppScala(sb: StringBuffer, lvl: Int, 
@@ -403,16 +365,12 @@ object Trees {
       Some((Seq(t1,t2,t3), (as: Seq[Expr]) => ArrayUpdate(as(0), as(1), as(2))))
     }
 
-    def pp(sb: StringBuffer, lvl: Int, 
-      ep: (Expr, StringBuffer, Int) => StringBuffer, 
-      tp: (TypeTree, StringBuffer, Int) => StringBuffer,
-      dp: (Definition, StringBuffer, Int) => StringBuffer
-    ): StringBuffer = {
-      ep(array, sb, lvl)
-      sb.append("(")
-      ep(index, sb, lvl)
-      sb.append(") = ")
-      ep(newValue, sb, lvl)
+    def printWith(lvl: Int, printer: PrettyPrinter) {
+      printer.pp(array, lvl)
+      printer.append("(")
+      printer.pp(index, lvl)
+      printer.append(") = ")
+      printer.pp(newValue, lvl)
     }
   }