From b043306917e71eee0be7286b79d610560b65353d Mon Sep 17 00:00:00 2001
From: Manos Koukoutos <emmanouil.koukoutos@epfl.ch>
Date: Thu, 18 Feb 2016 16:45:35 +0100
Subject: [PATCH] Refactor the various contexts

---
 src/main/scala/leon/LeonContext.scala         |  9 +-
 src/main/scala/leon/genc/CConverter.scala     |  2 +-
 .../leon/purescala/SelfPrettyPrinter.scala    | 44 +++++----
 src/main/scala/leon/repair/rules/Focus.scala  |  7 +-
 .../scala/leon/synthesis/ExamplesBank.scala   |  2 +-
 src/main/scala/leon/synthesis/Rules.scala     |  2 -
 src/main/scala/leon/synthesis/Search.scala    |  6 +-
 .../scala/leon/synthesis/SearchContext.scala  | 17 ++--
 .../leon/synthesis/SynthesisContext.scala     | 22 +++--
 .../scala/leon/synthesis/graph/Graph.scala    | 17 ++--
 .../leon/synthesis/rules/ADTInduction.scala   |  2 +-
 .../synthesis/rules/ADTLongInduction.scala    |  2 +-
 .../leon/synthesis/rules/BottomUpTegis.scala  | 12 +--
 .../leon/synthesis/rules/CEGISLike.scala      | 95 +++++++++----------
 .../scala/leon/synthesis/rules/CEGLESS.scala  |  4 +-
 .../synthesis/rules/EquivalentInputs.scala    |  2 +-
 .../scala/leon/synthesis/rules/Ground.scala   |  2 +-
 .../synthesis/rules/IntegerEquation.scala     |  2 +-
 .../synthesis/rules/IntroduceRecCall.scala    | 10 +-
 .../synthesis/rules/OptimisticGround.scala    |  2 +-
 .../leon/synthesis/rules/StringRender.scala   | 78 ++++++++-------
 .../leon/synthesis/rules/SygusCVC4.scala      |  4 +-
 .../leon/synthesis/rules/TEGISLike.scala      | 19 ++--
 .../solvers/StringRenderSuite.scala           | 55 +++--------
 .../leon/regression/repair/RepairSuite.scala  |  2 +-
 .../synthesis/StablePrintingSuite.scala       |  4 +-
 26 files changed, 193 insertions(+), 230 deletions(-)

diff --git a/src/main/scala/leon/LeonContext.scala b/src/main/scala/leon/LeonContext.scala
index ca3fdd676..000e785b0 100644
--- a/src/main/scala/leon/LeonContext.scala
+++ b/src/main/scala/leon/LeonContext.scala
@@ -9,7 +9,7 @@ import java.io.File
 import scala.reflect.ClassTag
 
 /** Everything that is part of a compilation unit, except the actual program tree.
-  * Contexts are immutable, and so should all their fields (with the possible
+  * LeonContexts are immutable, and so should all their fields (with the possible
   * exception of the reporter).
   */
 case class LeonContext(
@@ -36,8 +36,11 @@ object LeonContext {
   }
 
   def printNames = {
-    empty.copy(options =
-      Seq(LeonOption[Set[DebugSection]](SharedOptions.optDebug)(Set(DebugSectionTrees)))
+    val reporter = new DefaultReporter(Set())
+    LeonContext(
+      reporter,
+      new InterruptManager(reporter),
+      options = Seq(LeonOption[Set[DebugSection]](SharedOptions.optDebug)(Set(DebugSectionTrees)))
     )
   }
 }
diff --git a/src/main/scala/leon/genc/CConverter.scala b/src/main/scala/leon/genc/CConverter.scala
index a979fd8dd..e7795adbe 100644
--- a/src/main/scala/leon/genc/CConverter.scala
+++ b/src/main/scala/leon/genc/CConverter.scala
@@ -156,7 +156,7 @@ class CConverter(val ctx: LeonContext, val prog: Program) {
     val extraParams = funCtx.toParams
     val params      = extraParams ++ stdParams
 
-    // Function Context:
+    // Function LeonContext:
     // 1) Save the variables of the current context for later function invocation
     // 2) Lift & augment funCtx with the current function's arguments
     // 3) Propagate it to the current function's body
diff --git a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala
index d1cc5f86c..e34b0a4a3 100644
--- a/src/main/scala/leon/purescala/SelfPrettyPrinter.scala
+++ b/src/main/scala/leon/purescala/SelfPrettyPrinter.scala
@@ -34,8 +34,7 @@ object SelfPrettyPrinter {
 
 /** This pretty-printer uses functions defined in Leon itself.
   * If not pretty printing function is defined, return the default value instead
-  * @param The list of functions which should be excluded from pretty-printing (to avoid rendering counter-examples of toString methods using the method itself)
-  * @return a user defined string for the given typed expression. */
+  */
 class SelfPrettyPrinter {
   implicit val section = leon.utils.DebugSectionEvaluation
   private var allowedFunctions = Set[FunDef]()
@@ -49,16 +48,18 @@ class SelfPrettyPrinter {
   /** Returns a list of possible lambdas that can transform the input type to a String.
     * At this point, it does not consider yet the inputType. Only [[prettyPrinterFromCandidate]] will consider it. */
   def prettyPrintersForType(inputType: TypeTree/*, existingPp: Map[TypeTree, List[Lambda]] = Map()*/)(implicit ctx: LeonContext, program: Program): Stream[Lambda] = {
-    program.definedFunctions.toStream flatMap {
-      fd =>
-        val isCandidate = fd.returnType == StringType &&
-        fd.params.length >= 1 &&
+    program.definedFunctions.toStream flatMap { fd =>
+      val isCandidate =
+        fd.returnType == StringType &&
+        fd.params.nonEmpty &&
         !excluded(fd) &&
-        (allowedFunctions(fd) || (
-        fd.id.name.toLowerCase().endsWith("tostring")))
-        if(isCandidate) {
-          prettyPrinterFromCandidate(fd, inputType)
-        } else Stream.Empty
+        (allowedFunctions(fd) || fd.id.name.toLowerCase().endsWith("tostring"))
+
+      if(isCandidate) {
+        prettyPrinterFromCandidate(fd, inputType)
+      } else {
+        Stream.Empty
+      }
     }
   }
   
@@ -88,19 +89,24 @@ class SelfPrettyPrinter {
       case None => Stream.empty
     }
   }
-  
-  
-  /** Actually prints the expression with as alternative the given orElse */
+
+
+  /** Actually prints the expression with as alternative the given orElse
+    * @param excluded The list of functions which should be excluded from pretty-printing
+    *                 (to avoid rendering counter-examples of toString methods using the method itself)
+    * @return a user defined string for the given typed expression.
+    **/
   def print(v: Expr, orElse: =>String, excluded: Set[FunDef] = Set())(implicit ctx: LeonContext, program: Program): String = {
     this.excluded = excluded
     val s = prettyPrintersForType(v.getType)   // TODO: Included the variable excluded if necessary.
-    s.take(100).find(l => l match { // Limit the number of pretty-printers.
+    s.take(100).find {
+      // Limit the number of pretty-printers.
       case Lambda(_, FunctionInvocation(TypedFunDef(fd, _), _)) =>
-        (program.callGraph.transitiveCallees(fd) + fd).forall { fde => 
-        !ExprOps.exists( _.isInstanceOf[Choose])(fde.fullBody)
-      }
+        (program.callGraph.transitiveCallees(fd) + fd).forall { fde =>
+          !ExprOps.exists(_.isInstanceOf[Choose])(fde.fullBody)
+        }
       case _ => false
-    }) match {
+    } match {
       case None => orElse
       case Some(l) =>
         ctx.reporter.debug("Executing pretty printer for type " + v.getType + " : " + l + " on " + v)
diff --git a/src/main/scala/leon/repair/rules/Focus.scala b/src/main/scala/leon/repair/rules/Focus.scala
index 93520c5ea..b625ab7d3 100644
--- a/src/main/scala/leon/repair/rules/Focus.scala
+++ b/src/main/scala/leon/repair/rules/Focus.scala
@@ -32,10 +32,9 @@ case object Focus extends PreprocessingRule("Focus") {
     }
 
     val fd      = hctx.ci.fd
-    val ctx     = hctx.sctx.context
-    val program = hctx.sctx.program
+    val program = hctx.program
 
-    val evaluator = new DefaultEvaluator(ctx, program)
+    val evaluator = new DefaultEvaluator(hctx, program)
 
     // Check how an expression behaves on tests
     //  - returns Some(true) if for all tests e evaluates to true
@@ -97,7 +96,7 @@ case object Focus extends PreprocessingRule("Focus") {
         case c if c eq cond => Some(not(cond))
         case _ => None
       }(fdSpec)
-      forAllTests(ndSpec, Map(), new AngelicEvaluator(new RepairNDEvaluator(ctx, program, cond)))
+      forAllTests(ndSpec, Map(), new AngelicEvaluator(new RepairNDEvaluator(hctx, program, cond)))
     }
 
     guides.flatMap {
diff --git a/src/main/scala/leon/synthesis/ExamplesBank.scala b/src/main/scala/leon/synthesis/ExamplesBank.scala
index 265b35a5e..89e018b5e 100644
--- a/src/main/scala/leon/synthesis/ExamplesBank.scala
+++ b/src/main/scala/leon/synthesis/ExamplesBank.scala
@@ -177,7 +177,7 @@ case class QualifiedExamplesBank(as: List[Identifier], xs: List[Identifier], eb:
 
   /** Filter inputs throught expr which is an expression evaluating to a boolean */
   def filterIns(expr: Expr): ExamplesBank = {
-    filterIns(m => hctx.sctx.defaultEvaluator.eval(expr, m).result == Some(BooleanLiteral(true)))
+    filterIns(m => hctx.defaultEvaluator.eval(expr, m).result == Some(BooleanLiteral(true)))
   }
 
   /** Filters inputs through the predicate pred, with an assignment of input variables to expressions. */
diff --git a/src/main/scala/leon/synthesis/Rules.scala b/src/main/scala/leon/synthesis/Rules.scala
index 1e3cb8717..abbe45fb2 100644
--- a/src/main/scala/leon/synthesis/Rules.scala
+++ b/src/main/scala/leon/synthesis/Rules.scala
@@ -20,8 +20,6 @@ abstract class Rule(val name: String) extends RuleDSL {
 
   implicit val thisRule = this
 
-  implicit def hctxToCtx(implicit hctx: SearchContext): LeonContext = hctx.sctx.context
-
   def asString(implicit ctx: LeonContext) = name
 }
 
diff --git a/src/main/scala/leon/synthesis/Search.scala b/src/main/scala/leon/synthesis/Search.scala
index 4700d7e33..cb7ecf048 100644
--- a/src/main/scala/leon/synthesis/Search.scala
+++ b/src/main/scala/leon/synthesis/Search.scala
@@ -23,12 +23,12 @@ class Search(val ctx: LeonContext, ci: SourceInfo, p: Problem, val strat: Strate
     ctx.timers.synthesis.step.timed {
       n match {
         case an: AndNode =>
-          ctx.timers.synthesis.applications.get(an.ri.asString(sctx.context)).timed {
-            an.expand(SearchContext(sctx, ci, an, this))
+          ctx.timers.synthesis.applications.get(an.ri.asString(sctx)).timed {
+            an.expand(new SearchContext(sctx, ci, an, this))
           }
 
         case on: OrNode =>
-          on.expand(SearchContext(sctx, ci, on, this))
+          on.expand(new SearchContext(sctx, ci, on, this))
       }
     }
   }
diff --git a/src/main/scala/leon/synthesis/SearchContext.scala b/src/main/scala/leon/synthesis/SearchContext.scala
index 1ee7361d5..c782f35ed 100644
--- a/src/main/scala/leon/synthesis/SearchContext.scala
+++ b/src/main/scala/leon/synthesis/SearchContext.scala
@@ -9,15 +9,17 @@ import graph._
  * This is context passed down rules, and include search-wise context, as well
  * as current search location information
  */
-case class SearchContext (
+class SearchContext (
   sctx: SynthesisContext,
-  ci: SourceInfo,
-  currentNode: Node,
-  search: Search
+  val ci: SourceInfo,
+  val currentNode: Node,
+  val search: Search
+) extends SynthesisContext(
+  sctx,
+  sctx.settings,
+  sctx.functionContext,
+  sctx.program
 ) {
-  val context  = sctx.context
-  val reporter = sctx.reporter
-  val program  = sctx.program
 
   def searchDepth = {
     def depthOf(n: Node): Int = n.parent match {
@@ -29,4 +31,5 @@ case class SearchContext (
   }
 
   def parentNode: Option[Node] = currentNode.parent
+
 }
diff --git a/src/main/scala/leon/synthesis/SynthesisContext.scala b/src/main/scala/leon/synthesis/SynthesisContext.scala
index 5b2a9a9f1..1d0a6b5e9 100644
--- a/src/main/scala/leon/synthesis/SynthesisContext.scala
+++ b/src/main/scala/leon/synthesis/SynthesisContext.scala
@@ -10,28 +10,32 @@ import evaluators._
 /**
  * This is global information per entire search, contains necessary information
  */
-case class SynthesisContext(
+class SynthesisContext(
   context: LeonContext,
-  settings: SynthesisSettings,
-  functionContext: FunDef,
-  program: Program
+  val settings: SynthesisSettings,
+  val functionContext: FunDef,
+  val program: Program
+) extends LeonContext(
+    context.reporter,
+    context.interruptManager,
+    context.options,
+    context.files,
+    context.classDir,
+    context.timers
 ) {
 
-  val reporter = context.reporter
-
-  val rules = settings.rules
-
   val solverFactory = SolverFactory.getFromSettings(context, program)
 
   lazy val defaultEvaluator = {
     new DefaultEvaluator(context, program)
   }
+
 }
 
 object SynthesisContext {
 
   def fromSynthesizer(synth: Synthesizer) = {
-    SynthesisContext(
+    new SynthesisContext(
       synth.context,
       synth.settings,
       synth.ci.fd,
diff --git a/src/main/scala/leon/synthesis/graph/Graph.scala b/src/main/scala/leon/synthesis/graph/Graph.scala
index 295f7b4c9..3bb9fbff9 100644
--- a/src/main/scala/leon/synthesis/graph/Graph.scala
+++ b/src/main/scala/leon/synthesis/graph/Graph.scala
@@ -35,7 +35,7 @@ sealed abstract class Node(val parent: Option[Node]) {
   // indicates whether this particular node has already been expanded
   var isExpanded: Boolean = false
 
-  def expand(hctx: SearchContext)
+  def expand(implicit hctx: SearchContext)
 
   val p: Problem
 
@@ -64,7 +64,6 @@ sealed abstract class Node(val parent: Option[Node]) {
 }
 
 /** Represents the conjunction of search nodes.
-  * @param cm The cost model used when prioritizing, evaluating and expanding
   * @param parent Some node. None if it is the root node.
   * @param ri The rule instantiation that created this AndNode.
   **/
@@ -73,7 +72,7 @@ class AndNode(parent: Option[Node], val ri: RuleInstantiation) extends Node(pare
 
   override def asString(implicit ctx: LeonContext) = "\u2227 "+ri.asString
 
-  def expand(hctx: SearchContext): Unit = {
+  def expand(implicit hctx: SearchContext): Unit = {
     require(!isExpanded)
     isExpanded = true
 
@@ -83,9 +82,7 @@ class AndNode(parent: Option[Node], val ri: RuleInstantiation) extends Node(pare
       prefix + lines.head + "\n" + lines.tail.map(padding + _).mkString("\n")
     }
 
-    implicit val ctx = hctx.sctx.context
-
-    import hctx.sctx.reporter.info
+    import hctx.reporter.info
 
     val prefix = f"[${Option(ri.rule).getOrElse("?")}%-20s] "
 
@@ -158,15 +155,15 @@ class OrNode(parent: Option[Node], val p: Problem) extends Node(parent) {
   implicit val debugSection = DebugSectionSynthesis
   
   def getInstantiations(hctx: SearchContext): List[RuleInstantiation] = {
-    val rules = hctx.sctx.rules
+    val rules = hctx.settings.rules
 
     val rulesPrio = rules.groupBy(_.priority).toSeq.sortBy(_._1)
 
     for ((prio, rs) <- rulesPrio) {
       
       val results = rs.flatMap{ r =>
-        hctx.context.reporter.ifDebug(printer => printer("Testing rule: " + r))
-        hctx.context.timers.synthesis.instantiations.get(r.asString(hctx.sctx.context)).timed {
+        hctx.reporter.ifDebug(printer => printer("Testing rule: " + r))
+        hctx.timers.synthesis.instantiations.get(r.asString(hctx)).timed {
           r.instantiateOn(hctx, p)
         }
       }.toList
@@ -181,7 +178,7 @@ class OrNode(parent: Option[Node], val p: Problem) extends Node(parent) {
     Nil
   }
 
-  def expand(hctx: SearchContext): Unit = {
+  def expand(implicit hctx: SearchContext): Unit = {
     require(!isExpanded)
 
     val ris = getInstantiations(hctx)
diff --git a/src/main/scala/leon/synthesis/rules/ADTInduction.scala b/src/main/scala/leon/synthesis/rules/ADTInduction.scala
index 9787811b7..2c0bd4439 100644
--- a/src/main/scala/leon/synthesis/rules/ADTInduction.scala
+++ b/src/main/scala/leon/synthesis/rules/ADTInduction.scala
@@ -17,7 +17,7 @@ case object ADTInduction extends Rule("ADT Induction") {
   def instantiateOn(implicit hctx: SearchContext, p: Problem): Traversable[RuleInstantiation] = {
     /* All input variables which are inductive in the post condition, along with their abstract data type. */
     val candidates = p.as.collect {
-        case IsTyped(origId, act: AbstractClassType) if isInductiveOn(hctx.sctx.solverFactory)(p.pc, origId) => (origId, act)
+        case IsTyped(origId, act: AbstractClassType) if isInductiveOn(hctx.solverFactory)(p.pc, origId) => (origId, act)
     }
 
     val instances = for (candidate <- candidates) yield {
diff --git a/src/main/scala/leon/synthesis/rules/ADTLongInduction.scala b/src/main/scala/leon/synthesis/rules/ADTLongInduction.scala
index 66b27a66a..3d031db44 100644
--- a/src/main/scala/leon/synthesis/rules/ADTLongInduction.scala
+++ b/src/main/scala/leon/synthesis/rules/ADTLongInduction.scala
@@ -15,7 +15,7 @@ import purescala.Definitions._
 case object ADTLongInduction extends Rule("ADT Long Induction") {
   def instantiateOn(implicit hctx: SearchContext, p: Problem): Traversable[RuleInstantiation] = {
     val candidates = p.as.collect {
-        case IsTyped(origId, act @ AbstractClassType(cd, tpe)) if isInductiveOn(hctx.sctx.solverFactory)(p.pc, origId) => (origId, act)
+        case IsTyped(origId, act @ AbstractClassType(cd, tpe)) if isInductiveOn(hctx.solverFactory)(p.pc, origId) => (origId, act)
     }
 
     val instances = for (candidate <- candidates) yield {
diff --git a/src/main/scala/leon/synthesis/rules/BottomUpTegis.scala b/src/main/scala/leon/synthesis/rules/BottomUpTegis.scala
index 4c12f5822..2f4eee0d6 100644
--- a/src/main/scala/leon/synthesis/rules/BottomUpTegis.scala
+++ b/src/main/scala/leon/synthesis/rules/BottomUpTegis.scala
@@ -37,16 +37,13 @@ abstract class BottomUpTEGISLike[T <: Typed](name: String) extends Rule(name) {
     if (tests.nonEmpty) {
       List(new RuleInstantiation(this.name) {
         def apply(hctx: SearchContext): RuleApplication = {
-          val sctx = hctx.sctx
-
-          implicit val ctx = sctx.context
 
           val evalParams            = CodeGenParams.default.copy(maxFunctionInvocations = 2000)
           //val evaluator             = new CodeGenEvaluator(sctx.context, sctx.program, evalParams)
           //val evaluator             = new DefaultEvaluator(sctx.context, sctx.program)
-          val evaluator             = new DualEvaluator(sctx.context, sctx.program, evalParams)
+          val evaluator             = new DualEvaluator(hctx, hctx.program, evalParams)
 
-          val grammar               = getGrammar(sctx, p)
+          val grammar               = getGrammar(hctx, p)
 
           val nTests = tests.size
 
@@ -108,7 +105,7 @@ abstract class BottomUpTEGISLike[T <: Typed](name: String) extends Rule(name) {
           val wrappedTests = tests.map { case (is, os) => (is, tupleWrap(os))}
 
           val enum = new BottomUpEnumerator[T, Expr, Expr, ProductionRule[T, Expr]](
-            grammar.getProductions,
+            grammar.getProductions(_)(hctx),
             wrappedTests,
             { (vecs, gen) =>
               compile(gen)(vecs)
@@ -116,9 +113,6 @@ abstract class BottomUpTEGISLike[T <: Typed](name: String) extends Rule(name) {
             3
           )
 
-
-          val timers = sctx.context.timers.synthesis.rules.tegis
-
           val matches = enum.iterator(getRootLabel(targetType))
 
           if (matches.hasNext) {
diff --git a/src/main/scala/leon/synthesis/rules/CEGISLike.scala b/src/main/scala/leon/synthesis/rules/CEGISLike.scala
index e51a3c30e..b0052a372 100644
--- a/src/main/scala/leon/synthesis/rules/CEGISLike.scala
+++ b/src/main/scala/leon/synthesis/rules/CEGISLike.scala
@@ -42,24 +42,21 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
     // Track non-deterministic programs up to 100'000 programs, or give up
     val nProgramsLimit = 100000
 
-    val sctx = hctx.sctx
-    val ctx  = sctx.context
-
-    val timers = ctx.timers.synthesis.cegis
+    val timers = hctx.timers.synthesis.cegis
 
     // CEGIS Flags to activate or deactivate features
-    val useOptTimeout = sctx.settings.cegisUseOptTimeout
-    val useVanuatoo   = sctx.settings.cegisUseVanuatoo
+    val useOptTimeout = hctx.settings.cegisUseOptTimeout
+    val useVanuatoo   = hctx.settings.cegisUseVanuatoo
 
     // The factor by which programs need to be reduced by testing before we validate them individually
     val testReductionRatio = 10
 
-    val interruptManager = sctx.context.interruptManager
+    val interruptManager = hctx.interruptManager
 
-    val params = getParams(sctx, p)
+    val params = getParams(hctx, p)
 
     // If this CEGISLike forces a maxSize, take it, otherwise find it in the settings
-    val maxSize = params.maxSize.getOrElse(sctx.settings.cegisMaxSize)
+    val maxSize = params.maxSize.getOrElse(hctx.settings.cegisMaxSize)
 
     if (maxSize == 0) {
       return Nil
@@ -141,7 +138,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
       // Update the c-tree after an increase in termsize
       def updateCTree(): Unit = {
-        ctx.timers.synthesis.cegis.updateCTree.start()
+        hctx.timers.synthesis.cegis.updateCTree.start()
         def freshB() = {
           val id = FreshIdentifier("B", BooleanType, true)
           bs += id
@@ -183,7 +180,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
           c
         }
 
-        sctx.reporter.ifDebug { printer =>
+        hctx.reporter.ifDebug { printer =>
           printer("Grammar so far:")
           grammar.printProductions(printer)
           printer("")
@@ -195,7 +192,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
         excludedPrograms = Set()
         prunedPrograms = allPrograms().toSet
 
-        ctx.timers.synthesis.cegis.updateCTree.stop()
+        hctx.timers.synthesis.cegis.updateCTree.stop()
       }
 
       // Returns a count of all possible programs
@@ -223,7 +220,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
         val allCount = allProgramsCount()
         if (allCount > nProgramsLimit) {
-          ctx.reporter.debug(s"Exceeded program limit: $allCount > $nProgramsLimit")
+          hctx.reporter.debug(s"Exceeded program limit: $allCount > $nProgramsLimit")
           return Seq()
         }
 
@@ -284,10 +281,10 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
         val outerSolution = {
           new PartialSolution(hctx.search.strat, true)
             .solutionAround(hctx.currentNode)(FunctionInvocation(solFd.typed, p.as.map(_.toVariable)))
-            .getOrElse(ctx.reporter.fatalError("Unable to get outer solution"))
+            .getOrElse(hctx.reporter.fatalError("Unable to get outer solution"))
         }
 
-        val program0 = addFunDefs(sctx.program, Seq(cTreeFd, solFd, phiFd) ++ outerSolution.defs, hctx.ci.fd)
+        val program0 = addFunDefs(hctx.program, Seq(cTreeFd, solFd, phiFd) ++ outerSolution.defs, hctx.ci.fd)
 
         cTreeFd.body = None
 
@@ -396,7 +393,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
         cTreeFd.body = Some(cExpr)
         programCTree = addFunDefs(innerProgram, newFds, cTreeFd)
-        evaluator = new DefaultEvaluator(sctx.context, programCTree)
+        evaluator = new DefaultEvaluator(hctx, programCTree)
 
         //println("-- "*30)
         //println(programCTree.asString)
@@ -482,11 +479,11 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
               println(err)
               println()
             }*/
-            sctx.reporter.debug("RE testing CE: "+err)
+            hctx.reporter.debug("RE testing CE: "+err)
             false
 
           case EvaluationResults.EvaluatorError(err) =>
-            sctx.reporter.debug("Error testing CE: "+err)
+            hctx.reporter.debug("Error testing CE: "+err)
             false
         }
 
@@ -525,7 +522,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
           val cnstr = and(innerPc, letTuple(p.xs, innerSol, Not(innerPhi)))
 
-          val eval = new DefaultEvaluator(ctx, innerProgram)
+          val eval = new DefaultEvaluator(hctx, innerProgram)
 
           if (cexs exists (cex => eval.eval(cnstr, p.as.zip(cex).toMap).result == Some(BooleanLiteral(true)))) {
             //println(s"Program $outerSol fails!")
@@ -534,7 +531,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
           } else {
             //println("Solving for: "+cnstr.asString)
 
-            val solverf = SolverFactory.getFromSettings(ctx, innerProgram).withTimeout(cexSolverTo)
+            val solverf = SolverFactory.getFromSettings(hctx, innerProgram).withTimeout(cexSolverTo)
             val solver = solverf.getNewSolver()
             try {
               solver.assertCnstr(cnstr)
@@ -559,7 +556,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
                 case None =>
                   if (useOptTimeout) {
                     // Interpret timeout in CE search as "the candidate is valid"
-                    sctx.reporter.info("CEGIS could not prove the validity of the resulting expression")
+                    hctx.reporter.info("CEGIS could not prove the validity of the resulting expression")
                     // Optimistic valid solution
                     return Right(Solution(BooleanLiteral(true), Set(), outerSol, false))
                   }
@@ -608,7 +605,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
        * First phase of CEGIS: discover potential programs (that work on at least one input)
        */
       def solveForTentativeProgram(): Option[Option[Set[Identifier]]] = {
-        val solverf = SolverFactory.getFromSettings(ctx, programCTree).withTimeout(exSolverTo)
+        val solverf = SolverFactory.getFromSettings(hctx, programCTree).withTimeout(exSolverTo)
         val solver  = solverf.getNewSolver()
         val cnstr = FunctionInvocation(phiFd.typed, phiFd.params.map(_.id.toVariable))
 
@@ -670,7 +667,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
                * might timeout instead of returning Some(false). We might still
                * benefit from unfolding further
                */
-              ctx.reporter.debug("Timeout while getting tentative program!")
+              hctx.reporter.debug("Timeout while getting tentative program!")
               Some(None)
           }
         } finally {
@@ -683,7 +680,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
        * Second phase of CEGIS: verify a given program by looking for CEX inputs
        */
       def solveForCounterExample(bs: Set[Identifier]): Option[Option[Seq[Expr]]] = {
-        val solverf = SolverFactory.getFromSettings(ctx, programCTree).withTimeout(cexSolverTo)
+        val solverf = SolverFactory.getFromSettings(hctx, programCTree).withTimeout(cexSolverTo)
         val solver  = solverf.getNewSolver()
         val cnstr = FunctionInvocation(phiFd.typed, phiFd.params.map(_.id.toVariable))
 
@@ -724,19 +721,19 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
     List(new RuleInstantiation(this.name) {
       def apply(hctx: SearchContext): RuleApplication = {
         var result: Option[RuleApplication] = None
-        val sctx = hctx.sctx
-        implicit val ctx = sctx.context
 
         val ndProgram = NonDeterministicProgram
         ndProgram.init()
 
-        sctx.reporter.debug("Acquiring initial list of examples")
+        implicit val ic = hctx
+
+        hctx.reporter.debug("Acquiring initial list of examples")
 
         // To the list of known examples, we add an additional one produced by the solver
         val solverExample = if (p.pc == BooleanLiteral(true)) {
           InExample(p.as.map(a => simplestValue(a.getType)))
         } else {
-          val solverf = sctx.solverFactory
+          val solverf = hctx.solverFactory
           val solver  = solverf.getNewSolver().setTimeout(exSolverTo)
 
           solver.assertCnstr(p.pc)
@@ -748,12 +745,12 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
                 InExample(p.as.map(a => model.getOrElse(a, simplestValue(a.getType))))
 
               case Some(false) =>
-                sctx.reporter.debug("Path-condition seems UNSAT")
+                hctx.reporter.debug("Path-condition seems UNSAT")
                 return RuleFailed()
 
               case None =>
                 if (!interruptManager.isInterrupted) {
-                  sctx.reporter.warning("Solver could not solve path-condition")
+                  hctx.reporter.warning("Solver could not solve path-condition")
                 }
                 return RuleFailed() // This is not necessary though, but probably wanted
             }
@@ -764,7 +761,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
         val baseExampleInputs = p.eb.examples :+ solverExample
 
-        sctx.reporter.ifDebug { debug =>
+        hctx.reporter.ifDebug { debug =>
           baseExampleInputs.foreach { in =>
             debug("  - "+in.asString)
           }
@@ -776,9 +773,9 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
         val nTests = if (p.pc == BooleanLiteral(true)) 50 else 20
 
         val inputGenerator: Iterator[Example] = if (useVanuatoo) {
-          new VanuatooDataGen(sctx.context, sctx.program).generateFor(p.as, p.pc, nTests, 3000).map(InExample)
+          new VanuatooDataGen(hctx, hctx.program).generateFor(p.as, p.pc, nTests, 3000).map(InExample)
         } else {
-          val evaluator = new DualEvaluator(sctx.context, sctx.program, CodeGenParams.default)
+          val evaluator = new DualEvaluator(hctx, hctx.program, CodeGenParams.default)
           new GrammarDataGen(evaluator, ValueGrammar).generateFor(p.as, p.pc, nTests, 1000).map(InExample)
         }
 
@@ -807,7 +804,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
             ndProgram.unfold()
 
             val nInitial = ndProgram.prunedPrograms.size
-            sctx.reporter.debug("#Programs: "+nInitial)
+            hctx.reporter.debug("#Programs: "+nInitial)
 
             //sctx.reporter.ifDebug{ printer =>
             //  val limit = 100
@@ -828,12 +825,12 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
                 val examples = allInputExamples()
                 examples.find(e => !ndProgram.testForProgram(bs)(e)).foreach { e =>
                   failedTestsStats(e) += 1
-                  sctx.reporter.debug(f" Program: ${ndProgram.getExpr(bs).asString}%-80s failed on: ${e.asString}")
+                  hctx.reporter.debug(f" Program: ${ndProgram.getExpr(bs).asString}%-80s failed on: ${e.asString}")
                   ndProgram.excludeProgram(bs, true)
                 }
 
                 if (ndProgram.excludedPrograms.size+1 % 1000 == 0) {
-                  sctx.reporter.debug("..."+ndProgram.excludedPrograms.size)
+                  hctx.reporter.debug("..."+ndProgram.excludedPrograms.size)
                 }
               }
               timers.filter.stop()
@@ -841,8 +838,8 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
             def nPassing = ndProgram.prunedPrograms.size
 
-            sctx.reporter.debug(s"#Programs passing tests: $nPassing out of $nInitial")
-            sctx.reporter.ifDebug{ printer =>
+            hctx.reporter.debug(s"#Programs passing tests: $nPassing out of $nInitial")
+            hctx.reporter.ifDebug{ printer =>
               for (p <- ndProgram.prunedPrograms.take(100)) {
                 printer(" - "+ndProgram.getExpr(p).asString)
               }
@@ -850,8 +847,8 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
                 printer(" - ...")
               }
             }
-            sctx.reporter.debug("#Tests: "+baseExampleInputs.size)
-            sctx.reporter.ifDebug{ printer =>
+            hctx.reporter.debug("#Tests: "+baseExampleInputs.size)
+            hctx.reporter.ifDebug{ printer =>
               for (e <- baseExampleInputs.take(10)) {
                 printer(" - "+e.asString)
               }
@@ -863,12 +860,12 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
             // CEGIS Loop at a given unfolding level
             while (result.isEmpty && !interruptManager.isInterrupted && !ndProgram.allProgramsClosed) {
               timers.loop.start()
-              ctx.reporter.debug("Programs left: " + ndProgram.prunedPrograms.size)
+              hctx.reporter.debug("Programs left: " + ndProgram.prunedPrograms.size)
 
               // Phase 0: If the number of remaining programs is small, validate them individually
               if (nInitial / nPassing > testReductionRatio || nPassing <= 10) {
                 val programsToValidate = ndProgram.prunedPrograms
-                sctx.reporter.debug(s"Will send ${programsToValidate.size} program(s) to validate individually")
+                hctx.reporter.debug(s"Will send ${programsToValidate.size} program(s) to validate individually")
                 ndProgram.validatePrograms(programsToValidate) match {
                   case Right(sol) =>
                     // Found solution! Exit CEGIS
@@ -879,18 +876,18 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
                     val newCexs = cexs.map(InExample)
                     gi ++= newCexs
                 }
-                ctx.reporter.debug(s"#Programs after validating individually: ${ndProgram.prunedPrograms.size}")
+                hctx.reporter.debug(s"#Programs after validating individually: ${ndProgram.prunedPrograms.size}")
               }
 
               if (result.isEmpty && !ndProgram.allProgramsClosed) {
                 // Phase 1: Find a candidate program that works for at least 1 input
                 ndProgram.solveForTentativeProgram() match {
                   case Some(Some(bs)) =>
-                    sctx.reporter.debug(s"Found tentative model ${ndProgram.getExpr(bs)}, need to validate!")
+                    hctx.reporter.debug(s"Found tentative model ${ndProgram.getExpr(bs)}, need to validate!")
                     // Phase 2: Validate candidate model
                     ndProgram.solveForCounterExample(bs) match {
                       case Some(Some(inputsCE)) =>
-                        sctx.reporter.debug("Found counter-example:" + inputsCE)
+                        hctx.reporter.debug("Found counter-example:" + inputsCE)
                         val ce = InExample(inputsCE)
                         // Found counterexample! Exclude this program
                         gi += ce
@@ -912,10 +909,10 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
                       case None =>
                         // We are not sure
-                        sctx.reporter.debug("Unknown")
+                        hctx.reporter.debug("Unknown")
                         if (useOptTimeout) {
                           // Interpret timeout in CE search as "the candidate is valid"
-                          sctx.reporter.info("CEGIS could not prove the validity of the resulting expression")
+                          hctx.reporter.info("CEGIS could not prove the validity of the resulting expression")
                           val expr = ndProgram.getExpr(bs)
                           result = Some(RuleClosed(Solution(BooleanLiteral(true), Set(), expr, isTrusted = false)))
                         } else {
@@ -927,7 +924,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
                     }
 
                   case Some(None) =>
-                    sctx.reporter.debug("There exists no candidate program!")
+                    hctx.reporter.debug("There exists no candidate program!")
                     ndProgram.prunedPrograms foreach (ndProgram.excludeProgram(_, true))
 
                   case None =>
@@ -944,7 +941,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
 
         } catch {
           case e: Throwable =>
-            sctx.reporter.warning("CEGIS crashed: "+e.getMessage)
+            hctx.reporter.warning("CEGIS crashed: "+e.getMessage)
             e.printStackTrace()
             RuleFailed()
         }
diff --git a/src/main/scala/leon/synthesis/rules/CEGLESS.scala b/src/main/scala/leon/synthesis/rules/CEGLESS.scala
index 36cc7f9e6..8f30f229f 100644
--- a/src/main/scala/leon/synthesis/rules/CEGLESS.scala
+++ b/src/main/scala/leon/synthesis/rules/CEGLESS.scala
@@ -15,8 +15,6 @@ case object CEGLESS extends CEGISLike[NonTerminal[String]]("CEGLESS") {
   def getParams(sctx: SynthesisContext, p: Problem) = {
     val TopLevelAnds(clauses) = p.ws
 
-    val ctx = sctx.context
-
     val guides = clauses.collect {
       case Guide(e) => e
     }
@@ -26,7 +24,7 @@ case object CEGLESS extends CEGISLike[NonTerminal[String]]("CEGLESS") {
     sctx.reporter.ifDebug { printer =>
       printer("Guides available:")
       for (g <- guides) {
-        printer(" - "+g.asString(ctx))
+        printer(" - "+g.asString(sctx))
       }
     }
 
diff --git a/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala b/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala
index 6b2f9e858..a4e01064c 100644
--- a/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala
+++ b/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala
@@ -73,7 +73,7 @@ case object EquivalentInputs extends NormalizingRule("EquivalentInputs") {
     }
 
     if (substs.nonEmpty) {
-      val simplifier = Simplifiers.bestEffort(hctx.context, hctx.program) _
+      val simplifier = Simplifiers.bestEffort(hctx, hctx.program) _
 
       val sub = p.copy(ws = replaceSeq(substs, p.ws), 
                        pc = simplifier(andJoin(replaceSeq(substs, p.pc) +: postsToInject)),
diff --git a/src/main/scala/leon/synthesis/rules/Ground.scala b/src/main/scala/leon/synthesis/rules/Ground.scala
index a385bb126..e5eb75463 100644
--- a/src/main/scala/leon/synthesis/rules/Ground.scala
+++ b/src/main/scala/leon/synthesis/rules/Ground.scala
@@ -15,7 +15,7 @@ case object Ground extends NormalizingRule("Ground") {
     if (p.as.isEmpty) {
       List(new RuleInstantiation(this.name) {
         def apply(hctx: SearchContext): RuleApplication = {
-          val solver = SimpleSolverAPI(hctx.sctx.solverFactory.withTimeout(10.seconds))
+          val solver = SimpleSolverAPI(hctx.solverFactory.withTimeout(10.seconds))
 
           val result = solver.solveSAT(p.phi) match {
             case (Some(true), model) =>
diff --git a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
index 8aaa89ee1..a668f26fc 100644
--- a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
+++ b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
@@ -23,7 +23,7 @@ case object IntegerEquation extends Rule("Integer Equation") {
     var candidates: Seq[Expr] = eqs
     var allOthers: Seq[Expr] = others
 
-    val evaluator = new DefaultEvaluator(hctx.context, hctx.program)
+    val evaluator = new DefaultEvaluator(hctx, hctx.program)
 
     var vars: Set[Identifier] = Set()
     var eqxs: List[Identifier] = List()
diff --git a/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala b/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala
index c93846666..95892b4a0 100644
--- a/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala
+++ b/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala
@@ -25,7 +25,7 @@ case object IntroduceRecCall extends Rule("Introduce rec. calls") {
   }
 
   def instantiateOn(implicit hctx: SearchContext, p: Problem): Traversable[RuleInstantiation] = {
-    val evaluator = new NoChooseEvaluator(hctxToCtx, hctx.program)
+    val evaluator = new NoChooseEvaluator(hctx, hctx.program)
 
     val calls = terminatingCalls(hctx.program, p.ws, p.pc, None, false)
 
@@ -56,11 +56,11 @@ case object IntroduceRecCall extends Rule("Introduce rec. calls") {
 
           val psol = new PartialSolution(hctx.search.strat, true)
             .solutionAround(hctx.currentNode)(Error(p.outType, "Encountered choose!"))
-            .getOrElse(hctx.sctx.context.reporter.fatalError("Unable to get outer solution"))
+            .getOrElse(hctx.reporter.fatalError("Unable to get outer solution"))
             .term
 
-          val origImpl = hctx.sctx.functionContext.fullBody
-          hctx.sctx.functionContext.fullBody = psol
+          val origImpl = hctx.functionContext.fullBody
+          hctx.functionContext.fullBody = psol
 
           def mapExample(e: Example): List[Example] = {
             val res = evaluator.eval(newCall, p.as.zip(e.ins).toMap)
@@ -79,7 +79,7 @@ case object IntroduceRecCall extends Rule("Introduce rec. calls") {
             eb = p.eb.map(mapExample)
           )
 
-          hctx.sctx.functionContext.fullBody = origImpl
+          hctx.functionContext.fullBody = origImpl
 
           RuleExpanded(List(newProblem))
         }
diff --git a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala
index 679e49e38..f99ae625a 100644
--- a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala
+++ b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala
@@ -18,7 +18,7 @@ case object OptimisticGround extends Rule("Optimistic Ground") {
       val res = new RuleInstantiation(this.name) {
         def apply(hctx: SearchContext) = {
 
-          val solver = SimpleSolverAPI(hctx.sctx.solverFactory.withTimeout(50.millis))
+          val solver = SimpleSolverAPI(hctx.solverFactory.withTimeout(50.millis))
 
           val xss = p.xs.toSet
           val ass = p.as.toSet
diff --git a/src/main/scala/leon/synthesis/rules/StringRender.scala b/src/main/scala/leon/synthesis/rules/StringRender.scala
index 715d54385..1244dc6df 100644
--- a/src/main/scala/leon/synthesis/rules/StringRender.scala
+++ b/src/main/scala/leon/synthesis/rules/StringRender.scala
@@ -6,10 +6,8 @@ package rules
 
 import scala.annotation.tailrec
 import scala.collection.mutable.ListBuffer
-import bonsai.enumerators.MemoizedEnumerator
-import evaluators.DefaultEvaluator
 import evaluators.AbstractEvaluator
-import purescala.Definitions.{FunDef, ValDef, Program, TypedFunDef, CaseClassDef, AbstractClassDef}
+import purescala.Definitions._
 import purescala.Common._
 import purescala.Types._
 import purescala.Constructors._
@@ -19,14 +17,10 @@ import purescala.TypeOps
 import purescala.DefOps
 import purescala.ExprOps
 import purescala.SelfPrettyPrinter
-import solvers.Model
 import solvers.ModelBuilder
 import solvers.string.StringSolver
-import synthesis.programsets.DirectProgramSet
-import synthesis.programsets.JoinProgramSet
-import leon.utils.DebugSectionSynthesis
-
-
+import programsets.DirectProgramSet
+import programsets.JoinProgramSet
 
 
 /** A template generator for a given type tree. 
@@ -148,7 +142,8 @@ case object StringRender extends Rule("StringRender") {
   
   /** Returns a stream of assignments compatible with input/output examples for the given template */
   def findAssignments(p: Program, inputs: Seq[Identifier], examples: ExamplesBank, template: Expr)(implicit hctx: SearchContext): Stream[Map[Identifier, String]] = {
-    val e = new AbstractEvaluator(hctx.context, p)
+
+    val e = new AbstractEvaluator(hctx, p)
     
     @tailrec def gatherEquations(s: List[InOutExample], acc: ListBuffer[Equation] = ListBuffer()): Option[SProblem] = s match {
       case Nil => Some(acc.toList)
@@ -201,7 +196,7 @@ case object StringRender extends Rule("StringRender") {
     
     def computeSolutions(funDefsBodies: Seq[(FunDef, WithIds[Expr])], template: WithIds[Expr]): Stream[Assignment] = {
       val funDefs = for((funDef, body) <- funDefsBodies) yield  { funDef.body = Some(body._1); funDef }
-      val newProgram = DefOps.addFunDefs(hctx.program, funDefs, hctx.sctx.functionContext)
+      val newProgram = DefOps.addFunDefs(hctx.program, funDefs, hctx.functionContext)
       findAssignments(newProgram, p.as.filter{ x => !x.getType.isInstanceOf[FunctionType] }, examples, template._1)
     }
     
@@ -327,9 +322,9 @@ case object StringRender extends Rule("StringRender") {
   /** Creates an empty function definition for the dependent type */
   def createEmptyFunDef(ctx: StringSynthesisContext, tpe: DependentType)(implicit hctx: SearchContext): FunDef = {
     def defaultFunName(t: TypeTree) = t match {
-      case AbstractClassType(c, d) => c.id.asString(hctx.context)
-      case CaseClassType(c, d) => c.id.asString(hctx.context)
-      case t => t.asString(hctx.context)
+      case AbstractClassType(c, d) => c.id.asString(hctx)
+      case CaseClassType(c, d) => c.id.asString(hctx)
+      case t => t.asString(hctx)
     }
     
     val funName2 = tpe.caseClassParent match {
@@ -339,10 +334,9 @@ case object StringRender extends Rule("StringRender") {
     val funName3 = funName2.replaceAll("[^a-zA-Z0-9_]","")
     val funName = funName3(0).toLower + funName3.substring(1) 
     val funId = FreshIdentifier(ctx.freshFunName(funName), alwaysShowUniqueID = true)
-    val argId= FreshIdentifier(tpe.typeToConvert.asString(hctx.context).toLowerCase()(0).toString, tpe.typeToConvert)
-    val tparams = hctx.sctx.functionContext.tparams
-    val fd = new FunDef(funId, tparams, ValDef(argId) :: ctx.provided_functions.map(ValDef(_)).toList, StringType) // Empty function.
-    fd
+    val argId= FreshIdentifier(tpe.typeToConvert.asString(hctx).toLowerCase()(0).toString, tpe.typeToConvert)
+    val tparams = hctx.functionContext.tparams
+    new FunDef(funId, tparams, ValDef(argId) :: ctx.provided_functions.map(ValDef).toList, StringType) // Empty function.
   }
   
   /** Pre-updates of the function definition */
@@ -354,7 +348,7 @@ case object StringRender extends Rule("StringRender") {
   }
 
   /** Assembles multiple MatchCase to a singleMatchExpr using the function definition fd */
-  private val mergeMatchCases = (fd: FunDef) => (cases: Seq[WithIds[MatchCase]]) => (MatchExpr(Variable(fd.params(0).id), cases.map(_._1)), cases.map(_._2).flatten.toList)
+  private val mergeMatchCases = (fd: FunDef) => (cases: Seq[WithIds[MatchCase]]) => (MatchExpr(Variable(fd.params(0).id), cases.map(_._1)), cases.flatMap(_._2).toList)
   
   /** Returns a (possibly recursive) template which can render the inputs in their order.
     * Returns an expression and path-dependent pretty printers which can be used.
@@ -414,23 +408,23 @@ case object StringRender extends Rule("StringRender") {
         result: ListBuffer[Stream[WithIds[Expr]]] = ListBuffer()): (List[Stream[WithIds[Expr]]], StringSynthesisResult) = inputs match {
       case Nil => (result.toList, ctx.result)
       case input::q => 
-        val dependentType = DependentType(ctx.currentCaseClassParent, input.asString(hctx.program)(hctx.context), input.getType)
+        val dependentType = DependentType(ctx.currentCaseClassParent, input.asString(hctx.program)(hctx), input.getType)
         ctx.result.adtToString.get(dependentType) match {
         case Some(fd) =>
           gatherInputs(ctx, q, result += Stream((functionInvocation(fd._1, input::ctx.provided_functions.toList.map(Variable)), Nil)))
         case None => // No function can render the current type.
           // We should not rely on calling the original function on the first line of the body of the function itself.
           val exprs1s = (new SelfPrettyPrinter)
-            .allowFunction(hctx.sctx.functionContext)
-            .excludeFunction(hctx.sctx.functionContext)
-            .prettyPrintersForType(input.getType)(hctx.context, hctx.program)
+            .allowFunction(hctx.functionContext)
+            .excludeFunction(hctx.functionContext)
+            .prettyPrintersForType(input.getType)(hctx, hctx.program)
             .map(l => (application(l, Seq(input)), List[Identifier]())) // Use already pre-defined pretty printers.
-          val exprs1 = exprs1s.toList.sortBy{ case (Lambda(_, FunctionInvocation(fd, _)), _) if fd == hctx.sctx.functionContext => 0 case _ => 1}
+          val exprs1 = exprs1s.toList.sortBy{ case (Lambda(_, FunctionInvocation(fd, _)), _) if fd == hctx.functionContext => 0 case _ => 1}
           val exprs2 = ctx.abstractStringConverters.getOrElse(input.getType, Nil).map(f => (f(input), List[Identifier]()))
           val defaultConverters: Stream[WithIds[Expr]] = exprs1.toStream #::: exprs2.toStream
           val recursiveConverters: Stream[WithIds[Expr]] =
             (new SelfPrettyPrinter)
-            .prettyPrinterFromCandidate(hctx.sctx.functionContext, input.getType)(hctx.context, hctx.program)
+            .prettyPrinterFromCandidate(hctx.functionContext, input.getType)(hctx, hctx.program)
             .map(l => (application(l, Seq(input)), List[Identifier]()))
             
           def mergeResults(templateConverters: =>Stream[WithIds[Expr]]): Stream[WithIds[Expr]] = {
@@ -457,7 +451,7 @@ case object StringRender extends Rule("StringRender") {
             case WithStringconverter(converter) => // Base case
               gatherInputs(ctx, q, result += mergeResults(Stream((converter(input), Nil))))
             case t: ClassType =>
-              if(enforceDefaultStringMethodsIfAvailable && !defaultConverters.isEmpty) {
+              if(enforceDefaultStringMethodsIfAvailable && defaultConverters.nonEmpty) {
                 gatherInputs(ctx, q, result += defaultConverters)
               } else {
                 // Create the empty function body and updates the assignments parts.
@@ -467,10 +461,10 @@ case object StringRender extends Rule("StringRender") {
                   case act@AbstractClassType(acd@AbstractClassDef(id, tparams, parent), tps) =>
                     // Create a complete FunDef body with pattern matching
                     
-                    val allKnownDescendantsAreCCAndHaveZeroArgs = act.knownCCDescendants.forall { x => x match {
+                    val allKnownDescendantsAreCCAndHaveZeroArgs = act.knownCCDescendants.forall {
                       case CaseClassType(ccd@CaseClassDef(id, tparams, parent, isCaseObject), tparams2) => ccd.fields.isEmpty
                       case _ => false
-                    }}
+                    }
                     
                     //TODO: Test other templates not only with Wilcard patterns, but more cases options for non-recursive classes (e.g. Option, Boolean, Finite parameterless case classes.)
                     val (ctx3, cases) = ((ctx2, ListBuffer[Stream[WithIds[MatchCase]]]()) /: act.knownCCDescendants) {
@@ -542,18 +536,19 @@ case object StringRender extends Rule("StringRender") {
     //hctx.reporter.debug("StringRender:Output variables="+p.xs+", their types="+p.xs.map(_.getType))
     p.xs match {
       case List(IsTyped(v, StringType)) =>
-        val description = "Creates a standard string conversion function"
+        val examplesFinder = new ExamplesFinder(hctx, hctx.program)
+          .setKeepAbstractExamples(true)
+          .setEvaluationFailOnChoose(true)
 
-        val examplesFinder = new ExamplesFinder(hctx.context, hctx.program)
-        .setKeepAbstractExamples(true)
-        .setEvaluationFailOnChoose(true)
         val examples = examplesFinder.extractFromProblem(p)
         
-        val abstractStringConverters: StringConverters =
-          (p.as.flatMap { case x => x.getType match {
-            case FunctionType(Seq(aType), StringType) => List((aType, (arg: Expr) => application(Variable(x), Seq(arg))))
+        val abstractStringConverters: StringConverters = p.as.flatMap { case x =>
+          x.getType match {
+            case FunctionType(Seq(aType), StringType) =>
+              List((aType, (arg: Expr) => application(Variable(x), Seq(arg))))
             case _ => Nil
-          }}).groupBy(_._1).mapValues(_.map(_._2))
+          }
+        }.groupBy(_._1).mapValues(_.map(_._2))
        
         val (inputVariables, functionVariables) = p.as.partition ( x => x.getType match {
           case f: FunctionType => false
@@ -564,11 +559,12 @@ case object StringRender extends Rule("StringRender") {
         val originalInputs = inputVariables.map(Variable)
         ruleInstantiations += RuleInstantiation("String conversion") {
           val (expr, synthesisResult) = createFunDefsTemplates(
-              StringSynthesisContext.empty(
-                  abstractStringConverters,
-                  originalInputs.toSet,
-                  functionVariables
-                  ), originalInputs)
+            StringSynthesisContext.empty(
+              abstractStringConverters,
+              originalInputs.toSet,
+              functionVariables
+            ), originalInputs
+          )
           val funDefs = synthesisResult.adtToString
           
           /*val toDebug: String = (("\nInferred functions:" /: funDefs)( (t, s) =>
diff --git a/src/main/scala/leon/synthesis/rules/SygusCVC4.scala b/src/main/scala/leon/synthesis/rules/SygusCVC4.scala
index bf6f57616..72c267b28 100644
--- a/src/main/scala/leon/synthesis/rules/SygusCVC4.scala
+++ b/src/main/scala/leon/synthesis/rules/SygusCVC4.scala
@@ -11,9 +11,7 @@ case object SygusCVC4 extends Rule("SygusCVC4") {
     List(new RuleInstantiation(this.name) {
       def apply(hctx: SearchContext): RuleApplication = {
 
-        val sctx = hctx.sctx
-
-        val s = new CVC4SygusSolver(sctx.context, sctx.program, p)
+        val s = new CVC4SygusSolver(hctx, hctx.program, p)
 
         s.checkSynth() match {
           case Some(expr) =>
diff --git a/src/main/scala/leon/synthesis/rules/TEGISLike.scala b/src/main/scala/leon/synthesis/rules/TEGISLike.scala
index 93e97de6f..62145f7ca 100644
--- a/src/main/scala/leon/synthesis/rules/TEGISLike.scala
+++ b/src/main/scala/leon/synthesis/rules/TEGISLike.scala
@@ -32,21 +32,18 @@ abstract class TEGISLike[T <: Typed](name: String) extends Rule(name) {
 
     List(new RuleInstantiation(this.name) {
       def apply(hctx: SearchContext): RuleApplication = {
-        val sctx = hctx.sctx
-
-        implicit val ctx = sctx.context
-
-        val params = getParams(sctx, p)
+        implicit val ci = hctx
+        val params = getParams(hctx, p)
         val grammar = params.grammar
 
         val nTests = if (p.pc == BooleanLiteral(true)) 50 else 20
 
-        val useVanuatoo = sctx.settings.cegisUseVanuatoo
+        val useVanuatoo = hctx.settings.cegisUseVanuatoo
 
         val inputGenerator: Iterator[Seq[Expr]] = if (useVanuatoo) {
-          new VanuatooDataGen(sctx.context, sctx.program).generateFor(p.as, p.pc, nTests, 3000)
+          new VanuatooDataGen(hctx, hctx.program).generateFor(p.as, p.pc, nTests, 3000)
         } else {
-          val evaluator = new DualEvaluator(sctx.context, sctx.program, CodeGenParams.default)
+          val evaluator = new DualEvaluator(hctx, hctx.program, CodeGenParams.default)
           new GrammarDataGen(evaluator, ValueGrammar).generateFor(p.as, p.pc, nTests, 1000)
         }
 
@@ -66,13 +63,13 @@ abstract class TEGISLike[T <: Typed](name: String) extends Rule(name) {
         if (gi.nonEmpty) {
 
           val evalParams = CodeGenParams.default.copy(maxFunctionInvocations = 2000)
-          val evaluator  = new DualEvaluator(sctx.context, sctx.program, evalParams)
+          val evaluator  = new DualEvaluator(hctx, hctx.program, evalParams)
 
           val enum = new MemoizedEnumerator[T, Expr, ProductionRule[T, Expr]](grammar.getProductions)
 
           val targetType = tupleTypeWrap(p.xs.map(_.getType))
 
-          val timers = sctx.context.timers.synthesis.rules.tegis
+          val timers = hctx.timers.synthesis.rules.tegis
 
           val allExprs = enum.iterator(params.rootLabel(targetType))
 
@@ -113,7 +110,7 @@ abstract class TEGISLike[T <: Typed](name: String) extends Rule(name) {
 
           RuleClosed(toStream)
         } else {
-          sctx.reporter.debug("No test available")
+          hctx.reporter.debug("No test available")
           RuleFailed()
         }
       }
diff --git a/src/test/scala/leon/integration/solvers/StringRenderSuite.scala b/src/test/scala/leon/integration/solvers/StringRenderSuite.scala
index cd6bd2ee7..fb3f1ecae 100644
--- a/src/test/scala/leon/integration/solvers/StringRenderSuite.scala
+++ b/src/test/scala/leon/integration/solvers/StringRenderSuite.scala
@@ -1,46 +1,26 @@
-package leon.integration.solvers
+package leon
+package integration
+package solvers
 
+import leon.test.LeonTestSuiteWithProgram
 
-import org.scalatest.FunSuite
-import org.scalatest.Matchers
-import leon.test.helpers.ExpressionsDSL
-import leon.solvers.string.StringSolver
 import leon.purescala.Common.FreshIdentifier
-import leon.purescala.Common.Identifier
 import leon.purescala.Expressions._
 import leon.purescala.Definitions._
 import leon.purescala.DefOps
 import leon.purescala.ExprOps
 import leon.purescala.Types._
-import leon.purescala.TypeOps
 import leon.purescala.Constructors._
+
+import leon.synthesis._
 import leon.synthesis.rules.{StringRender, TypedTemplateGenerator}
-import scala.collection.mutable.{HashMap => MMap}
-import scala.concurrent.Future
-import scala.concurrent.ExecutionContext.Implicits.global
-import org.scalatest.concurrent.Timeouts
-import org.scalatest.concurrent.ScalaFutures
-import org.scalatest.time.SpanSugar._
-import org.scalatest.FunSuite
-import org.scalatest.concurrent.Timeouts
-import org.scalatest.concurrent.ScalaFutures
-import org.scalatest.time.SpanSugar._
-import leon.purescala.Types.Int32Type
-import leon.test.LeonTestSuiteWithProgram
-import leon.synthesis.SourceInfo
-import leon.synthesis.CostModels
-import leon.synthesis.graph.SimpleSearch
+import leon.synthesis.strategies.CostBasedStrategy
 import leon.synthesis.graph.AndNode
-import leon.synthesis.SearchContext
-import leon.synthesis.Synthesizer
-import leon.synthesis.SynthesisSettings
-import leon.synthesis.RuleApplication
-import leon.synthesis.RuleClosed
+
 import leon.evaluators._
-import leon.LeonContext
-import leon.synthesis.rules.DetupleInput
-import leon.synthesis.Rules
-import leon.solvers.ModelBuilder
+
+import org.scalatest.concurrent.ScalaFutures
+import org.scalatest.Matchers
 import scala.language.implicitConversions
 
 class StringRenderSuite extends LeonTestSuiteWithProgram with Matchers with ScalaFutures {
@@ -102,11 +82,11 @@ class StringRenderSuite extends LeonTestSuiteWithProgram with Matchers with Scal
 
   def applyStringRenderOn(functionName: String): (FunDef, Program) = {
     val ci = synthesisInfos(functionName)
-    val search = new SimpleSearch(ctx, ci, ci.problem, CostModels.default, Some(200))
+    val search = new Search(ctx, ci, ci.problem, new CostBasedStrategy(ctx, CostModels.default))
     val synth = new Synthesizer(ctx, program, ci, SynthesisSettings(rules = Seq(StringRender)))
     val orNode = search.g.root
     if (!orNode.isExpanded) {
-      val hctx = SearchContext(synth.sctx, synth.ci, orNode, search)
+      val hctx = new SearchContext(synth.sctx, synth.ci, orNode, search)
       orNode.expand(hctx)
     }
     val andNodes = orNode.descendants.collect {
@@ -115,11 +95,6 @@ class StringRenderSuite extends LeonTestSuiteWithProgram with Matchers with Scal
     }
 
     val rulesApps = (for ((t, i) <- andNodes.zipWithIndex) yield {
-      val status = if (t.isDeadEnd) {
-        "closed"
-      } else {
-        "open"
-      }
       t.ri.asString -> i
     }).toMap
     rulesApps should contain key "String conversion"
@@ -129,7 +104,7 @@ class StringRenderSuite extends LeonTestSuiteWithProgram with Matchers with Scal
     
     val solutions = (search.traversePath(path) match {
       case Some(an: AndNode) =>
-        val hctx = SearchContext(synth.sctx, synth.ci, an, search)
+        val hctx = new SearchContext(synth.sctx, synth.ci, an, search)
         an.ri.apply(hctx)
       case _ => throw new Exception("Was not an and node")
     }) match {
@@ -241,7 +216,6 @@ class StringRenderSuite extends LeonTestSuiteWithProgram with Matchers with Scal
   def synthesizeAndTest(functionName: String, tests: (Seq[Expr], String)*) {
     val (fd, program) = applyStringRenderOn(functionName)
     val when = new DefaultEvaluator(ctx, program)
-    val args = getFunctionArguments(functionName)
     for((in, out) <- tests) {
       val expr = functionInvocation(fd, in)
       when.eval(expr) match {
@@ -254,7 +228,6 @@ class StringRenderSuite extends LeonTestSuiteWithProgram with Matchers with Scal
   def synthesizeAndAbstractTest(functionName: String)(tests: (FunDef, Program) => Seq[(Seq[Expr], Expr)]) {
     val (fd, program) = applyStringRenderOn(functionName)
     val when_abstract = new AbstractEvaluator(ctx, program)
-    val args = getFunctionArguments(functionName)
     for((in, out) <- tests(fd, program)) {
       val expr = functionInvocation(fd, in)
       when_abstract.eval(expr) match {
diff --git a/src/test/scala/leon/regression/repair/RepairSuite.scala b/src/test/scala/leon/regression/repair/RepairSuite.scala
index 6301f4ea1..5f9861362 100644
--- a/src/test/scala/leon/regression/repair/RepairSuite.scala
+++ b/src/test/scala/leon/regression/repair/RepairSuite.scala
@@ -34,7 +34,7 @@ class RepairSuite extends LeonRegressionSuite {
     val reporter = new TestSilentReporter
     //val reporter = new DefaultReporter(Set(utils.DebugSectionRepair))
 
-    val ctx = LeonContext(
+    val ctx = new LeonContext(
       reporter = reporter,
       interruptManager = new InterruptManager(reporter),
       options = Seq(
diff --git a/src/test/scala/leon/regression/synthesis/StablePrintingSuite.scala b/src/test/scala/leon/regression/synthesis/StablePrintingSuite.scala
index c8bfc2688..61bbe1deb 100644
--- a/src/test/scala/leon/regression/synthesis/StablePrintingSuite.scala
+++ b/src/test/scala/leon/regression/synthesis/StablePrintingSuite.scala
@@ -104,9 +104,9 @@ class StablePrintingSuite extends LeonRegressionSuite {
             val sctx = synthesizer.sctx
             try {
               val search = synthesizer.getSearch
-              val hctx = SearchContext(sctx, ci, search.g.root, search)
+              val hctx = new SearchContext(sctx, ci, search.g.root, search)
               val problem = ci.problem
-              info(j.info("synthesis "+problem.asString(sctx.context)))
+              info(j.info("synthesis "+problem.asString(sctx)))
               val apps = decompRules flatMap { _.instantiateOn(hctx, problem)}
 
               for (a <- apps) {
-- 
GitLab