diff --git a/src/main/scala/inox/InoxContext.scala b/src/main/scala/inox/InoxContext.scala
index f029a985b8f0449dfd1bd39d2c996dfdfb132ac2..fa49102840a559fbb0f3d30b827ac64547d902b4 100644
--- a/src/main/scala/inox/InoxContext.scala
+++ b/src/main/scala/inox/InoxContext.scala
@@ -12,7 +12,9 @@ case class InoxContext(
   reporter: Reporter,
   interruptManager: InterruptManager,
   options: Seq[InoxOption[Any]] = Seq(),
-  timers: TimerStorage = new TimerStorage) extends InoxOptions {
+  timers: TimerStorage = new TimerStorage) extends InoxOptions[InoxContext] {
+
+  def build(opts: Seq[InoxOption[Any]]) = InoxContext(reporter, interruptManager, opts, timers)
 
   def toSolver: solvers.SolverOptions = ???
   def toEvaluator: evaluators.EvaluatorOptions = ???
diff --git a/src/main/scala/inox/InoxOptions.scala b/src/main/scala/inox/InoxOptions.scala
index 4ae72dcda50d9551e86411b0b28269729804cac3..151a60ba9d57856c060fb2684d1f421084808b04 100644
--- a/src/main/scala/inox/InoxOptions.scala
+++ b/src/main/scala/inox/InoxOptions.scala
@@ -154,14 +154,28 @@ object OptionsHelpers {
   }
 }
 
-trait InoxOptions {
+trait InoxOptions[Self <: InoxOptions[Self]] {
   val options: Seq[InoxOption[Any]]
 
+  protected def build(newOpts: Seq[InoxOption[Any]]): Self
+
   def findOption[A: ClassTag](optDef: InoxOptionDef[A]): Option[A] = options.collectFirst {
     case InoxOption(`optDef`, value: A) => value
   }
 
   def findOptionOrDefault[A: ClassTag](optDef: InoxOptionDef[A]): A = findOption(optDef).getOrElse(optDef.default)
+
+  def +(newOpt: InoxOption[Any]): Self = build {
+    options.filter(_.optionDef != newOpt.optionDef) :+ newOpt
+  }
+
+  def ++(newOpts: Seq[InoxOption[Any]]): Self = build {
+    val defs = newOpts.map(_.optionDef).toSet
+    options.filter(opt => !defs(opt.optionDef)) ++ newOpts
+  }
+
+  @inline
+  def ++(that: Self): Self = this ++ that.options
 }
 
 object InoxOptions {
diff --git a/src/main/scala/inox/datagen/ModelEnumerator.scala b/src/main/scala/inox/datagen/ModelEnumerator.scala
index 636ff3c16be236b17d6f2f6c46baf3652292afa5..6cfbc50d884955c874bd29db5eb1190f132b17c8 100644
--- a/src/main/scala/inox/datagen/ModelEnumerator.scala
+++ b/src/main/scala/inox/datagen/ModelEnumerator.scala
@@ -8,17 +8,16 @@ import solvers._
 import utils._
 
 trait ModelEnumerator {
-  val factory: SolverFactory
-  lazy val program: factory.program.type = factory.program
+  val program: Program
+  val factory: SolverFactory { val program: ModelEnumerator.this.program.type }
+  val evaluator: DeterministicEvaluator { val program: ModelEnumerator.this.program.type }
+
   import program._
   import program.trees._
   import program.symbols._
 
   import SolverResponses._
 
-  protected val evaluator: RecursiveEvaluator { val program: ModelEnumerator.this.program.type } =
-    RecursiveEvaluator(program)(ctx.toEvaluator, ctx.toSolver)
-
   def enumSimple(vs: Seq[ValDef], satisfying: Expr): FreeableIterator[Map[ValDef, Expr]] = {
     enumVarying0(vs, satisfying, None, -1)
   }
@@ -203,7 +202,11 @@ trait ModelEnumerator {
 }
 
 object ModelEnumerator {
-  def apply(sf: SolverFactory): ModelEnumerator { val factory: sf.type } = new ModelEnumerator {
+  def apply(p: Program)
+           (sf: SolverFactory { val program: p.type }, ev: DeterministicEvaluator { val program: p.type }):
+            ModelEnumerator { val program: p.type; val factory: sf.type; val evaluator: ev.type } = new {
+    val program: p.type = p
     val factory: sf.type = sf
-  }
+    val evaluator: ev.type = ev
+  } with ModelEnumerator
 }
diff --git a/src/main/scala/inox/datagen/SolverDataGen.scala b/src/main/scala/inox/datagen/SolverDataGen.scala
index 072ec731d01af83a584d43da3768f3a97b3bd137..9a530c88bff663ba5ff4fe5086159db301bae540 100644
--- a/src/main/scala/inox/datagen/SolverDataGen.scala
+++ b/src/main/scala/inox/datagen/SolverDataGen.scala
@@ -3,6 +3,7 @@
 package inox
 package datagen
 
+import evaluators._
 import solvers._
 import utils._
 
@@ -12,6 +13,7 @@ trait SolverDataGen extends DataGenerator { self =>
   import program.symbols._
 
   def factory(p: Program { val trees: self.program.trees.type }): SolverFactory { val program: p.type }
+  def evaluator(p: Program { val trees: self.program.trees.type }): DeterministicEvaluator { val program: p.type }
 
   def generate(tpe: Type): FreeableIterator[Expr] = {
     generateFor(Seq(ValDef(FreshIdentifier("tmp"), tpe)),
@@ -76,7 +78,7 @@ trait SolverDataGen extends DataGenerator { self =>
 
       // We need to synthesize a size function for ins' types.
       val pgm1 = program.extend(functions = fds)
-      val modelEnum = ModelEnumerator(factory(pgm1))
+      val modelEnum = ModelEnumerator(pgm1)(factory(pgm1), evaluator(pgm1))
 
       val enum = modelEnum.enumVarying(ins, satisfying, sizeOf, 5)
 
@@ -86,8 +88,9 @@ trait SolverDataGen extends DataGenerator { self =>
 }
 
 object SolverDataGen {
-  def apply(p: Program): SolverDataGen { val program: p.type } = new SolverDataGen {
+  def apply(p: InoxProgram): SolverDataGen { val program: p.type } = new SolverDataGen {
     val program: p.type = p
-    def factory(p2: Program { val trees: p.trees.type }): SolverFactory { val program: p2.type } = SolverFactory(p2)
+    def factory(p: InoxProgram): SolverFactory { val program: p.type } = SolverFactory.default(p)
+    def evaluator(p: InoxProgram): RecursiveEvaluator { val program: p.type } = RecursiveEvaluator.default(p)
   }
 }
diff --git a/src/main/scala/inox/evaluators/Evaluator.scala b/src/main/scala/inox/evaluators/Evaluator.scala
index b9069f65f681a8c094c1bee6d5eeff7dbd92445f..c7f271d2769467e7b9d05852ee57adcfdb734ca2 100644
--- a/src/main/scala/inox/evaluators/Evaluator.scala
+++ b/src/main/scala/inox/evaluators/Evaluator.scala
@@ -3,7 +3,13 @@
 package inox
 package evaluators
 
-case class EvaluatorOptions(options: Seq[InoxOption[Any]]) extends InoxOptions
+case class EvaluatorOptions(options: Seq[InoxOption[Any]]) extends InoxOptions[EvaluatorOptions] {
+  protected def build(opts: Seq[InoxOption[Any]]): EvaluatorOptions = EvaluatorOptions(opts)
+}
+
+object EvaluatorOptions {
+  def empty = EvaluatorOptions(Seq())
+}
 
 trait Evaluator {
   val program: Program
diff --git a/src/main/scala/inox/evaluators/RecursiveEvaluator.scala b/src/main/scala/inox/evaluators/RecursiveEvaluator.scala
index c9f4da8f0328b98e8dca754fd14ec9318ca9cf61..66d4d3e1ed0fa428997b7542fea67529a67f31c5 100644
--- a/src/main/scala/inox/evaluators/RecursiveEvaluator.scala
+++ b/src/main/scala/inox/evaluators/RecursiveEvaluator.scala
@@ -529,15 +529,16 @@ trait RecursiveEvaluator
 }
 
 object RecursiveEvaluator {
-  def apply(p: Program)
-           (evOpts: EvaluatorOptions, solverOpts: solvers.SolverOptions):
-           RecursiveEvaluator { val program: p.type } = {
+  def apply(p: InoxProgram)(evOpts: EvaluatorOptions, solverOpts: solvers.SolverOptions):
+            RecursiveEvaluator { val program: p.type } = {
     new {
       val program: p.type = p
     } with RecursiveEvaluator with HasDefaultGlobalContext with HasDefaultRecContext {
-      val options = evOpts
+      val options = p.ctx.toEvaluator ++ evOpts
       val maxSteps = 50000
-      def getSolver(opts: InoxOption[Any]*) = solvers.SolverFactory(p)
+      def getSolver(opts: InoxOption[Any]*) = solvers.SolverFactory(p)(p.ctx.toSolver ++ opts, options)
     }
   }
+
+  def default(p: InoxProgram) = apply(p)(EvaluatorOptions.empty, solvers.SolverOptions.empty)
 }
diff --git a/src/main/scala/inox/package.scala b/src/main/scala/inox/package.scala
index ed98affade8896e911f05fbab1508dc25fa86e01..a35924d04e4d53721b0450b4464290091d1adcf7 100644
--- a/src/main/scala/inox/package.scala
+++ b/src/main/scala/inox/package.scala
@@ -1,15 +1,56 @@
 /* Copyright 2009-2016 EPFL, Lausanne */
 
-import scala.language.implicitConversions
-
-/** Core package of the Leon system 
-  *
-  * Provides the basic types and definitions for the Leon system.
-  */
+/** Core package of the Inox solving interface */
 package object inox {
   implicit class BooleanToOption(cond: Boolean) {
     def option[A](v: => A) = if (cond) Some(v) else None
   }
 
   case class FatalError(msg: String) extends Exception(msg)
+
+  type InoxProgram = Program { val trees: inox.trees.type }
+
+  object InoxProgram {
+    def apply(ictx: InoxContext,
+      functions: Seq[inox.trees.FunDef],
+      classes: Seq[inox.trees.ClassDef]) = new Program {
+        val trees = inox.trees
+        val ctx = ictx
+        val symbols = new inox.trees.Symbols(
+          functions.map(fd => fd.id -> fd).toMap,
+          classes.map(cd => cd.id -> cd).toMap)
+      }
+  }
+
+  object trees extends ast.Trees {
+
+    class Symbols(
+      val functions: Map[Identifier, FunDef],
+      val classes: Map[Identifier, ClassDef]
+    ) extends AbstractSymbols {
+
+      def transform(t: TreeTransformer) = new Symbols(
+        functions.mapValues(fd => new FunDef(
+          fd.id,
+          fd.tparams, // type parameters can't be transformed!
+          fd.params.map(vd => t.transform(vd)),
+          t.transform(fd.returnType),
+          fd.body.map(t.transform),
+          fd.flags)),
+        classes.mapValues {
+          case acd: AbstractClassDef => acd
+          case ccd: CaseClassDef => new CaseClassDef(
+            ccd.id,
+            ccd.tparams,
+            ccd.parent,
+            ccd.fields.map(t.transform),
+            ccd.flags)
+        })
+
+      def extend(functions: Seq[FunDef] = Seq.empty, classes: Seq[ClassDef] = Seq.empty) = new Symbols(
+        this.functions ++ functions.map(fd => fd.id -> fd),
+        this.classes ++ classes.map(cd => cd.id -> cd)
+      )
+    }
+  }
 }
diff --git a/src/main/scala/inox/solvers/GroundSolver.scala b/src/main/scala/inox/solvers/GroundSolver.scala
deleted file mode 100644
index a50b3f814fac04474cbf0604583f3e4c05cea83c..0000000000000000000000000000000000000000
--- a/src/main/scala/inox/solvers/GroundSolver.scala
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Copyright 2009-2016 EPFL, Lausanne */
-
-package leon
-package solvers
-
-import evaluators.DefaultEvaluator
-import evaluators.EvaluationResults._
-import purescala.Common.Identifier
-import purescala.Definitions.Program
-import purescala.Expressions.{BooleanLiteral, Expr}
-import purescala.ExprOps.isGround
-import purescala.Constructors.andJoin
-import utils.Interruptible
-import utils.IncrementalSeq
-
-// This solver only "solves" ground terms by evaluating them
-class GroundSolver(val sctx: SolverContext, val program: Program) extends Solver with NaiveAssumptionSolver {
-
-  context.interruptManager.registerForInterrupts(this)
-
-  val evaluator = new DefaultEvaluator(context, program)
-  val dbg: Any => Unit = context.reporter.debug(_)
-
-  def name: String = "ground"
-
-  private val assertions = new IncrementalSeq[Expr]()
-
-  // Ground terms will always have the empty model
-  def getModel: Model = Model.empty
-
-  def assertCnstr(expression: Expr): Unit = {
-    assertions += expression
-  }
-
-  def check: Option[Boolean] = {
-    val expr = andJoin(assertions.toSeq)
-
-    if (isGround(expr)) {
-      evaluator.eval(expr) match {
-        case Successful(BooleanLiteral(result)) =>
-          Some(result)
-        case RuntimeError(message) =>
-          dbg(s"Evaluation of $expr resulted in runtime error")
-          Some(false)
-        case _ =>
-          dbg(s"Solver $name encountered error during evaluation.")
-          None
-      }
-    } else {
-      dbg(s"Non-ground expression $expr given to solver $name")
-      None
-    }
-  }
-
-  def free(): Unit = {
-    assertions.clear()
-  }
-
-  def push() = {
-    assertions.push()
-  }
-
-  def pop() = {
-    assertions.pop()
-  }
-
-  def reset() = {
-    assertions.reset()
-  }
-
-  def interrupt(): Unit = {}
-
-  def recoverInterrupt(): Unit = {}
-}
diff --git a/src/main/scala/inox/solvers/NaiveAssumptionSolver.scala b/src/main/scala/inox/solvers/NaiveAssumptionSolver.scala
deleted file mode 100644
index a0dd48a7c80e3b04cd6336f27b60f9f0b0b78296..0000000000000000000000000000000000000000
--- a/src/main/scala/inox/solvers/NaiveAssumptionSolver.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright 2009-2016 EPFL, Lausanne */
-
-package leon
-package solvers
-
-import purescala.Expressions._
-import purescala.Constructors._
-
-trait NaiveAssumptionSolver {
-  self: Solver =>
-
-  var lastBs = Set[Expr]()
-  def checkAssumptions(bs: Set[Expr]): Option[Boolean] = {
-    push()
-    lastBs = bs
-    assertCnstr(andJoin(bs.toSeq))
-    val res = check
-    pop()
-
-    res
-  }
-
-  def getUnsatCore: Set[Expr] = {
-    lastBs
-  }
-}
diff --git a/src/main/scala/inox/solvers/Solver.scala b/src/main/scala/inox/solvers/Solver.scala
index e725331488bd38b29edabe4713b9f6c4aafff803..5b71c18546b5081263549438cd03167b684f5aa2 100644
--- a/src/main/scala/inox/solvers/Solver.scala
+++ b/src/main/scala/inox/solvers/Solver.scala
@@ -6,14 +6,12 @@ package solvers
 import utils._
 import evaluators._
 
-case class SolverOptions(options: Seq[InoxOption[Any]]) extends InoxOptions {
-  def set(opts: Seq[InoxOption[Any]]): SolverOptions = {
-    val changed = opts.map(_.optionDef).toSet
-    val remainingOpts = options.filter { case InoxOption(optDef, _) => !changed(optDef) }
-    copy(options = remainingOpts ++ opts)
-  }
+case class SolverOptions(options: Seq[InoxOption[Any]]) extends InoxOptions[SolverOptions] {
+  def build(opts: Seq[InoxOption[Any]]): SolverOptions = SolverOptions(opts)
+}
 
-  def set(opt: InoxOption[Any], opts: InoxOption[Any]*): SolverOptions = set(opt +: opts)
+object SolverOptions {
+  def empty = SolverOptions(Seq())
 }
 
 case object DebugSectionSolver extends DebugSection("solver")
diff --git a/src/main/scala/inox/solvers/SolverFactory.scala b/src/main/scala/inox/solvers/SolverFactory.scala
index 6ef8140accbb15046c2069d0c8e7f03be12a256c..d1521868348716c30f6943575902a95e666b308a 100644
--- a/src/main/scala/inox/solvers/SolverFactory.scala
+++ b/src/main/scala/inox/solvers/SolverFactory.scala
@@ -33,5 +33,10 @@ object SolverFactory {
     }
   }
 
-  def apply(p: Program): SolverFactory { val program: p.type } = ???
+  def apply(p: InoxProgram)
+           (solverOpts: SolverOptions, evOpts: evaluators.EvaluatorOptions):
+            SolverFactory { val program: p.type } = ???
+
+  def default(p: InoxProgram): SolverFactory { val program: p.type } =
+    apply(p)(SolverOptions.empty, evaluators.EvaluatorOptions.empty)
 }