From bff35e416406569072d84d53666a65c73d10bacd Mon Sep 17 00:00:00 2001
From: Nicolas Voirol <voirol.nicolas@gmail.com>
Date: Fri, 24 Jul 2015 17:43:08 +0200
Subject: [PATCH] Finished transitivity of propositions

---
 .../codegen/runtime/SolverEntryPoint.scala    | 149 ++++++++
 .../leon/frontends/scalac/ASTExtractors.scala |   1 -
 .../frontends/scalac/CodeExtraction.scala     |   3 +
 .../scala/leon/purescala/Expressions.scala    |   6 +-
 .../leon/purescala/FunctionClosure.scala      |   4 +-
 .../templates/QuantificationManager.scala     | 355 +++++++++++-------
 .../solvers/templates/TemplateGenerator.scala |  21 +-
 .../leon/solvers/templates/Templates.scala    | 135 ++++---
 .../solvers/templates/UnrollingBank.scala     |   4 +-
 .../leon/solvers/z3/AbstractZ3Solver.scala    |   2 +-
 .../solvers/z3/Z3ModelReconstruction.scala    |   2 +
 11 files changed, 480 insertions(+), 202 deletions(-)
 create mode 100644 src/main/scala/leon/codegen/runtime/SolverEntryPoint.scala

diff --git a/src/main/scala/leon/codegen/runtime/SolverEntryPoint.scala b/src/main/scala/leon/codegen/runtime/SolverEntryPoint.scala
new file mode 100644
index 000000000..e36f8aef0
--- /dev/null
+++ b/src/main/scala/leon/codegen/runtime/SolverEntryPoint.scala
@@ -0,0 +1,149 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package codegen.runtime
+
+import utils._
+import purescala.Common._
+import purescala.Expressions._
+import purescala.ExprOps.valuateWithModel
+import purescala.Constructors._
+import solvers.SolverFactory
+
+import java.util.WeakHashMap
+import java.lang.ref.WeakReference
+import scala.collection.mutable.{HashMap => MutableMap}
+import scala.concurrent.duration._
+
+import codegen.CompilationUnit
+
+import synthesis._
+
+object SolverEntryPoint {
+  implicit val debugSection = DebugSectionSolver
+
+  private case class ExprId(id: Int)
+  private case class SolverProblem(args: Seq[Identifier], problem: Expr)
+
+  private[this] val context = new WeakHashMap[ExprId, (WeakReference[CompilationUnit], SolverProblem)]()
+  private[this] val cache   = new WeakHashMap[ExprId, MutableMap[Seq[AnyRef], Option[java.lang.Object]]]()
+
+  private[this] val ids = new WeakHashMap[CompilationUnit, MutableMap[SolverProblem, ExprId]]()
+
+  private[this] var _next = 0
+  private[this] def nextInt(): Int = {
+    _next += 1
+    _next
+  }
+
+  private def getUniqueId(unit: CompilationUnit, p: SolverProblem): ExprId = {
+    if (!ids.containsKey(unit)) {
+      ids.put(unit, new MutableMap())
+    }
+
+    if (ids.get(unit) contains p) {
+      ids.get(unit)(p)
+    } else {
+      val cid = new ExprId(nextInt())
+      ids.get(unit) += p -> cid
+      cid
+    }
+  }
+
+  def register(args: Seq[Identifier], expr: Expr, unit: CompilationUnit): Int = {
+    val p = SolverProblem(args, expr)
+    val cid = getUniqueId(unit, p)
+
+    context.put(cid, new WeakReference(unit) -> p)
+
+    cid.id
+  }
+
+  def check(i: Int, inputs: Array[AnyRef]): Boolean = {
+    val id = ExprId(i)
+    val (ur, p) = context.get(id)
+    val unit    = ur.get
+
+    val program = unit.program
+    val ctx     = unit.ctx
+
+    ctx.reporter.debug("Executing SAT problem (codegen)!")
+    val is = inputs.toSeq
+
+    if (!cache.containsKey(id)) {
+      cache.put(id, new MutableMap())
+    }
+
+    val resultCache = cache.get(id)
+
+    if (resultCache contains is) {
+      resultCache(is).isDefined
+    } else {
+      val tStart = System.currentTimeMillis
+
+      val solverf = SolverFactory.default(ctx, program).withTimeout(10.second)
+      val solver = solverf.getNewSolver()
+
+      val bindingCnstrs = (p.args zip inputs).flatMap { case (id, jvmExpr) =>
+        val v = Variable(id)
+        val expr = unit.jvmToExpr(jvmExpr, id.getType)
+
+        val inputCnstr = expr match {
+          case purescala.Extractors.FiniteLambda(default, mapping) =>
+            Some(ElementOfSet(v, FiniteSet(mapping.map(_._1).toSet, id.getType)))
+          case _ => None
+        }
+
+        Seq(Equals(v, expr)) ++ inputCnstr
+      }
+
+      solver.assertCnstr(andJoin(p.problem +: bindingCnstrs))
+
+      try {
+        solver.check match {
+          case Some(true) =>
+            val model = solver.getModel
+            val valModel = valuateWithModel(model) _
+            val res = p.args.map(valModel)
+            val leonRes = tupleWrap(res)
+
+            val total = System.currentTimeMillis-tStart
+
+            ctx.reporter.debug("Constraint \"execution\" took "+total+"ms")
+            ctx.reporter.debug("SAT with model "+leonRes.asString(ctx))
+
+            val obj = unit.exprToJVM(leonRes)(new LeonCodeGenRuntimeMonitor(unit.params.maxFunctionInvocations))
+            resultCache += is -> Some(obj)
+            true
+
+          case Some(false) =>
+            resultCache += is -> None
+            false
+
+          case _ =>
+            throw new LeonCodeGenRuntimeException("Timeout exceeded")
+        }
+      } finally {
+        solver.free()
+        solverf.shutdown()
+      }
+    }
+  }
+
+  def getModel(i: Int, inputs: Array[AnyRef]): java.lang.Object = {
+    val id = ExprId(i)
+    if (!cache.containsKey(id)) {
+      throw new LeonCodeGenRuntimeException("Problem was not checked before model query")
+    }
+
+    cache.get(id).get(inputs.toSeq) match {
+      case Some(Some(obj)) =>
+        obj
+      case Some(None) =>
+        throw new LeonCodeGenRuntimeException("Problem was UNSAT")
+      case None =>
+        throw new LeonCodeGenRuntimeException("Problem was not checked before model query")
+    }
+  }
+}
+
diff --git a/src/main/scala/leon/frontends/scalac/ASTExtractors.scala b/src/main/scala/leon/frontends/scalac/ASTExtractors.scala
index 066955731..db477ec49 100644
--- a/src/main/scala/leon/frontends/scalac/ASTExtractors.scala
+++ b/src/main/scala/leon/frontends/scalac/ASTExtractors.scala
@@ -1036,6 +1036,5 @@ trait ASTExtractors {
         case _ => None
       }
     }
-
   }
 }
diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala
index 34d781046..da0a83066 100644
--- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala
+++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala
@@ -1634,6 +1634,9 @@ trait CodeExtraction extends ASTExtractors {
             case (IsTyped(a1, at: ArrayType), "updated", List(k, v)) =>
               ArrayUpdated(a1, k, v)
 
+            case (IsTyped(a1, at: ArrayType), "clone", Nil) =>
+              a1
+
 
             // Map methods
             case (IsTyped(a1, MapType(_, vt)), "apply", List(a2)) =>
diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala
index b44a4b9f8..720024b4f 100644
--- a/src/main/scala/leon/purescala/Expressions.scala
+++ b/src/main/scala/leon/purescala/Expressions.scala
@@ -95,6 +95,8 @@ object Expressions {
     * @param pred The predicate to satisfy. It should be a function whose argument's type can handle the type of the body
     */
   case class Ensuring(body: Expr, pred: Expr) extends Expr {
+    require(pred.isInstanceOf[Lambda])
+
     val getType = pred.getType match {
       case FunctionType(Seq(bodyType), BooleanType) if isSubtypeOf(body.getType, bodyType) =>
         body.getType
@@ -833,8 +835,8 @@ object Expressions {
     *                      with a default value (as genereted with `Array.fill` in Scala).
     */
   case class NonemptyArray(elems: Map[Int, Expr], defaultLength: Option[(Expr, Expr)]) extends Expr {
-    private val elements = elems.values.toList ++ defaultLength.map{_._1}
-    val getType = ArrayType(optionToType(leastUpperBound(elements map { _.getType}))).unveilUntyped
+    private val elements = elems.values.toList ++ defaultLength.map(_._1)
+    val getType = ArrayType(optionToType(leastUpperBound(elements map { _.getType }))).unveilUntyped
   }
 
   /** $encodingof `Array[tpe]()` */
diff --git a/src/main/scala/leon/purescala/FunctionClosure.scala b/src/main/scala/leon/purescala/FunctionClosure.scala
index 4ac11044d..607e9aeb4 100644
--- a/src/main/scala/leon/purescala/FunctionClosure.scala
+++ b/src/main/scala/leon/purescala/FunctionClosure.scala
@@ -77,7 +77,9 @@ class FunctionClosure extends TransformationPhase {
       val newPrecondition = simplifyLets(introduceLets(and((capturedConstraints ++ fd.precondition).toSeq :_*), fd2FreshFd))
       newFunDef.precondition = if(newPrecondition == BooleanLiteral(true)) None else Some(newPrecondition)
 
-      val freshPostcondition = fd.postcondition.map{ post => introduceLets(post, fd2FreshFd).setPos(post) }
+      val freshPostcondition = fd.postcondition.map { case post @ Lambda(args, body) =>
+        Lambda(args, introduceLets(body, fd2FreshFd).setPos(body)).setPos(post)
+      }
       newFunDef.postcondition = freshPostcondition
       
       pathConstraints = fd.precondition.getOrElse(BooleanLiteral(true)) :: pathConstraints
diff --git a/src/main/scala/leon/solvers/templates/QuantificationManager.scala b/src/main/scala/leon/solvers/templates/QuantificationManager.scala
index d5bec5c31..a420b817b 100644
--- a/src/main/scala/leon/solvers/templates/QuantificationManager.scala
+++ b/src/main/scala/leon/solvers/templates/QuantificationManager.scala
@@ -14,8 +14,21 @@ import Instantiation._
 
 import scala.collection.mutable.{Map => MutableMap, Set => MutableSet}
 
-case class Matcher[T](caller: T, tpe: TypeTree, args: Seq[T]) {
-  override def toString = "M(" + caller + " : " + tpe + ", " + args.mkString("(",",",")") + ")"
+object Matcher {
+  def argValue[T](arg: Either[T, Matcher[T]]): T = arg match {
+    case Left(value) => value
+    case Right(matcher) => matcher.encoded
+  }
+}
+
+case class Matcher[T](caller: T, tpe: TypeTree, args: Seq[Either[T, Matcher[T]]], encoded: T) {
+  override def toString = "M(" + caller + " : " + tpe + ", " + args.map(Matcher.argValue).mkString("(",",",")") + ")"
+
+  def substitute(substituter: T => T): Matcher[T] = copy(
+    caller = substituter(caller),
+    args = args.map { arg => arg.left.map(substituter).right.map(_.substitute(substituter)) },
+    encoded = substituter(encoded)
+  )
 }
 
 case class IllegalQuantificationException(expr: Expr, msg: String)
@@ -25,7 +38,8 @@ class QuantificationTemplate[T](
   val quantificationManager: QuantificationManager[T],
   val start: T,
   val qs: (Identifier, T),
-  val holdVar: T,
+  val q2s: (Identifier, T),
+  val insts: (Identifier, T),
   val guardVar: T,
   val quantifiers: Seq[(Identifier, T)],
   val condVars: Map[Identifier, T],
@@ -47,7 +61,8 @@ object QuantificationTemplate {
     quantificationManager: QuantificationManager[T],
     pathVar: (Identifier, T),
     qs: (Identifier, T),
-    holder: Identifier,
+    q2: Identifier,
+    inst: Identifier,
     guard: Identifier,
     quantifiers: Seq[(Identifier, T)],
     condVars: Map[Identifier, T],
@@ -57,28 +72,24 @@ object QuantificationTemplate {
     subst: Map[Identifier, T]
   ): QuantificationTemplate[T] = {
 
-    val holders: (Identifier, T) = holder -> encoder.encodeId(holder)
+    val q2s: (Identifier, T) = q2 -> encoder.encodeId(q2)
+    val insts: (Identifier, T) = inst -> encoder.encodeId(inst)
     val guards: (Identifier, T) = guard -> encoder.encodeId(guard)
 
     val (clauses, blockers, applications, matchers, _) =
       Template.encode(encoder, pathVar, quantifiers, condVars, exprVars, guardedExprs, lambdas,
-        substMap = subst + holders + guards + qs)
+        substMap = subst + q2s + insts + guards + qs)
 
     new QuantificationTemplate[T](quantificationManager,
-      pathVar._2, qs, holders._2, guards._2, quantifiers,
+      pathVar._2, qs, q2s, insts, guards._2, quantifiers,
       condVars, exprVars, clauses, blockers, applications, matchers, lambdas)
   }
 }
 
 class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManager[T](encoder) {
 
-  private val nextHolder: () => T = {
-    val id: Identifier = FreshIdentifier("ph", BooleanType)
-    () => encoder.encodeId(id)
-  }
-
   private val nextQ: () => T = {
-    val id: Identifier = FreshIdentifier("q", BooleanType)
+    val id: Identifier = FreshIdentifier("q", BooleanType, true)
     () => encoder.encodeId(id)
   }
 
@@ -113,7 +124,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage
     knownStack = knownStack.drop(lvl)
   }
 
-  def blockers: Seq[T] = quantifications.map(_.holdVar)
+  def assumptions: Seq[T] = quantifications.map(_.currentQ2Var)
 
   override def registerFree(ids: Seq[(TypeTree, T)]): Unit = {
     super.registerFree(ids)
@@ -121,47 +132,24 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage
   }
 
   private class Quantification (
-    val qVar: T,
-    val holdVar: T,
+    val qs: (Identifier, T),
+    val q2s: (Identifier, T),
+    val insts: (Identifier, T),
     val guardVar: T,
-    var currentHolder: T,
     val quantified: Set[T],
+    val matchers: Set[Matcher[T]],
+    val allMatchers: Map[T, Set[Matcher[T]]],
     val condVars: Map[Identifier, T],
     val exprVars: Map[Identifier, T],
     val clauses: Seq[T],
     val blockers: Map[T, Set[TemplateCallInfo[T]]],
     val applications: Map[T, Set[App[T]]],
-    val matchers: Set[Matcher[T]],
     val lambdas: Map[T, LambdaTemplate[T]]) {
 
-    def this(
-      qVar: T, holdVar: T, guardVar: T, quantified: Set[T],
-      condVars: Map[Identifier, T], exprVars: Map[Identifier, T],
-      clauses: Seq[T], blockers: Map[T, Set[TemplateCallInfo[T]]], applications: Map[T, Set[App[T]]],
-      matchers: Set[Matcher[T]], lambdas: Map[T, LambdaTemplate[T]]) = {
-        this(qVar, holdVar, guardVar, nextHolder(), quantified,
-          condVars, exprVars, clauses, blockers, applications, matchers, lambdas)
-    }
-
-    def substitute(subst: Map[T, T]) = {
-      val substituter = encoder.substitute(subst)
-
-      new Quantification (
-        qVar, holdVar, guardVar, currentHolder,
-        quantified, condVars, exprVars,
-        clauses map substituter,
-        blockers map { case (b, fis) =>
-          substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(substituter)))
-        },
-        applications map { case (b, fas) =>
-          substituter(b) -> fas.map(fa => fa.copy(caller = substituter(fa.caller), args = fa.args.map(substituter)))
-        },
-        matchers map (m => m.copy(caller = substituter(m.caller), args = m.args.map(substituter))),
-        lambdas map { case (idT, template) => substituter(idT) -> template.substitute(subst) }
-      )
-    }
+    var currentQ2Var: T = qs._2
+    private var slaves: Seq[(T, Map[T,T], Quantification)] = Nil
 
-    def instantiate(blocker: T, matcher: Matcher[T]): Instantiation[T] = {
+    private def mappings(blocker: T, matcher: Matcher[T]): Set[(T, Map[T, T])] = {
 
       // Build a mapping from applications in the quantified statement to all potential concrete
       // applications previously encountered. Also make sure the current `app` is in the mapping
@@ -172,136 +160,251 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage
         //    be bound when generating the new constraints
         .filter(qm => correspond(qm, matcher))
         // 2. build the instantiation mapping associated to the chosen current application binding
-        .flatMap { bindingMatcher => matchers
+        .flatMap { bindingMatcher =>
+
           // 2.1. select all potential matches for each quantified application
-          .map(qm => if (qm == bindingMatcher) {
-            bindingMatcher -> Set(blocker -> matcher)
-          } else {
-            val instances: Set[(T, Matcher[T])] = instantiated.filter { case (b, m) => correspond(qm, m) }
+          val matcherToInstances = matchers
+            .map(qm => if (qm == bindingMatcher) {
+              bindingMatcher -> Set(blocker -> matcher)
+            } else {
+              val instances: Set[(T, Matcher[T])] = instantiated.filter { case (b, m) => correspond(qm, m) }
+
+              // concrete applications can appear multiple times in the constraint, and this is also the case
+              // for the current application for which we are generating the constraints
+              val withCurrent = if (correspond(qm, matcher)) instances + (blocker -> matcher) else instances
 
-            // concrete applications can appear multiple times in the constraint, and this is also the case
-            // for the current application for which we are generating the constraints
-            val withCurrent = if (correspond(qm, matcher)) instances + (blocker -> matcher) else instances
+              qm -> withCurrent
+            }).toMap
 
-            qm -> withCurrent
-          })
           // 2.2. based on the possible bindings for each quantified application, build a set of
           //      instantiation mappings that can be used to instantiate all necessary constraints
-          .foldLeft[Set[Set[(T, Matcher[T], Matcher[T])]]] (Set(Set.empty)) {
-            case (mappings, (qm, instances)) => Set(instances.toSeq.flatMap {
-              case (b, m) => mappings.map(mapping => mapping + ((b, qm, m)))
-            } : _*)
-          }
+          extractMappings(matcherToInstances)
         }
 
-      var instantiation = Instantiation.empty[T]
-
-      for (mapping <- matcherMappings) {
-        var constraints: List[T] = Nil
-        var subst: Map[T, T] = Map.empty
+      for (mapping <- matcherMappings) yield extractSubst(quantified, mapping)
+    }
 
-        for {
-          (b, Matcher(qcaller, _, qargs), Matcher(caller, _, args)) <- mapping
-          _ = constraints :+= b
-          (qarg, arg) <- (qargs zip args)
-        } if (subst.isDefinedAt(qarg)) {
-          constraints :+= encoder.mkEquals(subst(qarg), arg)
-        } else if (!quantified(qarg)) {
-          constraints :+= encoder.mkEquals(qarg, arg)
-        } else {
-          subst += qarg -> arg
-        }
+    private def extractSlaveInfo(enabler: T, senabler: T, subst: Map[T,T], ssubst: Map[T,T]): (T, Map[T,T]) = {
+      val substituter = encoder.substitute(subst)
+      val slaveEnabler = encoder.mkAnd(enabler, substituter(senabler))
+      val slaveSubst = ssubst.map(p => p._1 -> substituter(p._2))
+      (slaveEnabler, slaveSubst)
+    }
 
-        val enabler = if (constraints.size == 1) constraints.head else encoder.mkAnd(constraints : _*)
-        val newHolder = nextHolder()
+    private def instantiate(enabler: T, subst: Map[T, T], seen: Set[Quantification]): Instantiation[T] = {
+      if (seen(this)) {
+        Instantiation.empty[T]
+      } else {
+        val nextQ2Var = encoder.encodeId(q2s._1)
 
         val baseSubstMap = (condVars ++ exprVars).map { case (id, idT) => idT -> encoder.encodeId(id) }
         val lambdaSubstMap = lambdas map { case (idT, lambda) => idT -> encoder.encodeId(lambda.id) }
         val substMap = subst ++ baseSubstMap ++ lambdaSubstMap +
-          (qVar -> currentHolder) + (guardVar -> enabler) + (holdVar -> newHolder)
+          (qs._2 -> currentQ2Var) + (guardVar -> enabler) + (q2s._2 -> nextQ2Var) +
+          (insts._2 -> encoder.encodeId(insts._1))
+
+        var instantiation = Template.instantiate(encoder, QuantificationManager.this,
+          clauses, blockers, applications, Seq.empty, Map.empty[T, Set[Matcher[T]]], lambdas, substMap)
+
+        for {
+          (senabler, ssubst, slave) <- slaves
+          (slaveEnabler, slaveSubst) = extractSlaveInfo(enabler, senabler, subst, ssubst)
+        } instantiation ++= slave.instantiate(slaveEnabler, slaveSubst, seen + this)
+
+        currentQ2Var = nextQ2Var
+        instantiation
+      }
+    }
+
+    def register(senabler: T, ssubst: Map[T, T], slave: Quantification): Instantiation[T] = {
+      var instantiation = Instantiation.empty[T]
+
+      for {
+        (blocker, matcher) <- instantiated
+        (enabler, subst) <- mappings(blocker, matcher)
+        (slaveEnabler, slaveSubst) = extractSlaveInfo(enabler, senabler, subst, ssubst)
+      } instantiation ++= slave.instantiate(slaveEnabler, slaveSubst, Set(this))
+
+      slaves :+= (senabler, ssubst, slave)
 
-        instantiation ++= Template.instantiate(encoder, QuantificationManager.this,
-          clauses, blockers, applications, Seq.empty, Map.empty, lambdas, substMap)
-        currentHolder = newHolder
+      instantiation
+    }
+
+    def instantiate(blocker: T, matcher: Matcher[T]): Instantiation[T] = {
+      var instantiation = Instantiation.empty[T]
+
+      for ((enabler, subst) <- mappings(blocker, matcher)) {
+        instantiation ++= instantiate(enabler, subst, Set.empty)
       }
 
       instantiation
     }
   }
 
-  def instantiateQuantification(template: QuantificationTemplate[T], substMap: Map[T, T]): Instantiation[T] = {
-    val trueT = encoder.encodeExpr(Map.empty)(BooleanLiteral(true))
-    val instantiationSubst: Map[T, T] = substMap + (template.guardVar -> trueT)
+  private def extractMappings(
+    bindings: Map[Matcher[T], Set[(T, Matcher[T])]]
+  ): Set[Set[(T, Matcher[T], Matcher[T])]] = {
+    val allMappings = bindings.foldLeft[Set[Set[(T, Matcher[T], Matcher[T])]]](Set(Set.empty)) {
+      case (mappings, (qm, instances)) => Set(instances.toSeq.flatMap {
+        case (b, m) => mappings.map(mapping => mapping + ((b, qm, m)))
+      } : _*)
+    }
+
+    def subBindings(b: T, sm: Matcher[T], m: Matcher[T]): Set[(T, Matcher[T], Matcher[T])] = {
+      (for ((sarg, arg) <- sm.args zip m.args) yield {
+        (sarg, arg) match {
+          case (Right(sargm), Right(argm)) => Set((b, sargm, argm)) ++ subBindings(b, sargm, argm)
+          case _ => Set.empty[(T, Matcher[T], Matcher[T])]
+        }
+      }).flatten.toSet
+    }
 
-    val substituter = encoder.substitute(instantiationSubst)
-    val matchers = template.matchers.map { case (b, ms) =>
-      substituter(b) -> ms.map(m => m.copy(caller = substituter(m.caller), args = m.args.map(substituter)))
+    allMappings.filter { s =>
+      val withSubs = s ++ s.flatMap { case (b, sm, m) => subBindings(b, sm, m) }
+      withSubs.groupBy(_._2).forall(_._2.size == 1)
     }
+  }
+
+  private def extractSubst(quantified: Set[T], mapping: Set[(T, Matcher[T], Matcher[T])]): (T, Map[T,T]) = {
+    var constraints: List[T] = Nil
+    var subst: Map[T, T] = Map.empty
+
+    for {
+      (b, Matcher(qcaller, _, qargs, _), Matcher(caller, _, args, _)) <- mapping
+      _ = constraints :+= b
+      (qarg, arg) <- (qargs zip args)
+      argVal = Matcher.argValue(arg)
+    } qarg match {
+      case Left(quant) if subst.isDefinedAt(quant) =>
+        constraints :+= encoder.mkEquals(quant, argVal)
+      case Left(quant) if quantified(quant) =>
+        subst += quant -> argVal
+      case _ =>
+        constraints :+= encoder.mkEquals(Matcher.argValue(qarg), argVal)
+    }
+
+    val enabler =
+      if (constraints.isEmpty) encoder.encodeExpr(Map.empty)(BooleanLiteral(true))
+      else if (constraints.size == 1) constraints.head
+      else encoder.mkAnd(constraints : _*)
 
-    var instantiation: Instantiation[T] = Template.instantiate(encoder, this,
-      template.clauses, template.blockers, template.applications,
-      Seq.empty, Map.empty, template.lambdas, instantiationSubst)
+    (encoder.substitute(subst)(enabler), subst)
+  }
 
+  def instantiateQuantification(template: QuantificationTemplate[T], substMap: Map[T, T]): Instantiation[T] = {
     val quantified = template.quantifiers.map(_._2).toSet
 
-    val allMatchers: Set[Matcher[T]] = {
-      def rec(templates: Map[T, LambdaTemplate[T]]): Set[Matcher[T]] = templates.flatMap {
-        case (_, template) => template.matchers.flatMap(_._2).toSet ++ rec(template.lambdas)
-      }.toSet
+    val allMatchers: Map[T, Set[Matcher[T]]] = {
+      def rec(templates: Map[T, LambdaTemplate[T]]): Map[T, Set[Matcher[T]]] =
+        templates.foldLeft(Map.empty[T, Set[Matcher[T]]]) {
+          case (matchers, (_, template)) => matchers merge template.matchers merge rec(template.lambdas)
+        }
 
-      val allMatchers = template.matchers.flatMap(_._2).toSet ++ rec(template.lambdas)
-      for (m @ Matcher(caller, tpe, args) <- allMatchers if args exists quantified) yield m
+      template.matchers merge rec(template.lambdas)
     }
 
-    val matchQuorums: Seq[Set[Matcher[T]]] = allMatchers.subsets.filter { ms =>
+    val quantifiedMatchers = for {
+      (_, ms) <- allMatchers
+      m @ Matcher(_, _, args, _) <- ms
+      if args exists (_.left.exists(quantified))
+    } yield m
+
+    val matchQuorums: Seq[Set[Matcher[T]]] = quantifiedMatchers.toSet.subsets.filter { ms =>
       var doubled: Boolean = false
       var qs: Set[T] = Set.empty
-      for (m @ Matcher(_, _, args) <- ms) {
-        val qargs = (args filter quantified).toSet
+      for (m @ Matcher(_, _, args, _) <- ms) {
+        val qargs = (args collect { case Left(a) if quantified(a) => a }).toSet
         if ((qs & qargs).nonEmpty) doubled = true
         qs ++= qargs
       }
 
       !doubled && (qs == quantified)
-    }.toSeq
+    }.toList
+
+    var instantiation = Instantiation.empty[T]
 
-    val newQuantifications = for (matchers <- matchQuorums) yield {
-      val newQ = nextQ()
+    val qs = for (matchers <- matchQuorums) yield {
+      val newQ = encoder.encodeId(template.qs._1)
+      val subst = substMap + (template.qs._2 -> newQ)
 
-      new Quantification(newQ,
-        template.holdVar, template.guardVar,
-        newQ, // set template `qVar` as `currentHolder` to ensure blocking before first unfolding
+      val substituter = encoder.substitute(substMap + (template.qs._2 -> newQ))
+      val quantification = new Quantification(template.qs._1 -> newQ,
+        template.q2s, template.insts, template.guardVar,
         quantified,
+        matchers map (m => m.substitute(substituter)),
+        allMatchers map { case (b, ms) => substituter(b) -> ms.map(_.substitute(substituter)) },
         template.condVars,
         template.exprVars,
-        template.clauses,
-        template.blockers,
-        template.applications,
-        matchers,
-        template.lambdas
-      ).substitute(substMap)
-    }
+        template.clauses map substituter,
+        template.blockers map { case (b, fis) =>
+          substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(substituter)))
+        },
+        template.applications map { case (b, fas) =>
+          substituter(b) -> fas.map(fa => fa.copy(caller = substituter(fa.caller), args = fa.args.map(substituter)))
+        },
+        template.lambdas map { case (idT, template) => substituter(idT) -> template.substitute(subst) }
+      )
 
-    val eqClause = {
-      val qs = newQuantifications.map(_.qVar)
-      val newQs = if (qs.isEmpty) trueT else if (qs.size == 1) qs.head else encoder.mkAnd(qs : _*)
-      encoder.mkImplies(substMap(template.start), encoder.mkEquals(template.holdVar, newQs))
-    }
+      def extendClauses(master: Quantification, slave: Quantification): Instantiation[T] = {
+        val bindingsMap: Map[Matcher[T], Set[(T, Matcher[T])]] = slave.matchers.map { sm =>
+          val instances = master.allMatchers.toSeq.flatMap { case (b, ms) => ms.map(b -> _) }
+          sm -> instances.filter(p => correspond(p._2, sm)).toSet
+        }.toMap
+
+        val allMappings = extractMappings(bindingsMap)
+        val filteredMappings = allMappings.filter { s =>
+          s.exists { case (b, sm, m) => !master.matchers(m) } &&
+          s.exists { case (b, sm, m) => sm != m } &&
+          s.forall { case (b, sm, m) =>
+            (sm.args zip m.args).forall {
+              case (Right(_), Left(_)) => false
+              case _ => true
+            }
+          }
+        }
+
+        var instantiation = Instantiation.empty[T]
 
-    instantiation = (instantiation._1 :+ eqClause, instantiation._2, instantiation._3)
+        for (mapping <- filteredMappings) {
+          val (enabler, subst) = extractSubst(slave.quantified, mapping)
+          instantiation ++= master.register(enabler, subst, slave)
+        }
+
+        instantiation
+      }
+
+      val allSet = quantification.allMatchers.flatMap(_._2).toSet
+      for (q <- quantifications) {
+        val allQSet = q.allMatchers.flatMap(_._2).toSet
+        if (quantification.matchers.forall(m => allQSet.exists(qm => correspond(qm, m))))
+          instantiation ++= extendClauses(q, quantification)
 
-    val saved = instantiated
+        if (q.matchers.forall(qm => allSet.exists(m => correspond(qm, m))))
+          instantiation ++= extendClauses(quantification, q)
+      }
+
+      for ((b, m) <- instantiated) {
+        instantiation ++= quantification.instantiate(b, m)
+      }
 
-    for ((b, ms) <- matchers; bp = substituter(b); m <- ms) {
-      val newM = m.copy(caller = substituter(m.caller), args = m.args.map(substituter))
-      instantiation ++= instantiateMatcher(bp, newM)
+      quantifications :+= quantification
+      quantification.qs._2
     }
 
-    for ((b, m) <- saved; q <- newQuantifications) {
-      instantiation ++= q.instantiate(b, m)
+    instantiation = instantiation withClause {
+      val newQs =
+        if (qs.isEmpty) encoder.encodeExpr(Map.empty)(BooleanLiteral(true))
+        else if (qs.size == 1) qs.head
+        else encoder.mkAnd(qs : _*)
+      encoder.mkImplies(substMap(template.start), encoder.mkEquals(substMap(template.qs._2), newQs))
     }
 
-    quantifications ++= newQuantifications
+    val quantifierSubst = substMap ++ template.quantifiers.map { case (id, idT) => idT -> encoder.encodeId(id) }
+    val substituter = encoder.substitute(quantifierSubst)
+
+    for ((b, ms) <- template.matchers; m <- ms) {
+      instantiation ++= instantiateMatcher(substMap(template.start), m.substitute(substituter))
+    }
 
     instantiation
   }
diff --git a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala
index 4c49c3101..ac155c710 100644
--- a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala
+++ b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala
@@ -170,9 +170,8 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T],
 
     def rec(pathVar: Identifier, expr: Expr): Expr = {
       expr match {
-        case a @ Assert(cond, _, body) =>
-          storeGuarded(pathVar, rec(pathVar, cond))
-          rec(pathVar, body)
+        case a @ Assert(cond, err, body) =>
+          rec(pathVar, IfExpr(cond, body, Error(body.getType, err getOrElse "assertion failed")))
 
         case e @ Ensuring(_, _) =>
           rec(pathVar, e.toAssert)
@@ -272,7 +271,7 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T],
           val localSubst: Map[Identifier, T] = substMap ++ condVars ++ exprVars ++ lambdaVars
           val clauseSubst: Map[Identifier, T] = localSubst ++ (idArgs zip trArgs)
           val (lambdaConds, lambdaExprs, lambdaGuarded, lambdaTemplates, lambdaQuants) = mkClauses(pathVar, clause, clauseSubst)
-          assert(lambdaQuants.isEmpty, "Unhandled quantification in lambdas in " + clause)
+          assert(lambdaQuants.isEmpty, "Unhandled quantification in lambdas in " + l)
 
           val ids: (Identifier, T) = lid -> storeLambda(lid)
           val dependencies: Map[Identifier, T] = variablesOf(l).map(id => id -> localSubst(id)).toMap
@@ -292,11 +291,12 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T],
             val idQuantifiers : Seq[Identifier] = quantifiers.toSeq
             val trQuantifiers : Seq[T] = idQuantifiers.map(encoder.encodeId)
 
-            val q: Identifier = FreshIdentifier("q", BooleanType)
-            val ph: Identifier = FreshIdentifier("ph", BooleanType)
-            val guard: Identifier = FreshIdentifier("guard", BooleanType)
+            val q: Identifier = FreshIdentifier("q", BooleanType, true)
+            val q2: Identifier = FreshIdentifier("qo", BooleanType, true)
+            val inst: Identifier = FreshIdentifier("inst", BooleanType, true)
+            val guard: Identifier = FreshIdentifier("guard", BooleanType, true)
 
-            val clause = Equals(Variable(q), And(Variable(ph), Implies(Variable(guard), conjunct)))
+            val clause = Equals(Variable(inst), Implies(Variable(guard), conjunct))
 
             val qs: (Identifier, T) = q -> encoder.encodeId(q)
             val localSubst: Map[Identifier, T] = substMap ++ condVars ++ exprVars ++ lambdaVars
@@ -304,8 +304,11 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T],
             val (qConds, qExprs, qGuarded, qTemplates, qQuants) = mkClauses(pathVar, clause, clauseSubst)
             assert(qQuants.isEmpty, "Unhandled nested quantification in "+clause)
 
+            val binder = Equals(Variable(q), And(Variable(q2), Variable(inst)))
+            val allQGuarded = qGuarded + (pathVar -> (binder +: qGuarded.getOrElse(pathVar, Seq.empty)))
+
             val template = QuantificationTemplate[T](encoder, manager, pathVar -> encodedCond(pathVar),
-              qs, ph, guard, idQuantifiers zip trQuantifiers, qConds, qExprs, qGuarded, qTemplates, localSubst)
+              qs, q2, inst, guard, idQuantifiers zip trQuantifiers, qConds, qExprs, allQGuarded, qTemplates, localSubst)
             registerQuantification(template)
             Variable(q)
           }
diff --git a/src/main/scala/leon/solvers/templates/Templates.scala b/src/main/scala/leon/solvers/templates/Templates.scala
index d14a294c0..678050fa1 100644
--- a/src/main/scala/leon/solvers/templates/Templates.scala
+++ b/src/main/scala/leon/solvers/templates/Templates.scala
@@ -36,6 +36,9 @@ object Instantiation {
 
       (thisClauses ++ thatClauses, thisBlockers merge thatBlockers, thisApps merge thatApps)
     }
+
+    def withClause(cl: T): Instantiation[T] = (i._1 :+ cl, i._2, i._3)
+    def withClauses(cls: Seq[T]): Instantiation[T] = (i._1 ++ cls, i._2, i._3)
   }
 }
 
@@ -82,56 +85,50 @@ trait Template[T] { self =>
 
 object Template {
 
-  private def functionCallInfos[T](encodeExpr: Expr => T)(expr: Expr): (Set[TemplateCallInfo[T]], Set[App[T]]) = {
-    def invocationCaller(expr: Expr): Boolean = expr match {
-      case fi: FunctionInvocation => true
-      case Application(caller, _) => invocationCaller(caller)
-      case _ => false
+  private object InvocationExtractor {
+    private def flatInvocation(expr: Expr): Option[(TypedFunDef, Seq[Expr])] = expr match {
+      case fi @ FunctionInvocation(tfd, args) => Some((tfd, args))
+      case Application(caller, args) => flatInvocation(caller) match {
+        case Some((tfd, prevArgs)) => Some((tfd, prevArgs ++ args))
+        case None => None
+      }
+      case _ => None
     }
 
-    val calls = collect[Expr] {
-      case IsTyped(f: FunctionInvocation, ft: FunctionType) => Set.empty
-      case IsTyped(f: Application, ft: FunctionType) => Set.empty
-      case f: FunctionInvocation => Set(f)
-      case f: Application => Set(f)
-      case _ => Set.empty
-    }(expr)
-
-    val (functionCalls, appCalls) = calls partition invocationCaller
-
-    def functionTemplate(expr: Expr): TemplateCallInfo[T] = expr match {
-      case FunctionInvocation(tfd, args) =>
-        TemplateCallInfo(tfd, args.map(encodeExpr))
-      case Application(caller, args) =>
-        val TemplateCallInfo(tfd, prevArgs) = functionTemplate(caller)
-        TemplateCallInfo(tfd, prevArgs ++ args.map(encodeExpr))
-      case _ => scala.sys.error("Should never happen!")
+    def unapply(expr: Expr): Option[(TypedFunDef, Seq[Expr])] = expr match {
+      case IsTyped(f: FunctionInvocation, ft: FunctionType) => None
+      case IsTyped(f: Application, ft: FunctionType) => None
+      case FunctionInvocation(tfd, args) => Some(tfd -> args)
+      case f: Application => flatInvocation(f)
+      case _ => None
     }
+  }
 
-    val templates : Set[TemplateCallInfo[T]] = functionCalls map functionTemplate
-
-    def applicationTemplate(expr: Expr): App[T] = expr match {
-      case Application(caller : Application, args) =>
-        val App(c, tpe, prevArgs) = applicationTemplate(caller)
-        App(c, tpe, prevArgs ++ args.map(encodeExpr))
-      case Application(c, args) =>
-        App(encodeExpr(c), c.getType.asInstanceOf[FunctionType], args.map(encodeExpr))
-      case _ => scala.sys.error("Should never happen!")
+  private object ApplicationExtractor {
+    private def flatApplication(expr: Expr): Option[(Expr, Seq[Expr])] = expr match {
+      case Application(fi: FunctionInvocation, _) => None
+      case Application(caller: Application, args) => flatApplication(caller) match {
+        case Some((c, prevArgs)) => Some((c, prevArgs ++ args))
+        case None => None
+      }
+      case Application(caller, args) => Some((caller, args))
+      case _ => None
     }
 
-    val apps : Set[App[T]] = appCalls map applicationTemplate
-
-    (templates, apps)
+    def unapply(expr: Expr): Option[(Expr, Seq[Expr])] = expr match {
+      case IsTyped(f: Application, ft: FunctionType) => None
+      case f: Application => flatApplication(f)
+      case _ => None
+    }
   }
 
-  private def selectMatchInfos[T](encodeExpr: Expr => T)(expr: Expr): Set[Matcher[T]] = {
-    collect[Matcher[T]] {
-      case ArraySelect(arr, index) =>
-        Set(Matcher(encodeExpr(arr), arr.getType, Seq(encodeExpr(index))))
-      case MapGet(map, key) =>
-        Set(Matcher(encodeExpr(map), map.getType, Seq(encodeExpr(key))))
-      case _ => Set.empty
-    }(expr)
+  private object MatchExtractor {
+    def unapply(expr: Expr): Option[(Expr, Seq[Expr])] = expr match {
+      case ApplicationExtractor(caller, args) => Some(caller -> args)
+      case ArraySelect(arr, index) => Some(arr -> Seq(index))
+      case MapGet(map, key) => Some(map -> Seq(key))
+      case _ => None
+    }
   }
 
   private def invocationMatcher[T](encodeExpr: Expr => T)(tfd: TypedFunDef, args: Seq[Expr]): Matcher[T] = {
@@ -146,8 +143,8 @@ object Template {
     }
 
     val (fiArgs, appArgs) = args.splitAt(tfd.params.size)
-    val Application(caller, arguments) = rec(FunctionInvocation(tfd, fiArgs), appArgs)
-    Matcher(encodeExpr(caller), caller.getType, arguments.map(encodeExpr))
+    val app @ Application(caller, arguments) = rec(FunctionInvocation(tfd, fiArgs), appArgs)
+    Matcher(encodeExpr(caller), caller.getType, arguments.map(arg => Left(encodeExpr(arg))), encodeExpr(app))
   }
 
   def encode[T](
@@ -174,9 +171,6 @@ object Template {
       encodeExpr(Implies(Variable(b), e))
     }).toSeq
 
-    val extractInfos    : Expr => (Set[TemplateCallInfo[T]], Set[App[T]]) = functionCallInfos(encodeExpr)
-    val extractMatchers : Expr => Set[Matcher[T]]                         = selectMatchInfos(encodeExpr)
-
     val optIdCall = optCall.map(tfd => TemplateCallInfo[T](tfd, arguments.map(_._2)))
     val optIdApp = optApp.map { case (idT, tpe) => App(idT, tpe, arguments.map(_._2)) }
 
@@ -194,10 +188,34 @@ object Template {
         var matchInfos : Set[Matcher[T]]          = Set.empty
 
         for (e <- es) {
-          val (newFunInfos, newAppInfos) = extractInfos(e)
-          funInfos ++= newFunInfos
-          appInfos ++= newAppInfos
-          matchInfos ++= extractMatchers(e)
+          funInfos ++= collect[TemplateCallInfo[T]] {
+            case InvocationExtractor(tfd, args) =>
+              Set(TemplateCallInfo(tfd, args.map(encodeExpr)))
+            case _ => Set.empty
+          }(e)
+
+          appInfos ++= collect[App[T]] {
+            case ApplicationExtractor(c, args) =>
+              Set(App(encodeExpr(c), c.getType.asInstanceOf[FunctionType], args.map(encodeExpr)))
+            case _ => Set.empty
+          }(e)
+
+          matchInfos ++= foldRight[Map[Expr, Matcher[T]]] { (expr, res) =>
+            val result = res.flatten.toMap
+
+            result ++ (expr match {
+              case MatchExtractor(c, args) =>
+                // Note that we rely here on the fact that foldRight visits the matcher's arguments first,
+                // so any Matcher in arguments will belong to the `result` map
+                val encodedArgs = args.map(arg => result.get(arg) match {
+                  case Some(matcher) => Right(matcher)
+                  case None => Left(encodeExpr(arg))
+                })
+
+                Some(expr -> Matcher(encodeExpr(c), c.getType, encodedArgs, encodeExpr(expr)))
+              case _ => None
+            })
+          }(e).values
         }
 
         val calls = funInfos -- optIdCall
@@ -206,13 +224,11 @@ object Template {
         val apps = appInfos -- optIdApp
         if (apps.nonEmpty) applications += b -> apps
 
-        val matchs = matchInfos ++
-          apps.map(app => Matcher(app.caller, app.tpe, app.args)) ++
-          funInfos.flatMap {
-            case info @ TemplateCallInfo(tfd, args) if Some(info) == optIdCall =>
-              invocMatcher
-            case _ => None
+        val matchs = matchInfos.filter { case m @ Matcher(mc, mtpe, margs, _) =>
+          !optIdApp.exists { case App(ac, atpe, aargs) =>
+            mc == ac && mtpe == atpe && margs.map(Matcher.argValue) == aargs
           }
+        } ++ (if (funInfos.exists(info => Some(info) == optIdCall)) invocMatcher else None)
 
         if (matchs.nonEmpty) matchers += b -> matchs
       }
@@ -280,8 +296,7 @@ object Template {
     }
 
     for ((b, matchs) <- matchers; bp = substituter(b); m <- matchs) {
-      val newM = m.copy(caller = substituter(m.caller), args = m.args.map(substituter))
-      instantiation ++= manager.instantiateMatcher(bp, newM)
+      instantiation ++= manager.instantiateMatcher(bp, m.substitute(substituter))
     }
 
     for (q <- quantifications) {
@@ -494,7 +509,7 @@ class LambdaTemplate[T] private (
 
     val newMatchers = matchers.map { case (b, ms) =>
       val bp = if (b == start) newStart else b
-      bp -> ms.map(m => m.copy(caller = substituter(m.caller), args = m.args.map(substituter)))
+      bp -> ms.map(_.substitute(substituter))
     }
 
     val newLambdas = lambdas.map { case (idT, template) => idT -> template.substitute(substMap) }
diff --git a/src/main/scala/leon/solvers/templates/UnrollingBank.scala b/src/main/scala/leon/solvers/templates/UnrollingBank.scala
index c1912d9d5..617f1c5f4 100644
--- a/src/main/scala/leon/solvers/templates/UnrollingBank.scala
+++ b/src/main/scala/leon/solvers/templates/UnrollingBank.scala
@@ -93,7 +93,7 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat
 
   def currentBlockers = callInfo.map(_._2._3).toSeq ++ appInfo.map(_._2._4).toSeq
 
-  def quantificationAssumptions = manager.blockers
+  def quantificationAssumptions = manager.assumptions
 
   def getBlockersToUnlock: Seq[T] = {
     if (callInfo.isEmpty && appInfo.isEmpty) {
@@ -265,7 +265,7 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat
           defBlockers += info -> defBlocker
 
           val template = templateGenerator.mkTemplate(tfd)
-          reporter.debug(template)
+          //reporter.debug(template)
 
           val (newExprs, callBlocks, appBlocks) = template.instantiate(defBlocker, args)
           val blockExprs = freshAppBlocks(appBlocks.keys)
diff --git a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala
index bfa1d9c16..d2dc57f50 100644
--- a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala
+++ b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala
@@ -637,7 +637,7 @@ trait AbstractZ3Solver extends Solver {
                       case _ => reporter.fatalError("Translation from Z3 to Array failed")
                     }
 
-                    finiteArray(entries, Some(s, default), to)
+                    finiteArray(entries, Some(default, s), to)
                   case _ =>
                     reporter.fatalError("Translation from Z3 to Array failed")
                 }
diff --git a/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala b/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala
index 62f43d736..5b9d6be13 100644
--- a/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala
+++ b/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala
@@ -38,6 +38,8 @@ trait Z3ModelReconstruction {
   def modelToMap(model: Z3Model, ids: Iterable[Identifier]) : Map[Identifier,Expr] = {
     var asMap = Map.empty[Identifier,Expr]
 
+    println(model)
+
     def completeID(id : Identifier) : Unit = {
       asMap = asMap + (id -> simplestValue(id.getType))
       reporter.debug("Completing variable '" + id + "' to simplest value")
-- 
GitLab