From e69cbc147a291ac3483f3bb025cdbf1843fdd83d Mon Sep 17 00:00:00 2001
From: Etienne Kneuss <ekneuss@gmail.com>
Date: Mon, 19 Nov 2012 22:23:19 +0100
Subject: [PATCH] Some re-work based on and-or graphs

---
 src/main/scala/leon/aographs/Graph.scala      | 235 ++++++++++++++++++
 src/main/scala/leon/synthesis/Cost.scala      |  20 ++
 .../scala/leon/synthesis/DerivationTree.scala |   2 +
 .../scala/leon/synthesis/Heuristics.scala     |  12 +-
 src/main/scala/leon/synthesis/Rules.scala     |  41 +--
 src/main/scala/leon/synthesis/Solution.scala  |   9 +-
 .../scala/leon/synthesis/Synthesizer.scala    | 136 +++++-----
 src/main/scala/leon/synthesis/Task.scala      |   2 +
 .../synthesis/heuristics/ADTInduction.scala   |   6 +-
 .../synthesis/heuristics/IntInduction.scala   |   8 +-
 .../heuristics/OptimisticInjection.scala      |   8 +-
 .../heuristics/SelectiveInlining.scala        |   8 +-
 .../scala/leon/synthesis/rules/ADTDual.scala  |   8 +-
 .../scala/leon/synthesis/rules/ADTSplit.scala |   8 +-
 .../scala/leon/synthesis/rules/Assert.scala   |  12 +-
 .../leon/synthesis/rules/CaseSplit.scala      |   7 +-
 .../scala/leon/synthesis/rules/Cegis.scala    |   8 +-
 .../leon/synthesis/rules/EqualitySplit.scala  |   8 +-
 .../scala/leon/synthesis/rules/Ground.scala   |  12 +-
 .../synthesis/rules/IntegerEquation.scala     |  10 +-
 .../synthesis/rules/IntegerInequalities.scala |   9 +-
 .../scala/leon/synthesis/rules/OnePoint.scala |   9 +-
 .../synthesis/rules/OptimisticGround.scala    |  18 +-
 .../synthesis/rules/UnconstrainedOutput.scala |   7 +-
 .../leon/synthesis/rules/Unification.scala    |  16 +-
 .../leon/synthesis/rules/UnusedInput.scala    |   7 +-
 26 files changed, 428 insertions(+), 198 deletions(-)
 create mode 100644 src/main/scala/leon/aographs/Graph.scala
 create mode 100644 src/main/scala/leon/synthesis/Cost.scala

diff --git a/src/main/scala/leon/aographs/Graph.scala b/src/main/scala/leon/aographs/Graph.scala
new file mode 100644
index 000000000..6519fe3b5
--- /dev/null
+++ b/src/main/scala/leon/aographs/Graph.scala
@@ -0,0 +1,235 @@
+package leon.aographs
+
+trait AOCost extends Ordered[AOCost] {
+  def +(that: AOCost): AOCost = AOCostFixed(value + that.value)
+
+  def value: Int
+
+  def compare(that: AOCost) = this.value - that.value
+}
+
+case class AOCostFixed(value: Int) extends AOCost
+
+object AOCost {
+  val zero = new AOCostFixed(0)
+}
+
+trait AOTask[S <: AOSolution] {
+  def composeSolution(sols: List[S]): S
+  def cost: AOCost
+}
+
+trait AOSolution {
+  def cost: AOCost
+}
+
+
+class AndOrGraph[T <: AOTask[S], S <: AOSolution](val root: T) {
+  type C = AOCost
+
+  var tree: AndTree = RootNode
+
+  trait Tree {
+    val task  : T
+    val parent: Node
+
+    def minCost: C
+
+    def isSolved: Boolean
+  }
+
+  abstract class AndTree extends Tree
+  abstract class OrTree extends Tree
+
+
+  trait Leaf extends Tree with Ordered[Leaf] {
+    val minCost: C = task.cost
+
+    def compare(that: Leaf) = this.minCost.compare(that.minCost)
+
+    def isSolved: Boolean = false
+    def expandWith(succ: List[T])
+  }
+
+  trait Node {
+    def expandLeaf(l: Leaf, succ: List[T])
+    def unsolvable(l: Tree)
+    def notifySolution(sub: Tree, sol: S)
+
+    var solution: Option[S] = None
+
+    def isSolved: Boolean = solution.isDefined
+  }
+
+  case class AndNode(parent: OrNode, subTasks: List[T], task: T) extends AndTree with Node {
+    var subProblems         = Map[T, OrTree]()
+    var subSolutions        = Map[T, S]()
+
+    def computeCost = {
+      solution match {
+        case Some(s) =>
+          s.cost
+        case _ =>
+          val subCosts = subProblems.map { case (t, ot) => subSolutions.get(t).map(_.cost).getOrElse(ot.minCost) }
+
+          subCosts.foldLeft(task.cost)(_ + _)
+      }
+    }
+
+    var minCost = computeCost
+
+    def unsolvable(l: Tree) {
+      parent.unsolvable(this)
+    }
+
+    def expandLeaf(l: Leaf, succ: List[T]) {
+      val n = OrNode(this, Map(), l.task)
+      n.alternatives = succ.map(t => t -> AndLeaf(n, t)).toMap
+      n.minAlternative = n.computeMin
+
+      subProblems += l.task -> n
+    }
+
+    def notifySolution(sub: Tree, sol: S) {
+      subSolutions += sub.task -> sol
+
+      if (subSolutions.size == subProblems.size) {
+        solution = Some(task.composeSolution(subTasks.map(subSolutions)))
+        minCost = computeCost
+
+        notifyParent(solution.get)
+      } else {
+        minCost = computeCost
+      }
+
+    }
+
+    def notifyParent(s: S) {
+      parent.notifySolution(this, s)
+    }
+  }
+
+  object RootNode extends AndLeaf(null, root) {
+    override def expandWith(succ: List[T]) {
+      val n = new AndNode(null, succ, task) {
+        override def unsolvable(l: Tree) {}
+        override def notifyParent(sol: S) {}
+      }
+
+      n.subProblems = succ.map(t => t -> OrLeaf(n, t)).toMap
+
+      tree = n
+    }
+  }
+
+  case class AndLeaf(parent: OrNode, task: T) extends AndTree with Leaf {
+    def expandWith(succ: List[T]) {
+      parent.expandLeaf(this, succ)
+    }
+
+  }
+
+
+  case class OrNode(parent: AndNode, var alternatives: Map[T, AndTree], task: T) extends OrTree with Node {
+    var minAlternative: Tree    = _
+    def minCost: C              = minAlternative.minCost
+
+    def computeMin = {
+      if (!alternatives.isEmpty) {
+        alternatives.values.minBy(_.minCost)
+      } else {
+        null
+      }
+    }
+
+    def unsolvable(l: Tree) {
+      alternatives -= l.task
+
+      if (alternatives.isEmpty) {
+        parent.unsolvable(this)
+      } else {
+        minAlternative = computeMin
+      }
+    }
+
+    def expandLeaf(l: Leaf, succ: List[T]) {
+      val n = AndNode(this, succ, l.task)
+      n.subProblems = succ.map(t => t -> OrLeaf(n, t)).toMap
+      n.minCost     = n.computeCost
+
+      alternatives += l.task -> n
+    }
+
+    def notifySolution(sub: Tree, sol: S) {
+      solution match {
+        case Some(preSol) if (preSol.cost < sol.cost) =>
+          solution       = Some(sol)
+          minAlternative = sub
+
+          parent.notifySolution(this, solution.get)
+        case None =>
+          solution       = Some(sol)
+          minAlternative = sub
+
+          parent.notifySolution(this, solution.get)
+      }
+    }
+  }
+
+  case class OrLeaf(parent: AndNode, task: T) extends OrTree with Leaf {
+    def expandWith(succ: List[T]) {
+      parent.expandLeaf(this, succ)
+    }
+  }
+}
+
+abstract class AndOrGraphSearch[T <: AOTask[S], S <: AOSolution](val g: AndOrGraph[T, S]) {
+  import collection.mutable.PriorityQueue
+
+  def nextLeaf: Option[g.Leaf] = {
+    var c: g.Tree = g.tree
+    var res: Option[g.Leaf] = None
+
+    while(res.isEmpty) {
+      c match {
+        case l: g.Leaf =>
+          res = Some(l)
+
+        case an: g.AndNode =>
+          c = an.subProblems.values.minBy(_.minCost)
+
+        case on: g.OrNode =>
+          c = on.minAlternative
+      }
+    }
+
+    res
+  }
+
+  abstract class ExpandResult
+  case class Expanded(sub: List[T]) extends ExpandResult
+  case class ExpandSuccess(sol: S) extends ExpandResult
+  case object ExpandFailure extends ExpandResult
+
+  var continue = true
+
+  def search = {
+    while (!g.tree.isSolved && continue) {
+      nextLeaf match {
+        case Some(l) =>
+          processLeaf(l) match {
+            case r @ Expanded(ls) =>
+              l.expandWith(ls)
+            case r @ ExpandSuccess(sol) =>
+              l.parent.notifySolution(l, sol)
+            case r @ ExpandFailure =>
+              l.parent.unsolvable(l)
+          }
+        case None =>
+          continue = false
+      }
+    }
+  }
+
+  def processLeaf(l: g.Leaf): ExpandResult
+}
diff --git a/src/main/scala/leon/synthesis/Cost.scala b/src/main/scala/leon/synthesis/Cost.scala
new file mode 100644
index 000000000..bd479a489
--- /dev/null
+++ b/src/main/scala/leon/synthesis/Cost.scala
@@ -0,0 +1,20 @@
+package leon
+package synthesis
+
+import purescala.Trees._
+import purescala.TreeOps._
+
+import aographs.AOCost
+
+case class SolutionCost(s: Solution) extends AOCost {
+  val value = {
+    val chooses = collectChooses(s.toExpr)
+    val chooseCost = chooses.foldLeft(0)((i, c) => i + (1000 * math.pow(2, c.vars.size).toInt + formulaSize(c.pred)))
+
+    formulaSize(s.toExpr) + chooseCost
+  }
+}
+
+case class ProblemCost(p: Problem) extends AOCost {
+  val value = math.pow(2, p.xs.size).toInt + formulaSize(p.phi)
+}
diff --git a/src/main/scala/leon/synthesis/DerivationTree.scala b/src/main/scala/leon/synthesis/DerivationTree.scala
index eae0104f4..0bf44b00e 100644
--- a/src/main/scala/leon/synthesis/DerivationTree.scala
+++ b/src/main/scala/leon/synthesis/DerivationTree.scala
@@ -1,6 +1,7 @@
 package leon
 package synthesis
 
+/*
 class DerivationTree(root: RootTask)  {
 
   private[this] var _nextID = 0
@@ -60,3 +61,4 @@ class DerivationTree(root: RootTask)  {
   }
 
 }
+*/
diff --git a/src/main/scala/leon/synthesis/Heuristics.scala b/src/main/scala/leon/synthesis/Heuristics.scala
index 05b35b83f..928d7e31c 100644
--- a/src/main/scala/leon/synthesis/Heuristics.scala
+++ b/src/main/scala/leon/synthesis/Heuristics.scala
@@ -7,10 +7,10 @@ import heuristics._
 
 object Heuristics {
   def all = Set[Synthesizer => Rule](
-    new IntInduction(_)
+    new IntInduction(_),
     //new OptimisticInjection(_),
     //new SelectiveInlining(_),
-//    new ADTInduction(_)
+    new ADTInduction(_)
   )
 }
 
@@ -35,14 +35,16 @@ object HeuristicStep {
   }
 
   def apply(synth: Synthesizer, problem: Problem, subProblems: List[Problem], onSuccess: List[Solution] => Solution) = {
-    RuleMultiSteps(subProblems, Nil, onSuccess.andThen(verifyPre(synth, problem)))
+    new RuleApplication(subProblems.size, onSuccess.andThen(verifyPre(synth, problem))) {
+      def apply() = RuleDecomposed(subProblems, onSuccess)
+    }
   }
 }
 
 
-object HeuristicOneStep {
+object HeuristicFastStep {
   def apply(synth: Synthesizer, problem: Problem, subProblems: List[Problem], onSuccess: List[Solution] => Solution) = {
-    RuleAlternatives(Set(HeuristicStep(synth, problem, subProblems, onSuccess)))
+    RuleResult(List(HeuristicStep(synth, problem, subProblems, onSuccess)))
   }
 }
 
diff --git a/src/main/scala/leon/synthesis/Rules.scala b/src/main/scala/leon/synthesis/Rules.scala
index ea4271b45..8602b74fb 100644
--- a/src/main/scala/leon/synthesis/Rules.scala
+++ b/src/main/scala/leon/synthesis/Rules.scala
@@ -26,32 +26,43 @@ object Rules {
   )
 }
 
-sealed abstract class RuleResult
-case class RuleSuccess(solution: Solution) extends RuleResult
-case class RuleAlternatives(steps: Traversable[RuleMultiSteps]) extends RuleResult
+case class RuleResult(alternatives: Traversable[RuleApplication])
+object RuleInapplicable extends RuleResult(List())
 
-case class RuleMultiSteps(subProblems: List[Problem],
-                          interSteps: List[List[Solution] => List[Problem]],
-                          onSuccess: List[Solution] => (Solution, Boolean))
+abstract class RuleApplication(val subProblemsCount: Int,
+                               val onSuccess: List[Solution] => (Solution, Boolean)) {
 
-object RuleInapplicable {
-  def apply() = RuleAlternatives(Seq())
+  def apply(): RuleApplicationResult
 }
 
-object RuleStep {
-  def apply(subProblems: List[Problem], onSuccess: List[Solution] => Solution) = {
-    RuleMultiSteps(subProblems, Nil, onSuccess.andThen((_, true)))
+sealed abstract class RuleApplicationResult
+case class RuleSuccess(solution: Solution) extends RuleApplicationResult
+case class RuleDecomposed(sub: List[Problem], onSuccess: List[Solution] => (Solution, Boolean)) extends RuleApplicationResult
+
+object RuleFastApplication {
+  def apply(sub: List[Problem], onSuccess: List[Solution] => Solution) = {
+    new RuleApplication(sub.size, ls => (onSuccess(ls), true)) {
+      def apply() = RuleDecomposed(sub, onSuccess)
+    }
+  }
+}
+
+object RuleFastStep {
+  def apply(sub: List[Problem], onSuccess: List[Solution] => Solution) = {
+    RuleResult(List(RuleFastApplication(sub, onSuccess)))
   }
 }
 
-object RuleOneStep {
-  def apply(subProblems: List[Problem], onSuccess: List[Solution] => Solution) = {
-    RuleAlternatives(Set(RuleStep(subProblems, onSuccess)))
+object RuleFastSuccess {
+  def apply(solution: Solution) = {
+    RuleResult(List(new RuleApplication(0, ls => (solution, true)) {
+      def apply() = RuleSuccess(solution)
+    }))
   }
 }
 
 abstract class Rule(val name: String, val synth: Synthesizer, val priority: Priority) {
-  def applyOn(task: Task): RuleResult
+  def attemptToApplyOn(problem: Problem): RuleResult
 
   def subst(what: Tuple2[Identifier, Expr], in: Expr): Expr = replace(Map(Variable(what._1) -> what._2), in)
   def substAll(what: Map[Identifier, Expr], in: Expr): Expr = replace(what.map(w => Variable(w._1) -> w._2), in)
diff --git a/src/main/scala/leon/synthesis/Solution.scala b/src/main/scala/leon/synthesis/Solution.scala
index 309addbaf..255d7fd10 100644
--- a/src/main/scala/leon/synthesis/Solution.scala
+++ b/src/main/scala/leon/synthesis/Solution.scala
@@ -4,13 +4,14 @@ package synthesis
 import leon.purescala.Trees._
 import leon.purescala.Definitions._
 import leon.purescala.TreeOps._
+import aographs._
 
 // Defines a synthesis solution of the form:
 // ⟨ P | T ⟩
-class Solution(val pre: Expr, val defs: Set[FunDef], val term: Expr) {
+class Solution(val pre: Expr, val defs: Set[FunDef], val term: Expr) extends AOSolution {
   override def toString = "⟨ "+pre+" | "+defs.mkString(" ")+" "+term+" ⟩" 
 
-  lazy val complexity: AbsSolComplexity = new SolComplexity(this)
+  val cost: AOCost = null
 
   def toExpr = {
     val result = if (pre == BooleanLiteral(true)) {
@@ -47,6 +48,10 @@ object Solution {
     new Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(id => simplestValue(id.getType))))
   }
 
+  def simplest: Solution = {
+    new Solution(BooleanLiteral(true), Set(), BooleanLiteral(true))
+  }
+
   def none: Solution = {
     throw new Exception("Unexpected failure to construct solution")
   }
diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala
index d88d3e898..5b95fa9f5 100644
--- a/src/main/scala/leon/synthesis/Synthesizer.scala
+++ b/src/main/scala/leon/synthesis/Synthesizer.scala
@@ -25,105 +25,93 @@ class Synthesizer(val reporter: Reporter,
 
   val rules = ruleConstructors.map(_.apply(this))
 
-  var derivationCounter = 1;
-
-  val rootTask: RootTask = new RootTask(this, problem)
-
-  val workList = new PriorityQueue[Task]()
-
-  def bestSolutionSoFar(): Solution = {
-    rootTask.solution.getOrElse(worstSolution)
-  }
-
-  val worstSolution = Solution.choose(problem)
-
   var continue = true
 
   def synthesize(): Solution = {
-    continue = true
-    workList.clear()
-    workList += rootTask
-
-    val ts = System.currentTimeMillis
-
-    def currentDurationMs = System.currentTimeMillis-ts
-
-    def timeoutExpired(): Boolean = {
-      timeoutMs match {
-        case Some(t) if currentDurationMs/1000 > t => true
-        case _ => false
-      }
-    }
 
     val sigINT = new Signal("INT")
     var oldHandler: SignalHandler = null
     oldHandler = Signal.handle(sigINT, new SignalHandler {
       def handle(sig: Signal) {
         reporter.info("Aborting...")
-        continue = false;
+        continue = false
         Signal.handle(sigINT, oldHandler)
       }
     })
 
-    while (!workList.isEmpty && continue) {
-      val task = workList.dequeue()
+    /*
+    if (generateDerivationTrees) {
+      val deriv = new DerivationTree(rootTask)
+      deriv.toDotFile("derivation"+derivationCounter+".dot")
+      derivationCounter += 1
+    }
+    */
 
-      val prefix = "[%-20s] ".format(Option(task.rule).map(_.toString).getOrElse("root"))
+    val search = new AOSearch(problem, rules)
 
-      if (!(firstOnly && (task.parent ne null) && task.parent.isSolvedFor(task.problem))) {
-        val subProblems = task.run()
+    val res = search.search
 
-        if (task.minComplexity <= bestSolutionSoFar.complexity) {
-          if (task.solution.isDefined || !subProblems.isEmpty) {
-            if (task.solution.isDefined) {
-              info(prefix+"Got: "+task.problem)
-              info(prefix+"Solved with: "+task.solution.get)
-            } else {
-              info(prefix+"Got: "+task.problem)
-              info(prefix+"Decomposed into:")
-              for(p <- subProblems) {
-                info(prefix+" - "+p)
-              }
-            }
-          }
-          addProblems(task, subProblems)
-        }
-      }
+    println(res)
 
-      if (timeoutExpired()) {
-        warning("Timeout reached")
-        continue = false
-      }
-    }
+    Solution.none
+  }
 
-    if (!workList.isEmpty) {
-      // We flush the worklist by solving everything with chooses, that should
-      // rebuild a partial solution
-      while (!workList.isEmpty) {
-        val t = workList.dequeue()
 
-        if (t.parent ne null) {
-          t.parent.partlySolvedBy(t, Solution.choose(t.problem))          
-        }
-      }
-    }
+  import aographs._
 
-    info("Finished in "+currentDurationMs+"ms")
+  abstract class Task extends AOTask[Solution]
+  case class TaskRunRule(problem: Problem, rule: Rule, app: RuleApplication) extends Task {
+    val subSols = (1 to app.subProblemsCount).map {i => Solution.simplest }.toList
+    val simpleSol = app.onSuccess(subSols)
 
-    if (generateDerivationTrees) {
-      val deriv = new DerivationTree(rootTask)
-      deriv.toDotFile("derivation"+derivationCounter+".dot")
-      derivationCounter += 1
+    def cost = SolutionCost(simpleSol._1)
+
+    def composeSolution(sols: List[Solution]): Solution = {
+      app.onSuccess(sols)._1
     }
+  }
 
-    bestSolutionSoFar()
+  case class TaskTryRules(p: Problem) extends Task {
+    def cost = ProblemCost(p)
+
+    def composeSolution(sols: List[Solution]): Solution = {
+      sys.error("Should not be called")
+    }
   }
 
-  def addProblems(task: Task, problems: Traversable[Problem]) {
-    // Check if solving this task has the slightest chance of improving the
-    // current solution
-    for (p <- problems; rule <- rules) yield {
-      workList += new Task(this, task, p, rule)
+  class AOSearch(problem: Problem, rules: Set[Rule]) extends AndOrGraphSearch[Task, Solution](new AndOrGraph(TaskTryRules(problem))) {
+    def processLeaf(l: g.Leaf) = {
+      l.task match {
+        case t: TaskTryRules =>
+          val sub = rules.flatMap ( r => r.attemptToApplyOn(t.p).alternatives.map(TaskRunRule(t.p, r, _)) )
+
+          if (!sub.isEmpty) {
+            Expanded(sub.toList)
+          } else {
+            ExpandFailure
+          }
+
+        case t: TaskRunRule=>
+          val prefix = "[%-20s] ".format(Option(t.rule))
+
+          t.app.apply() match {
+            case RuleSuccess(sol) =>
+              info(prefix+"Got: "+t.problem)
+              info(prefix+"Solved with: "+sol)
+
+              ExpandSuccess(sol)
+            case RuleDecomposed(sub, onSuccess) =>
+              info(prefix+"Got: "+t.problem)
+              info(prefix+"Decomposed into:")
+              for(p <- sub) {
+                info(prefix+" - "+p)
+              }
+
+              Expanded(sub.map(TaskTryRules(_)))
+          }
+      }
     }
   }
 }
+
+
diff --git a/src/main/scala/leon/synthesis/Task.scala b/src/main/scala/leon/synthesis/Task.scala
index 260745880..e2d8d65fc 100644
--- a/src/main/scala/leon/synthesis/Task.scala
+++ b/src/main/scala/leon/synthesis/Task.scala
@@ -1,6 +1,7 @@
 package leon
 package synthesis
 
+/*
 class Task(synth: Synthesizer,
            val parent: Task,
            val problem: Problem,
@@ -139,3 +140,4 @@ class RootTask(synth: Synthesizer, problem: Problem) extends Task(synth, null, p
     }
   }
 }
+*/
diff --git a/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala b/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala
index 1c2769402..30cbe5bfd 100644
--- a/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala
+++ b/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala
@@ -10,9 +10,7 @@ import purescala.TypeTrees._
 import purescala.Definitions._
 
 class ADTInduction(synth: Synthesizer) extends Rule("ADT Induction", synth, 80) with Heuristic {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val candidates = p.as.collect {
         case IsTyped(origId, AbstractClassType(cd)) => (origId, cd)
     }
@@ -104,6 +102,6 @@ class ADTInduction(synth: Synthesizer) extends Rule("ADT Induction", synth, 80)
       }
     }
 
-    RuleAlternatives(steps.flatten)
+    RuleResult(steps.flatten)
   }
 }
diff --git a/src/main/scala/leon/synthesis/heuristics/IntInduction.scala b/src/main/scala/leon/synthesis/heuristics/IntInduction.scala
index a82eae8f7..f75e44e6d 100644
--- a/src/main/scala/leon/synthesis/heuristics/IntInduction.scala
+++ b/src/main/scala/leon/synthesis/heuristics/IntInduction.scala
@@ -10,9 +10,7 @@ import purescala.TypeTrees._
 import purescala.Definitions._
 
 class IntInduction(synth: Synthesizer) extends Rule("Int Induction", synth, 50) with Heuristic {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     p.as match {
       case List(IsTyped(origId, Int32Type)) =>
         val tpe = TupleType(p.xs.map(_.getType))
@@ -56,9 +54,9 @@ class IntInduction(synth: Synthesizer) extends Rule("Int Induction", synth, 50)
             Solution.none
         }
 
-        HeuristicOneStep(synth, p, List(subBase, subGT, subLT), onSuccess)
+        HeuristicFastStep(synth, p, List(subBase, subGT, subLT), onSuccess)
       case _ =>
-        RuleInapplicable()
+        RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala b/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala
index 8f3e65195..85d5f34cc 100644
--- a/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala
+++ b/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala
@@ -10,9 +10,7 @@ import purescala.TypeTrees._
 import purescala.Definitions._
 
 class OptimisticInjection(synth: Synthesizer) extends Rule("Opt. Injection", synth, 50) with Heuristic {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val TopLevelAnds(exprs) = p.phi
 
     val eqfuncalls = exprs.collect{
@@ -38,9 +36,9 @@ class OptimisticInjection(synth: Synthesizer) extends Rule("Opt. Injection", syn
 
       val sub = p.copy(phi = And(newExprs))
 
-      HeuristicOneStep(synth, p, List(sub), forward)
+      HeuristicFastStep(synth, p, List(sub), forward)
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala b/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala
index 6ee4935f5..4fe3f764d 100644
--- a/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala
+++ b/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala
@@ -10,9 +10,7 @@ import purescala.TypeTrees._
 import purescala.Definitions._
 
 class SelectiveInlining(synth: Synthesizer) extends Rule("Sel. Inlining", synth, 20) with Heuristic {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val TopLevelAnds(exprs) = p.phi
 
     val eqfuncalls = exprs.collect{
@@ -38,9 +36,9 @@ class SelectiveInlining(synth: Synthesizer) extends Rule("Sel. Inlining", synth,
 
       val sub = p.copy(phi = And(newExprs))
 
-      HeuristicOneStep(synth, p, List(sub), forward)
+      HeuristicFastStep(synth, p, List(sub), forward)
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/ADTDual.scala b/src/main/scala/leon/synthesis/rules/ADTDual.scala
index 1cf0a71f1..bb4753752 100644
--- a/src/main/scala/leon/synthesis/rules/ADTDual.scala
+++ b/src/main/scala/leon/synthesis/rules/ADTDual.scala
@@ -7,9 +7,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class ADTDual(synth: Synthesizer) extends Rule("ADTDual", synth, 200) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val xs = p.xs.toSet
     val as = p.as.toSet
 
@@ -26,9 +24,9 @@ class ADTDual(synth: Synthesizer) extends Rule("ADTDual", synth, 200) {
     if (!toRemove.isEmpty) {
       val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq))
 
-      RuleOneStep(List(sub), forward)
+      RuleFastStep(List(sub), forward)
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/ADTSplit.scala b/src/main/scala/leon/synthesis/rules/ADTSplit.scala
index 41236da02..39e987c25 100644
--- a/src/main/scala/leon/synthesis/rules/ADTSplit.scala
+++ b/src/main/scala/leon/synthesis/rules/ADTSplit.scala
@@ -10,9 +10,7 @@ import purescala.Extractors._
 import purescala.Definitions._
 
 class ADTSplit(synth: Synthesizer) extends Rule("ADT Split.", synth, 70) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val candidates = p.as.collect {
       case IsTyped(id, AbstractClassType(cd)) =>
 
@@ -72,11 +70,11 @@ class ADTSplit(synth: Synthesizer) extends Rule("ADT Split.", synth, 70) {
             Solution(Or(globalPre), sols.flatMap(_.defs).toSet, MatchExpr(Variable(id), cases))
         }
 
-        Some(RuleStep(subInfo.map(_._2).toList, onSuccess))
+        Some(RuleFastApplication(subInfo.map(_._2).toList, onSuccess))
       case _ =>
         None
     }}.flatten
 
-    RuleAlternatives(steps)
+    RuleResult(steps)
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/Assert.scala b/src/main/scala/leon/synthesis/rules/Assert.scala
index 29514b2b3..62436e04d 100644
--- a/src/main/scala/leon/synthesis/rules/Assert.scala
+++ b/src/main/scala/leon/synthesis/rules/Assert.scala
@@ -7,9 +7,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class Assert(synth: Synthesizer) extends Rule("Assert", synth, 200) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     p.phi match {
       case TopLevelAnds(exprs) =>
         val xsSet = p.xs.toSet
@@ -18,17 +16,17 @@ class Assert(synth: Synthesizer) extends Rule("Assert", synth, 200) {
 
         if (!exprsA.isEmpty) {
           if (others.isEmpty) {
-            RuleSuccess(Solution(And(exprsA), Set(), Tuple(p.xs.map(id => simplestValue(Variable(id))))))
+            RuleFastSuccess(Solution(And(exprsA), Set(), Tuple(p.xs.map(id => simplestValue(Variable(id))))))
           } else {
             val sub = p.copy(c = And(p.c +: exprsA), phi = And(others))
 
-            RuleOneStep(List(sub), forward)
+            RuleFastStep(List(sub), forward)
           }
         } else {
-          RuleInapplicable()
+          RuleInapplicable
         }
       case _ =>
-        RuleInapplicable()
+        RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/CaseSplit.scala b/src/main/scala/leon/synthesis/rules/CaseSplit.scala
index 42d7e5095..44f0b7bad 100644
--- a/src/main/scala/leon/synthesis/rules/CaseSplit.scala
+++ b/src/main/scala/leon/synthesis/rules/CaseSplit.scala
@@ -7,8 +7,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class CaseSplit(synth: Synthesizer) extends Rule("Case-Split", synth, 200) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
+  def attemptToApplyOn(p: Problem): RuleResult = {
     p.phi match {
       case Or(o1 :: o2 :: _) =>
         val sub1 = Problem(p.as, p.c, o1, p.xs)
@@ -19,9 +18,9 @@ class CaseSplit(synth: Synthesizer) extends Rule("Case-Split", synth, 200) {
           case _ => Solution.none
         }
 
-        RuleOneStep(List(sub1, sub2), onSuccess)
+        RuleFastStep(List(sub1, sub2), onSuccess)
       case _ =>
-        RuleInapplicable()
+        RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/Cegis.scala b/src/main/scala/leon/synthesis/rules/Cegis.scala
index 14bf62a17..80decb7fb 100644
--- a/src/main/scala/leon/synthesis/rules/Cegis.scala
+++ b/src/main/scala/leon/synthesis/rules/Cegis.scala
@@ -10,9 +10,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class CEGIS(synth: Synthesizer) extends Rule("CEGIS", synth, 150) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     case class Generator(tpe: TypeTree, altBuilder: () => List[(Expr, Set[Identifier])]);
 
     var generators = Map[TypeTree, Generator]()
@@ -192,7 +190,7 @@ class CEGIS(synth: Synthesizer) extends Rule("CEGIS", synth, 150) {
                   mapping += c -> substAll(mapping, e)
                 }
 
-                result = Some(RuleSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(mapping))).setType(tpe))))
+                result = Some(RuleFastSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(mapping))).setType(tpe))))
 
               case _ =>
                 synth.reporter.warning("Solver returned 'UNKNOWN' in a CEGIS iteration.")
@@ -213,6 +211,6 @@ class CEGIS(synth: Synthesizer) extends Rule("CEGIS", synth, 150) {
       unrolings += 1
     } while(unrolings < maxUnrolings && lastF != currentF && result.isEmpty && synth.continue)
 
-    result.getOrElse(RuleInapplicable())
+    result.getOrElse(RuleInapplicable)
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/EqualitySplit.scala b/src/main/scala/leon/synthesis/rules/EqualitySplit.scala
index 8cd63ef80..76330d820 100644
--- a/src/main/scala/leon/synthesis/rules/EqualitySplit.scala
+++ b/src/main/scala/leon/synthesis/rules/EqualitySplit.scala
@@ -9,9 +9,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class EqualitySplit(synth: Synthesizer) extends Rule("Eq. Split.", synth, 90) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val candidates = p.as.groupBy(_.getType).map(_._2.toList).filter {
       case List(a1, a2) =>
         val toValEQ = Implies(p.c, Equals(Variable(a1), Variable(a2)))
@@ -50,11 +48,11 @@ class EqualitySplit(synth: Synthesizer) extends Rule("Eq. Split.", synth, 90) {
             Solution.none
         }
 
-        Some(RuleStep(List(sub1, sub2), onSuccess))
+        Some(RuleFastApplication(List(sub1, sub2), onSuccess))
       case _ =>
         None
     }).flatten
 
-    RuleAlternatives(steps)
+    RuleResult(steps)
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/Ground.scala b/src/main/scala/leon/synthesis/rules/Ground.scala
index ddb4c7b6d..ae2284294 100644
--- a/src/main/scala/leon/synthesis/rules/Ground.scala
+++ b/src/main/scala/leon/synthesis/rules/Ground.scala
@@ -8,23 +8,21 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class Ground(synth: Synthesizer) extends Rule("Ground", synth, 500) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     if (p.as.isEmpty) {
 
       val tpe = TupleType(p.xs.map(_.getType))
 
       synth.solver.solveSAT(p.phi) match {
         case (Some(true), model) =>
-          RuleSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(model))).setType(tpe)))
+          RuleFastSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(model))).setType(tpe)))
         case (Some(false), model) =>
-          RuleSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe)))
+          RuleFastSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe)))
         case _ =>
-          RuleInapplicable()
+          RuleInapplicable
       }
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
index a8af1d203..6b94025b6 100644
--- a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
+++ b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
@@ -12,9 +12,7 @@ import LinearEquations.elimVariable
 import ArithmeticNormalization.simplify
 
 class IntegerEquation(synth: Synthesizer) extends Rule("Integer Equation", synth, 300) {
-  def applyOn(task: Task): RuleResult = {
-
-    val problem = task.problem
+  def attemptToApplyOn(problem: Problem): RuleResult = {
 
     val TopLevelAnds(exprs) = problem.phi
 
@@ -43,7 +41,7 @@ class IntegerEquation(synth: Synthesizer) extends Rule("Integer Equation", synth
     allOthers = allOthers ++ candidates
 
     optionNormalizedEq match {
-      case None => RuleInapplicable()
+      case None => RuleInapplicable
       case Some(normalizedEq0) => {
 
         val eqas = problem.as.toSet.intersect(vars)
@@ -62,7 +60,7 @@ class IntegerEquation(synth: Synthesizer) extends Rule("Integer Equation", synth
             case _ => Solution.none
           }
 
-          RuleOneStep(List(newProblem), onSuccess)
+          RuleFastStep(List(newProblem), onSuccess)
 
         } else {
           val (eqPre0, eqWitness, freshxs) = elimVariable(eqas, normalizedEq)
@@ -105,7 +103,7 @@ class IntegerEquation(synth: Synthesizer) extends Rule("Integer Equation", synth
             case _ => Solution.none
           }
 
-          RuleOneStep(List(newProblem), onSuccess)
+          RuleFastStep(List(newProblem), onSuccess)
         }
 
 
diff --git a/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala b/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala
index 80b340595..5f70d9ac5 100644
--- a/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala
+++ b/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala
@@ -12,8 +12,7 @@ import LinearEquations.elimVariable
 import ArithmeticNormalization.simplify
 
 class IntegerInequalities(synth: Synthesizer) extends Rule("Integer Inequalities", synth, 300) {
-  def applyOn(task: Task): RuleResult = {
-    val problem = task.problem
+  def attemptToApplyOn(problem: Problem): RuleResult = {
     val TopLevelAnds(exprs) = problem.phi
 
     //assume that we only have inequalities
@@ -31,7 +30,7 @@ class IntegerInequalities(synth: Synthesizer) extends Rule("Integer Inequalities
     val ineqVars = lhsSides.foldLeft(Set[Identifier]())((acc, lhs) => acc ++ variablesOf(lhs))
     val nonIneqVars = exprNotUsed.foldLeft(Set[Identifier]())((acc, x) => acc ++ variablesOf(x))
     val candidateVars = ineqVars.intersect(problem.xs.toSet).filterNot(nonIneqVars.contains(_))
-    if(candidateVars.isEmpty) RuleInapplicable() else {
+    if(candidateVars.isEmpty) RuleInapplicable else {
       val processedVar = candidateVars.head
       val otherVars: List[Identifier] = problem.xs.filterNot(_ == processedVar)
 
@@ -96,7 +95,7 @@ class IntegerInequalities(synth: Synthesizer) extends Rule("Integer Inequalities
         val pre = And(
           for((ub, uc) <- upperBounds; (lb, lc) <- lowerBounds) 
             yield LessEquals(ceilingDiv(lb, IntLiteral(lc)), floorDiv(ub, IntLiteral(uc))))
-        RuleSuccess(Solution(pre, Set(), witness))
+        RuleFastSuccess(Solution(pre, Set(), witness))
       } else {
         val L = GCD.lcm((upperBounds ::: lowerBounds).map(_._2))
         val newUpperBounds: List[Expr] = upperBounds.map{case (bound, coef) => Times(IntLiteral(L/coef), bound)}
@@ -161,7 +160,7 @@ class IntegerInequalities(synth: Synthesizer) extends Rule("Integer Inequalities
           case _ => Solution.none
         }
 
-        RuleOneStep(List(subProblem), onSuccess)
+        RuleFastStep(List(subProblem), onSuccess)
       }
     }
   }
diff --git a/src/main/scala/leon/synthesis/rules/OnePoint.scala b/src/main/scala/leon/synthesis/rules/OnePoint.scala
index 40f7da864..50b3aa9e6 100644
--- a/src/main/scala/leon/synthesis/rules/OnePoint.scala
+++ b/src/main/scala/leon/synthesis/rules/OnePoint.scala
@@ -7,10 +7,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class OnePoint(synth: Synthesizer) extends Rule("One-point", synth, 300) {
-  def applyOn(task: Task): RuleResult = {
-
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val TopLevelAnds(exprs) = p.phi
 
     val candidates = exprs.collect {
@@ -38,9 +35,9 @@ class OnePoint(synth: Synthesizer) extends Rule("One-point", synth, 300) {
         case _ => Solution.none
       }
 
-      RuleOneStep(List(newProblem), onSuccess)
+      RuleFastStep(List(newProblem), onSuccess)
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala
index 390f41c39..0f3fd8607 100644
--- a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala
+++ b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala
@@ -8,9 +8,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class OptimisticGround(synth: Synthesizer) extends Rule("Optimistic Ground", synth, 150) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
-
+  def attemptToApplyOn(p: Problem): RuleResult = {
     if (!p.as.isEmpty && !p.xs.isEmpty) {
       val xss = p.xs.toSet
       val ass = p.as.toSet
@@ -39,28 +37,28 @@ class OptimisticGround(synth: Synthesizer) extends Rule("Optimistic Ground", syn
                 predicates = valuateWithModelIn(phi, ass, invalidModel) +: predicates
 
               case (Some(false), _) =>
-                result = Some(RuleSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(satModel))).setType(tpe))))
+                result = Some(RuleFastSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(satModel))).setType(tpe))))
 
               case _ =>
-                result = Some(RuleInapplicable())
+                result = Some(RuleInapplicable)
             }
 
           case (Some(false), _) =>
             if (predicates.isEmpty) {
-              result = Some(RuleSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe))))
+              result = Some(RuleFastSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe))))
             } else {
-              result = Some(RuleInapplicable())
+              result = Some(RuleInapplicable)
             }
           case _ =>
-            result = Some(RuleInapplicable())
+            result = Some(RuleInapplicable)
         }
 
         i += 1 
       }
 
-      result.getOrElse(RuleInapplicable())
+      result.getOrElse(RuleInapplicable)
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
   }
 }
diff --git a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala
index 021a68204..3c0dc96b7 100644
--- a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala
+++ b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala
@@ -7,8 +7,7 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class UnconstrainedOutput(synth: Synthesizer) extends Rule("Unconstr.Output", synth, 100) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val unconstr = p.xs.toSet -- variablesOf(p.phi)
 
     if (!unconstr.isEmpty) {
@@ -21,9 +20,9 @@ class UnconstrainedOutput(synth: Synthesizer) extends Rule("Unconstr.Output", sy
           Solution.none
       }
 
-      RuleOneStep(List(sub), onSuccess)
+      RuleFastStep(List(sub), onSuccess)
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
 
   }
diff --git a/src/main/scala/leon/synthesis/rules/Unification.scala b/src/main/scala/leon/synthesis/rules/Unification.scala
index e1412874f..f7aec9959 100644
--- a/src/main/scala/leon/synthesis/rules/Unification.scala
+++ b/src/main/scala/leon/synthesis/rules/Unification.scala
@@ -9,9 +9,7 @@ import purescala.Extractors._
 
 object Unification {
   class DecompTrivialClash(synth: Synthesizer) extends Rule("Unif Dec./Clash/Triv.", synth, 200) {
-    def applyOn(task: Task): RuleResult = {
-      val p = task.problem
-
+    def attemptToApplyOn(p: Problem): RuleResult = {
       val TopLevelAnds(exprs) = p.phi
 
       val (toRemove, toAdd) = exprs.collect {
@@ -29,17 +27,15 @@ object Unification {
         val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq))
 
 
-        RuleOneStep(List(sub), forward)
+        RuleFastStep(List(sub), forward)
       } else {
-        RuleInapplicable()
+        RuleInapplicable
       }
     }
   }
 
   class OccursCheck(synth: Synthesizer) extends Rule("Unif OccursCheck", synth, 200) {
-    def applyOn(task: Task): RuleResult = {
-      val p = task.problem
-
+    def attemptToApplyOn(p: Problem): RuleResult = {
       val TopLevelAnds(exprs) = p.phi
 
       val isImpossible = exprs.exists {
@@ -54,9 +50,9 @@ object Unification {
       if (isImpossible) {
         val tpe = TupleType(p.xs.map(_.getType))
 
-        RuleSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe)))
+        RuleFastSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe)))
       } else {
-        RuleInapplicable()
+        RuleInapplicable
       }
     }
   }
diff --git a/src/main/scala/leon/synthesis/rules/UnusedInput.scala b/src/main/scala/leon/synthesis/rules/UnusedInput.scala
index 482d594a1..18f633348 100644
--- a/src/main/scala/leon/synthesis/rules/UnusedInput.scala
+++ b/src/main/scala/leon/synthesis/rules/UnusedInput.scala
@@ -7,16 +7,15 @@ import purescala.TreeOps._
 import purescala.Extractors._
 
 class UnusedInput(synth: Synthesizer) extends Rule("UnusedInput", synth, 100) {
-  def applyOn(task: Task): RuleResult = {
-    val p = task.problem
+  def attemptToApplyOn(p: Problem): RuleResult = {
     val unused = p.as.toSet -- variablesOf(p.phi) -- variablesOf(p.c)
 
     if (!unused.isEmpty) {
       val sub = p.copy(as = p.as.filterNot(unused))
 
-      RuleOneStep(List(sub), forward)
+      RuleFastStep(List(sub), forward)
     } else {
-      RuleInapplicable()
+      RuleInapplicable
     }
   }
 }
-- 
GitLab