diff --git a/src/main/scala/inox/solvers/unrolling/FunctionTemplates.scala b/src/main/scala/inox/solvers/unrolling/FunctionTemplates.scala
index eada1e7e803c839e8bd49b6ab9c38c3d8ff0f1ac..5c0b037c03d8eafc92c4dc70d081122996d4675b 100644
--- a/src/main/scala/inox/solvers/unrolling/FunctionTemplates.scala
+++ b/src/main/scala/inox/solvers/unrolling/FunctionTemplates.scala
@@ -23,13 +23,14 @@ trait FunctionTemplates { self: Templates =>
       exprVars: Map[Variable, Encoded],
       condTree: Map[Variable, Set[Variable]],
       guardedExprs: Map[Variable, Seq[Expr]],
+      equations: Seq[Expr],
       lambdas: Seq[LambdaTemplate],
       quantifications: Seq[QuantificationTemplate]
     ) : FunctionTemplate = {
 
       val (clauses, blockers, applications, matchers, templateString) =
-        Template.encode(pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, quantifications,
-          optCall = Some(tfd))
+        Template.encode(pathVar, arguments, condVars, exprVars, guardedExprs, equations,
+          lambdas, quantifications, optCall = Some(tfd))
 
       val funString : () => String = () => {
         "Template for def " + tfd.signature +
@@ -153,6 +154,7 @@ trait FunctionTemplates { self: Templates =>
 
         // We connect it to the defBlocker:   blocker => defBlocker
         if (defBlocker != blocker) {
+          registerImplication(blocker, defBlocker)
           newCls += mkImplies(blocker, defBlocker)
         }
 
diff --git a/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala b/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala
index ff0573a5803639fcf94839c55bca0a871c4ae9f5..0455d0fc8e1eeb1ff080a329584e71e630edadaf 100644
--- a/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala
+++ b/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala
@@ -58,6 +58,7 @@ trait LambdaTemplates { self: Templates =>
       exprVars: Map[Variable, Encoded],
       condTree: Map[Variable, Set[Variable]],
       guardedExprs: Map[Variable, Seq[Expr]],
+      equations: Seq[Expr],
       lambdas: Seq[LambdaTemplate],
       quantifications: Seq[QuantificationTemplate],
       structure: LambdaStructure,
@@ -68,8 +69,8 @@ trait LambdaTemplates { self: Templates =>
       val id = ids._2
       val tpe = ids._1.getType.asInstanceOf[FunctionType]
       val (clauses, blockers, applications, matchers, templateString) =
-        Template.encode(pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, quantifications,
-          substMap = baseSubstMap + ids, optApp = Some(id -> tpe))
+        Template.encode(pathVar, arguments, condVars, exprVars, guardedExprs, equations,
+          lambdas, quantifications, substMap = baseSubstMap + ids, optApp = Some(id -> tpe))
 
       val lambdaString : () => String = () => {
         "Template for lambda " + ids._1 + ": " + lambda + " is :\n" + templateString()
@@ -375,6 +376,7 @@ trait LambdaTemplates { self: Templates =>
           Seq.empty
         } else typeBlockers.get(encoded) match {
           case Some(typeBlocker) =>
+            registerImplication(blocker, typeBlocker)
             Seq(mkImplies(blocker, typeBlocker))
 
           case None =>
@@ -393,7 +395,7 @@ trait LambdaTemplates { self: Templates =>
 
             val extClause = mkEquals(firstB, mkAnd(blocker, mkNot(mkOr(boundClauses.toSeq :+ nextB : _*))))
 
-            registerParent(typeBlocker, firstB)
+            registerImplication(firstB, typeBlocker)
             val symClauses = registerSymbol(typeBlocker, encoded, to)
 
             symClauses :+ extClause :+ mkImplies(firstB, typeBlocker)
@@ -520,6 +522,7 @@ trait LambdaTemplates { self: Templates =>
         }
 
         val enabler = if (equals == trueT) b else mkAnd(equals, b)
+        registerImplication(b, lambdaBlocker)
         newCls += mkImplies(enabler, lambdaBlocker)
 
         ctx.reporter.debug("Unrolling behind "+info+" ("+newCls.size+")")
diff --git a/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala b/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala
index a6cc136245cf040595c7ecaec2bc36370c8c2bab..5c1d651e81bdc829948cb3e38763e3889f6bff43 100644
--- a/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala
+++ b/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala
@@ -76,11 +76,19 @@ trait QuantificationTemplates { self: Templates =>
 
   case class Positive(guardVar: Encoded) extends Polarity
   case class Negative(insts: (Variable, Encoded)) extends Polarity
-  case class Unknown(
-    qs: (Variable, Encoded),
-    q2s: (Variable, Encoded),
-    insts: (Variable, Encoded),
-    guardVar: Encoded) extends Polarity
+
+  /** Unknown quantification polarity.
+    *
+    * Instantiations of unknown polarity quantification with body {{{p}}} follows the schema:
+    * {{{
+    *    b ==> (q == (q2 && inst)
+    *    b ==> (inst == (guard ==> p))
+    * }}}
+    * It is useful to keep the two clauses separate so that satisfying assignments can be
+    * constructed where only certain `inst` variables are falsified. This is used to enable
+    * a powerful unrolling heuristic in the presence of both quantifiers and recursive functions.
+    */
+  case class Unknown(qs: (Variable, Encoded), q2s: (Variable, Encoded), insts: (Variable, Encoded), guardVar: Encoded) extends Polarity
 
   class QuantificationTemplate private[QuantificationTemplates] (
     val pathVar: (Variable, Encoded),
@@ -138,52 +146,62 @@ trait QuantificationTemplates { self: Templates =>
       exprVars: Map[Variable, Encoded],
       condTree: Map[Variable, Set[Variable]],
       guardedExprs: Map[Variable, Seq[Expr]],
+      equations: Seq[Expr],
       lambdas: Seq[LambdaTemplate],
       quantifications: Seq[QuantificationTemplate],
-      baseSubstMap: Map[Variable, Encoded],
+      substMap: Map[Variable, Encoded],
       proposition: Forall
     ): (Option[Variable], QuantificationTemplate) = {
 
-      val (optVar, polarity, extraGuarded, extraSubst) = optPol match {
+      val pT = mkEncoder(
+        condVars ++ exprVars + pathVar ++ quantifiers ++
+        substMap ++ lambdas.map(_.ids) ++ quantifications.flatMap(_.mapping)
+      )(p)
+
+      val (optVar, polarity, extraClauses, extraSubst) = optPol match {
         case Some(true) =>
-          val guard: Variable = Variable(FreshIdentifier("guard", true), BooleanType)
-          val guards = guard -> encodeSymbol(guard)
-          (None, Positive(guards._2), Map(pathVar._1 -> Seq(Implies(guard, p))), Map(guards))
+          val guard = encodeSymbol(Variable(FreshIdentifier("guard", true), BooleanType))
+          val extraClause = mkImplies(mkAnd(pathVar._2, guard), pT)
+          (None, Positive(guard), Seq(extraClause), Map(pathVar._1 -> guard))
 
         case Some(false) =>
           val inst: Variable = Variable(FreshIdentifier("inst", true), BooleanType)
           val insts = inst -> encodeSymbol(inst)
-          (Some(inst), Negative(insts), Map(pathVar._1 -> Seq(Equals(inst, p))), Map(insts))
+          val extraClause = mkImplies(pathVar._2, mkEquals(insts._2, pT))
+          (Some(inst), Negative(insts), Seq(extraClause), Map.empty)
 
         case None =>
           val q: Variable = Variable(FreshIdentifier("q", true), BooleanType)
           val q2: Variable = Variable(FreshIdentifier("qo", true), BooleanType)
           val inst: Variable = Variable(FreshIdentifier("inst", true), BooleanType)
-          val guard: Variable = Variable(FreshIdentifier("guard", true), BooleanType)
+          val guard = encodeSymbol(Variable(FreshIdentifier("guard", true), BooleanType))
 
           val qs = q -> encodeSymbol(q)
           val q2s = q2 -> encodeSymbol(q2)
           val insts = inst -> encodeSymbol(inst)
-          val guards = guard -> encodeSymbol(guard)
 
-          val polarity = Unknown(qs, q2s, insts, guards._2)
-          val extraGuarded = Map(pathVar._1 -> Seq(Equals(inst, Implies(guard, p)), Equals(q, And(q2, inst))))
-          val extraSubst = Map(qs, q2s, insts, guards)
-          (Some(q), polarity, extraGuarded, extraSubst)
+          val polarity = Unknown(qs, q2s, insts, guard)
+          val extraClauses = Seq(
+            mkImplies(pathVar._2, mkEquals(qs._2, mkAnd(q2s._2, insts._2))),
+            mkImplies(pathVar._2, mkEquals(insts._2, mkImplies(guard, pT)))
+          )
+          (Some(q), polarity, extraClauses, Map(pathVar._1 -> guard))
       }
 
-      val substMap = baseSubstMap ++ extraSubst
-      val allGuarded = guardedExprs merge extraGuarded
-
+      // Note @nv: some hacky shit is going on here...
+      // We are overriding the mapping of `pathVar._1` for certain polarities so that
+      // the encoded clauses use the `guard` as blocker instead of `pathVar._2`. This only
+      // works due to [[Template.encode]] injecting `pathVar` BEFORE `substMap` into the
+      // global encoding substitution.
       val (clauses, blockers, applications, matchers, templateString) =
-        Template.encode(pathVar, quantifiers, condVars, exprVars, allGuarded,
-          lambdas, quantifications, substMap = substMap)
+        Template.encode(pathVar, quantifiers, condVars, exprVars, guardedExprs,
+          equations, lambdas, quantifications, substMap = substMap ++ extraSubst)
 
       val key = templateKey(proposition.args, proposition.body, substMap)
 
       (optVar, new QuantificationTemplate(
         pathVar, polarity, quantifiers, condVars, exprVars, condTree,
-        clauses, blockers, applications, matchers, lambdas, quantifications, key,
+        extraClauses ++ clauses, blockers, applications, matchers, lambdas, quantifications, key,
         proposition.body, () => "Template for " + proposition + " is :\n" + templateString()))
     }
   }
@@ -203,8 +221,8 @@ trait QuantificationTemplates { self: Templates =>
     val lambdaAxioms    = new IncrementalSet[(LambdaStructure, Seq[(Variable, Encoded)])]
     val templates       = new IncrementalMap[(Seq[ValDef], Expr, Seq[Encoded]), Map[Encoded, Encoded]]
 
-    val incrementals: Seq[IncrementalState] = Seq(
-      quantifications, ignoredMatchers, handledMatchers, ignoredSubsts, handledSubsts, lambdaAxioms, templates)
+    val incrementals: Seq[IncrementalState] = Seq(quantifications, lambdaAxioms, templates,
+      ignoredMatchers, handledMatchers, ignoredSubsts, handledSubsts, ignoredGrounds)
 
     override def push(): Unit  = { super.push();  for (q <- quantifications) q.push()  }
     override def pop(): Unit   = { super.pop();   for (q <- quantifications) q.pop()   }
@@ -228,24 +246,35 @@ trait QuantificationTemplates { self: Templates =>
     def promoteBlocker(b: Encoded): Boolean = false
 
     def unroll: Clauses = {
-      val clauses = new scala.collection.mutable.ListBuffer[Encoded]
-
+      val imClauses = new scala.collection.mutable.ListBuffer[Encoded]
       for (e @ (gen, bs, m) <- ignoredMatchers.toSeq if gen <= currentGeneration) {
-        clauses ++= instantiateMatcher(bs, m)
+        imClauses ++= instantiateMatcher(bs, m)
         ignoredMatchers -= e
       }
 
+      ctx.reporter.debug("Unrolling ignored matchers (" + imClauses.size + ")")
+      for (cl <- imClauses) {
+        ctx.reporter.debug("  . " + cl)
+      }
+
+      val suClauses = new scala.collection.mutable.ListBuffer[Encoded]
       for (q <- quantifications.toSeq if ignoredSubsts.isDefinedAt(q)) {
         val (release, keep) = ignoredSubsts(q).partition(_._1 <= currentGeneration)
         ignoredSubsts += q -> keep
 
         for ((_, bs, subst) <- release) {
-          clauses ++= q.instantiateSubst(bs, subst)
+          suClauses ++= q.instantiateSubst(bs, subst)
         }
       }
 
+      ctx.reporter.debug("Unrolling ignored substitutions (" + suClauses.size + ")")
+      for (cl <- suClauses) {
+        ctx.reporter.debug("  . " + cl)
+      }
+
+      val grClauses = new scala.collection.mutable.ListBuffer[Encoded]
       for ((gen, qs) <- ignoredGrounds.toSeq if gen <= currentGeneration; q <- qs) {
-        clauses ++= q.ensureGrounds
+        grClauses ++= q.ensureGrounds
         val remaining = ignoredGrounds.getOrElse(gen, Set.empty) - q
         if (remaining.nonEmpty) {
           ignoredGrounds += gen -> remaining
@@ -254,20 +283,28 @@ trait QuantificationTemplates { self: Templates =>
         }
       }
 
-      clauses.toSeq
+      ctx.reporter.debug("Unrolling ignored grounds (" + grClauses.size + ")")
+      for (cl <- grClauses) {
+        ctx.reporter.debug("  . " + cl)
+      }
+
+      imClauses.toSeq ++ suClauses ++ grClauses
     }
   }
 
-  def instantiateMatcher(blocker: Encoded, matcher: Matcher): Clauses =
+  def instantiateMatcher(blocker: Encoded, matcher: Matcher): Clauses = {
     instantiateMatcher(Set(blocker), matcher)
+  }
 
   @inline
   private def instantiateMatcher(blockers: Set[Encoded], matcher: Matcher): Clauses = {
-    if (handledMatchers(blockers -> matcher)) {
+    val relevantBlockers = blockerPath(blockers)
+
+    if (handledMatchers(relevantBlockers -> matcher)) {
       Seq.empty
     } else {
-      handledMatchers += blockers -> matcher
-      quantifications.flatMap(_.instantiate(blockers, matcher))
+      handledMatchers += relevantBlockers -> matcher
+      quantifications.flatMap(_.instantiate(relevantBlockers, matcher))
     }
   }
 
@@ -447,6 +484,7 @@ trait QuantificationTemplates { self: Templates =>
 
     lazy val quantified: Set[Encoded] = quantifiers.map(_._2).toSet
     lazy val start = pathVar._2
+    lazy val pathBlockers = blockerPath(start)
 
     private val constraints: Seq[(Encoded, MatcherKey, Int)] = (for {
       (_, ms) <- matchers
@@ -509,6 +547,8 @@ trait QuantificationTemplates { self: Templates =>
       instantiateSubsts(mappings)
     }
 
+    def hasAllGrounds: Boolean = quantified.forall(q => grounds(q).nonEmpty)
+
     def ensureGrounds: Clauses = {
       /* Build mappings from quantifiers to all potential ground values previously encountered
        * AND the constants we're introducing to make sure grounds are non-empty. */
@@ -561,7 +601,7 @@ trait QuantificationTemplates { self: Templates =>
       handledSubsts += this -> (handledSubsts.getOrElse(this, Set.empty) + (bs -> subst))
       val instantiation = new scala.collection.mutable.ListBuffer[Encoded]
 
-      val (enabler, enablerClauses) = encodeBlockers(bs)
+      val (enabler, enablerClauses) = encodeBlockers(bs ++ pathBlockers)
       instantiation ++= enablerClauses
 
       val baseSubst = subst ++ instanceSubst(enabler).mapValues(Left(_))
@@ -828,8 +868,12 @@ trait QuantificationTemplates { self: Templates =>
               clauses ++= quantification.instantiate(bs, m)
             }
 
+            val freshSubst = mkSubstituter(template.quantifiers.map(p => p._2 -> encodeSymbol(p._1)).toMap)
             for ((b,ms) <- template.matchers; m <- ms) {
-              clauses ++= instantiateMatcher(b, m)
+              clauses ++= instantiateMatcher(Set.empty[Encoded], m)
+              // it is very rare that such instantiations are actually required, so we defer them
+              val gen = currentGeneration + 20
+              ignoredMatchers += ((gen, Set(b), m.substitute(freshSubst, Map.empty)))
             }
 
             clauses ++= quantification.ensureGrounds
@@ -847,6 +891,13 @@ trait QuantificationTemplates { self: Templates =>
       throw FatalError("Attempting to promote inexistent quantifiers")
 
     val diff = (currentGeneration - optGen.get) max 0
+
+    val currentGrounds = ignoredGrounds.toSeq
+    ignoredGrounds.clear()
+    for ((gen, qs) <- currentGrounds) {
+      ignoredGrounds += (gen - diff) -> qs
+    }
+
     val currentMatchers = ignoredMatchers.toSeq
     ignoredMatchers.clear()
     for ((gen, bs, m) <- currentMatchers) {
@@ -865,6 +916,13 @@ trait QuantificationTemplates { self: Templates =>
     val clauses = new scala.collection.mutable.ListBuffer[Encoded]
     val keyClause = MutableMap.empty[MatcherKey, (Clauses, Encoded)]
 
+    val currentGrounds = ignoredGrounds.toSeq
+    for ((gen, qs) <- currentGrounds if qs.exists(!_.hasAllGrounds)) {
+      ignoredGrounds -= gen
+      ignoredGrounds += currentGeneration -> qs
+      clauses += falseT
+    }
+
     for ((_, bs, m) <- ignoredMatchers) {
       val key = matcherKey(m)
       val argTypes = key match {
diff --git a/src/main/scala/inox/solvers/unrolling/TemplateGenerator.scala b/src/main/scala/inox/solvers/unrolling/TemplateGenerator.scala
index 83acb6e041c67eca5149a91c0224b66372728b56..9d4f63a0773a95bd805ae241addbcb79b4d389cd 100644
--- a/src/main/scala/inox/solvers/unrolling/TemplateGenerator.scala
+++ b/src/main/scala/inox/solvers/unrolling/TemplateGenerator.scala
@@ -17,19 +17,22 @@ trait TemplateGenerator { self: Templates =>
     Map[Variable, Encoded],
     Map[Variable, Set[Variable]],
     Map[Variable, Seq[Expr]],
+    Seq[Expr],
     Seq[LambdaTemplate],
     Seq[QuantificationTemplate]
   )
 
-  private def emptyClauses: TemplateClauses = (Map.empty, Map.empty, Map.empty, Map.empty, Seq.empty, Seq.empty)
+  private def emptyClauses: TemplateClauses =
+    (Map.empty, Map.empty, Map.empty, Map.empty, Seq.empty, Seq.empty, Seq.empty)
 
   private implicit class ClausesWrapper(clauses: TemplateClauses) {
     def ++(that: TemplateClauses): TemplateClauses = {
-      val (thisConds, thisExprs, thisTree, thisGuarded, thisLambdas, thisQuants) = clauses
-      val (thatConds, thatExprs, thatTree, thatGuarded, thatLambdas, thatQuants) = that
+      val (thisConds, thisExprs, thisTree, thisGuarded, thisEqs, thisLambdas, thisQuants) = clauses
+      val (thatConds, thatExprs, thatTree, thatGuarded, thatEqs, thatLambdas, thatQuants) = that
 
-      (thisConds ++ thatConds, thisExprs ++ thatExprs, thisTree merge thatTree,
-        thisGuarded merge thatGuarded, thisLambdas ++ thatLambdas, thisQuants ++ thatQuants)
+      (thisConds ++ thatConds, thisExprs ++ thatExprs,
+        thisTree merge thatTree, thisGuarded merge thatGuarded,
+        thisEqs ++ thatEqs, thisLambdas ++ thatLambdas, thisQuants ++ thatQuants)
     }
   }
 
@@ -57,11 +60,11 @@ trait TemplateGenerator { self: Templates =>
 
     val substMap : Map[Variable, Encoded] = arguments.toMap + pathVar
 
-    val (condVars, exprVars, condTree, guardedExprs, lambdas, quantifications) =
+    val (condVars, exprVars, condTree, guardedExprs, eqs, lambdas, quantifications) =
       invocationEqualsBody.foldLeft(emptyClauses)((clsSet, cls) => clsSet ++ mkClauses(start, cls, substMap))
 
     val template = FunctionTemplate(tfd, pathVar, arguments,
-      condVars, exprVars, condTree, guardedExprs, lambdas, quantifications)
+      condVars, exprVars, condTree, guardedExprs, eqs, lambdas, quantifications)
     cache += tfd -> template
     template
   }
@@ -88,12 +91,13 @@ trait TemplateGenerator { self: Templates =>
   }
 
   def mkClauses(pathVar: Variable, expr: Expr, substMap: Map[Variable, Encoded], polarity: Option[Boolean] = None): TemplateClauses = {
-    val (p, (condVars, exprVars, condTree, guardedExprs, lambdas, quantifications)) = mkExprClauses(pathVar, expr, substMap, polarity)
+    val (p, (condVars, exprVars, condTree, guardedExprs, eqs, lambdas, quantifications)) = mkExprClauses(pathVar, expr, substMap, polarity)
     val allGuarded = guardedExprs + (pathVar -> (p +: guardedExprs.getOrElse(pathVar, Seq.empty)))
-    (condVars, exprVars, condTree, allGuarded, lambdas, quantifications)
+    (condVars, exprVars, condTree, allGuarded, eqs, lambdas, quantifications)
   }
 
   private def mkExprClauses(pathVar: Variable, expr: Expr, substMap: Map[Variable, Encoded], polarity: Option[Boolean] = None): (Expr, TemplateClauses) = {
+    val initVar = pathVar
 
     var condVars = Map[Variable, Encoded]()
     var condTree = Map[Variable, Set[Variable]](pathVar -> Set.empty).withDefaultValue(Set.empty)
@@ -117,7 +121,9 @@ trait TemplateGenerator { self: Templates =>
       guardedExprs += guardVar -> (expr +: prev)
     }
 
-    @inline def iff(e1: Expr, e2: Expr): Unit = storeGuarded(pathVar, Equals(e1, e2))
+    // Represents equations (simple formulas)
+    var eqs = Seq[Expr]()
+    @inline def iff(e1: Expr, e2: Expr): Unit = eqs :+= Equals(e1, e2)
 
     var lambdaVars = Map[Variable, Encoded]()
     @inline def storeLambda(id: Variable): Encoded = {
@@ -275,7 +281,7 @@ trait TemplateGenerator { self: Templates =>
 
         val localSubst: Map[Variable, Encoded] = substMap ++ condVars ++ exprVars ++ lambdaVars
         val clauseSubst: Map[Variable, Encoded] = localSubst ++ (idArgs zip trArgs)
-        val (lambdaConds, lambdaExprs, lambdaTree, lambdaGuarded, lambdaTemplates, lambdaQuants) =
+        val (lambdaConds, lambdaExprs, lambdaTree, lambdaGuarded, lambdaEqs, lambdaTemplates, lambdaQuants) =
           clauses.foldLeft(emptyClauses)((clsSet, cls) => clsSet ++ mkClauses(pathVar, cls, clauseSubst))
 
         val ids: (Variable, Encoded) = lid -> storeLambda(lid)
@@ -283,12 +289,12 @@ trait TemplateGenerator { self: Templates =>
         val (struct, deps) = normalizeStructure(l)
         val sortedDeps = deps.toSeq.sortBy(_._1.id.uniqueName)
 
-        val (dependencies, (depConds, depExprs, depTree, depGuarded, depLambdas, depQuants)) =
+        val (dependencies, (depConds, depExprs, depTree, depGuarded, depEqs, depLambdas, depQuants)) =
           sortedDeps.foldLeft[(Seq[Encoded], TemplateClauses)](Seq.empty -> emptyClauses) {
             case ((dependencies, clsSet), (id, expr)) =>
               if (!exprOps.isSimple(expr)) {
                 val encoded = encodeSymbol(id)
-                val (e, cls @ (_, _, _, _, lmbds, quants)) = mkExprClauses(pathVar, expr, localSubst)
+                val (e, cls @ (_, _, _, _, _, lmbds, quants)) = mkExprClauses(pathVar, expr, localSubst)
                 val clauseSubst = localSubst ++ lmbds.map(_.ids) ++ quants.flatMap(_.mapping)
                 (dependencies :+ mkEncoder(clauseSubst)(e), clsSet ++ cls)
               } else {
@@ -298,7 +304,7 @@ trait TemplateGenerator { self: Templates =>
 
         val (depClauses, depCalls, depApps, depMatchers, _) = Template.encode(
           pathVar -> encodedCond(pathVar), Seq.empty,
-          depConds, depExprs, depGuarded, depLambdas, depQuants, localSubst)
+          depConds, depExprs, depGuarded, depEqs, depLambdas, depQuants, localSubst)
 
         val depClosures: Seq[Encoded] = {
           val vars = exprOps.variablesOf(l)
@@ -313,7 +319,7 @@ trait TemplateGenerator { self: Templates =>
 
         val template = LambdaTemplate(ids, pathVar -> encodedCond(pathVar),
           idArgs zip trArgs, lambdaConds, lambdaExprs, lambdaTree,
-          lambdaGuarded, lambdaTemplates, lambdaQuants, structure, localSubst, l)
+          lambdaGuarded, lambdaEqs, lambdaTemplates, lambdaQuants, structure, localSubst, l)
         registerLambda(template)
         lid
 
@@ -329,10 +335,10 @@ trait TemplateGenerator { self: Templates =>
 
           val localSubst: Map[Variable, Encoded] = substMap ++ condVars ++ exprVars ++ lambdaVars
           val clauseSubst: Map[Variable, Encoded] = localSubst ++ (idQuantifiers zip trQuantifiers)
-          val (p, (qConds, qExprs, qTree, qGuarded, qLambdas, qQuants)) = mkExprClauses(pathVar, conjunct, clauseSubst)
+          val (p, (qConds, qExprs, qTree, qGuarded, qEqs, qLambdas, qQuants)) = mkExprClauses(pathVar, conjunct, clauseSubst)
 
           val (optVar, template) = QuantificationTemplate(pathVar -> encodedCond(pathVar),
-            pol, p, idQuantifiers zip trQuantifiers, qConds, qExprs, qTree, qGuarded, qLambdas, qQuants,
+            pol, p, idQuantifiers zip trQuantifiers, qConds, qExprs, qTree, qGuarded, qEqs, qLambdas, qQuants,
             localSubst, Forall(quantifiers.toSeq.sortBy(_.id.uniqueName).map(_.toVal), conjunct))
           registerQuantification(template)
           optVar.getOrElse(BooleanLiteral(true))
@@ -344,7 +350,7 @@ trait TemplateGenerator { self: Templates =>
     }
 
     val p = rec(pathVar, expr, polarity)
-    (p, (condVars, exprVars, condTree, guardedExprs, lambdas, quantifications))
+    (p, (condVars, exprVars, condTree, guardedExprs, eqs, lambdas, quantifications))
   }
 
 }
diff --git a/src/main/scala/inox/solvers/unrolling/Templates.scala b/src/main/scala/inox/solvers/unrolling/Templates.scala
index bdd3a9b9777e047956425f38b3099af738e6136d..5b7466c51db5d686298a8c45d86c742ca3a5b2d4 100644
--- a/src/main/scala/inox/solvers/unrolling/Templates.scala
+++ b/src/main/scala/inox/solvers/unrolling/Templates.scala
@@ -35,10 +35,11 @@ trait Templates extends TemplateGenerator
   def mkImplies(l: Encoded, r: Encoded): Encoded
 
   private[unrolling] lazy val trueT = mkEncoder(Map.empty)(BooleanLiteral(true))
+  private[unrolling] lazy val falseT = mkEncoder(Map.empty)(BooleanLiteral(false))
 
   private var currentGen: Int = 0
   protected def currentGeneration: Int = currentGen
-  protected def nextGeneration(gen: Int): Int = gen + 5
+  protected def nextGeneration(gen: Int): Int = gen + 3
 
   trait Manager extends IncrementalStateWrapper {
     def unrollGeneration: Option[Int]
@@ -59,22 +60,29 @@ trait Templates extends TemplateGenerator
   def canUnroll: Boolean = managers.exists(_.unrollGeneration.isDefined)
   def unroll: Clauses = {
     assert(canUnroll, "Impossible to unroll further")
-    val generation = managers.flatMap(_.unrollGeneration).min + 1
-    if (generation > currentGen) {
-      currentGen = generation
-    }
-
+    currentGen = managers.flatMap(_.unrollGeneration).min + 1
+    ctx.reporter.debug("Unrolling generation [" + currentGen + "]")
     managers.flatMap(_.unroll)
   }
 
   def satisfactionAssumptions = managers.flatMap(_.satisfactionAssumptions)
   def refutationAssumptions = managers.flatMap(_.refutationAssumptions)
 
+  // implication tree that we're sure about: if  (b1, b2) is in the tree, then
+  // we have the precise semantics of b1 ==> b2 in the resulting clause set
   private val condImplies = new IncrementalMap[Encoded, Set[Encoded]].withDefaultValue(Set.empty)
   private val condImplied = new IncrementalMap[Encoded, Set[Encoded]].withDefaultValue(Set.empty)
+
+  // implication tree that isn't quite ensured in the resulting clause set
+  // this can happen due to defBlocker caching in unrolling
+  private val potImplies = new IncrementalMap[Encoded, Set[Encoded]].withDefaultValue(Set.empty)
+  private val potImplied = new IncrementalMap[Encoded, Set[Encoded]].withDefaultValue(Set.empty)
+
   private val condEquals  = new IncrementalBijection[Encoded, Set[Encoded]]
 
-  val incrementals: Seq[IncrementalState] = managers ++ Seq(condImplies, condImplied, condEquals)
+  val incrementals: Seq[IncrementalState] = managers ++ Seq(
+    condImplies, condImplied, potImplies, potImplied, condEquals
+  )
 
   protected def freshConds(
     pathVar: Encoded,
@@ -89,7 +97,8 @@ trait Templates extends TemplateGenerator
         case None => // enabling condition, corresponds to pathVar
           for (child <- children) {
             val ec = mapping(child)
-            condImplied += ec -> (condImplies(ec) + pathVar)
+            condImplies += pathVar -> (condImplies(pathVar) + ec)
+            condImplied += ec -> (condImplied(ec) + pathVar)
           }
 
         case Some(ep) =>
@@ -106,8 +115,11 @@ trait Templates extends TemplateGenerator
 
   private val sym = Variable(FreshIdentifier("bs", true), BooleanType)
   protected def encodeBlockers(bs: Set[Encoded]): (Encoded, Clauses) = bs.toSeq match {
-    case Seq(b) if condImplies.isDefinedAt(b) || condImplied.isDefinedAt(b) || condEquals.containsA(b) =>
-      (b, Seq.empty)
+    case Seq(b) if (
+      condImplies.isDefinedAt(b) || condImplied.isDefinedAt(b) ||
+      potImplies.isDefinedAt(b) || potImplied.isDefinedAt(b) ||
+      condEquals.containsA(b)
+    ) => (b, Seq.empty)
 
     case _ =>
       val flatBs = fixpoint((bs: Set[Encoded]) => bs.flatMap(b => condEquals.getBorElse(b, Set(b))))(bs)
@@ -120,12 +132,30 @@ trait Templates extends TemplateGenerator
       }
   }
 
-  protected def registerParent(child: Encoded, parent: Encoded): Unit = {
-    condImplied += child -> (condImplied(child) + parent)
+  protected def registerImplication(b1: Encoded, b2: Encoded): Unit = {
+    potImplies += b1 -> (potImplies(b1) + b2)
+    potImplied += b2 -> (potImplied(b2) + b1)
+  }
+
+  protected def blockerEquals(b: Encoded): Set[Encoded] = condEquals.getBorElse(b, Set.empty)
+
+  protected def blockerParents(b: Encoded, strict: Boolean = true): Set[Encoded] = {
+    condImplied(b) ++ (if (!strict) potImplied(b) else Set.empty)
+  }
+
+  protected def blockerChildren(b: Encoded, strict: Boolean = true): Set[Encoded] = {
+    condImplies(b) ++ (if (!strict) potImplies(b) else Set.empty)
   }
 
-  protected def blockerParents(b: Encoded): Set[Encoded] = condImplied(b)
-  protected def blockerChildren(b: Encoded): Set[Encoded] = condImplies(b)
+  protected def blockerPath(b: Encoded): Set[Encoded] = blockerPath(Set(b))
+
+  /* This set is guaranteed finite and won't expand beyond the limit of a function's
+   * definition as aVar ==> defBlocker is NOT a strict implication (ie. won't be in
+   * the condImplied map) */
+  protected def blockerPath(bs: Set[Encoded]): Set[Encoded] = fixpoint((bs: Set[Encoded]) => bs.flatMap { b =>
+    val equal = condEquals.getBorElse(b, Set.empty)
+    if (equal.nonEmpty) equal else (condImplied(b) + b)
+  })(bs)
 
   def promoteBlocker(b: Encoded, force: Boolean = false): Boolean = {
     var seen: Set[Encoded] = Set.empty
@@ -145,7 +175,7 @@ trait Templates extends TemplateGenerator
         }
 
         if (force) {
-          blockerChildren(b)
+          blockerChildren(b, strict = false)
         } else {
           Seq.empty[Encoded]
         }
@@ -309,6 +339,7 @@ trait Templates extends TemplateGenerator
       condVars: Map[Variable, Encoded],
       exprVars: Map[Variable, Encoded],
       guardedExprs: Map[Variable, Seq[Expr]],
+      equations: Seq[Expr],
       lambdas: Seq[LambdaTemplate],
       quantifications: Seq[QuantificationTemplate],
       substMap: Map[Variable, Encoded] = Map.empty[Variable, Encoded],
@@ -395,6 +426,8 @@ trait Templates extends TemplateGenerator
           if (matchs.nonEmpty) matchers += b -> matchs
         }
 
+        clauses ++= equations.map(encoder)
+
         (clauses, blockers, applications, matchers merge optIdMatcher.map(m => pathVar._1 -> Set(m)).toMap)
       }
 
@@ -523,11 +556,11 @@ trait Templates extends TemplateGenerator
     val tpeClauses = bindings.flatMap { case (v, s) => registerSymbol(encodedStart, s, v.getType) }.toSeq
 
     val instExpr = simplifyHOFunctions(simplifyQuantifications(expr))
-    val (condVars, exprVars, condTree, guardedExprs, lambdas, quants) =
+    val (condVars, exprVars, condTree, guardedExprs, eqs, lambdas, quants) =
       mkClauses(start, instExpr, bindings + (start -> encodedStart), polarity = Some(true))
 
     val (clauses, calls, apps, matchers, _) = Template.encode(
-      start -> encodedStart, bindings.toSeq, condVars, exprVars, guardedExprs, lambdas, quants)
+      start -> encodedStart, bindings.toSeq, condVars, exprVars, guardedExprs, eqs, lambdas, quants)
 
     val (substMap, substClauses) = Template.substitution(
       condVars, exprVars, condTree, lambdas, quants, Map.empty, start, encodedStart)
diff --git a/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala b/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala
index e49d71bac411e1d9a3d45be09a51a785cc9c15c3..28081283e79cb74b783e1bb253a16b4d6ddda724 100644
--- a/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala
+++ b/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala
@@ -544,8 +544,11 @@ trait AbstractUnrollingSolver extends Solver { self =>
           }
 
           val timer = ctx.timers.solvers.check.start()
+
+          // we always ask for a model here in order to give priority to blockers that
+          // are keeping quantified clause instantiations from being considered
           val res: SolverResponse[underlying.Model, Set[underlying.Trees]] =
-            underlying.checkAssumptions(config max Configuration(model = feelingLucky))(
+            underlying.checkAssumptions(config max Configuration(model = true))(
               encodedAssumptions.toSet ++ templates.refutationAssumptions
             )
           timer.stop()
@@ -559,8 +562,8 @@ trait AbstractUnrollingSolver extends Solver { self =>
             case _: Unsatisfiable =>
               CheckResult.cast(res)
 
-            case SatWithModel(model) if feelingLucky =>
-              if (validateModel(extractSimpleModel(model), assumptionsSeq, silenceErrors = true)) {
+            case SatWithModel(model) =>
+              if (feelingLucky && validateModel(extractSimpleModel(model), assumptionsSeq, silenceErrors = true)) {
                 CheckResult.cast(res)
               } else {
                 val wrapped = wrapModel(model)
@@ -572,9 +575,6 @@ trait AbstractUnrollingSolver extends Solver { self =>
 
                 Unroll
               }
-
-            case _ =>
-              Unroll
           }
 
         case Unroll =>
@@ -582,7 +582,7 @@ trait AbstractUnrollingSolver extends Solver { self =>
 
           val timer = ctx.timers.solvers.unroll.start()
           // unfolling `unrollFactor` times
-          for (i <- 1 to unrollFactor.toInt) {
+          for (i <- 1 to unrollFactor.toInt if templates.canUnroll) {
             val newClauses = templates.unroll
             for (ncl <- newClauses) {
               underlying.assertCnstr(ncl)