From 9c8b6e54a05d58dfd6c3e4e6d85d2d806ae2c2e4 Mon Sep 17 00:00:00 2001
From: Etienne Kneuss <ekneuss@gmail.com>
Date: Mon, 19 Nov 2012 13:49:08 +0100
Subject: [PATCH] Rules can (and should) have multiple application alternatives

---
 .../scala/leon/synthesis/Heuristics.scala     |  11 +-
 src/main/scala/leon/synthesis/Rules.scala     |  17 ++-
 .../scala/leon/synthesis/Synthesizer.scala    |   2 +-
 src/main/scala/leon/synthesis/Task.scala      | 116 +++++++++---------
 .../synthesis/heuristics/ADTInduction.scala   |   6 +-
 .../synthesis/heuristics/IntInduction.scala   |   2 +-
 .../heuristics/OptimisticInjection.scala      |   2 +-
 .../heuristics/SelectiveInlining.scala        |   2 +-
 .../scala/leon/synthesis/rules/ADTDual.scala  |   2 +-
 .../scala/leon/synthesis/rules/ADTSplit.scala |   2 +-
 .../scala/leon/synthesis/rules/Assert.scala   |   2 +-
 .../leon/synthesis/rules/CaseSplit.scala      |   2 +-
 .../leon/synthesis/rules/EqualitySplit.scala  |   2 +-
 .../synthesis/rules/IntegerEquation.scala     |   4 +-
 .../synthesis/rules/IntegerInequalities.scala |   2 +-
 .../scala/leon/synthesis/rules/OnePoint.scala |   2 +-
 .../synthesis/rules/UnconstrainedOutput.scala |   2 +-
 .../leon/synthesis/rules/Unification.scala    |   2 +-
 .../leon/synthesis/rules/UnusedInput.scala    |   2 +-
 testcases/synthesis/BinaryTree.scala          |   8 ++
 20 files changed, 105 insertions(+), 85 deletions(-)

diff --git a/src/main/scala/leon/synthesis/Heuristics.scala b/src/main/scala/leon/synthesis/Heuristics.scala
index be50b59aa..05b35b83f 100644
--- a/src/main/scala/leon/synthesis/Heuristics.scala
+++ b/src/main/scala/leon/synthesis/Heuristics.scala
@@ -7,10 +7,10 @@ import heuristics._
 
 object Heuristics {
   def all = Set[Synthesizer => Rule](
-    new IntInduction(_),
+    new IntInduction(_)
     //new OptimisticInjection(_),
     //new SelectiveInlining(_),
-    new ADTInduction(_)
+//    new ADTInduction(_)
   )
 }
 
@@ -40,6 +40,9 @@ object HeuristicStep {
 }
 
 
-
-
+object HeuristicOneStep {
+  def apply(synth: Synthesizer, problem: Problem, subProblems: List[Problem], onSuccess: List[Solution] => Solution) = {
+    RuleAlternatives(Set(HeuristicStep(synth, problem, subProblems, onSuccess)))
+  }
+}
 
diff --git a/src/main/scala/leon/synthesis/Rules.scala b/src/main/scala/leon/synthesis/Rules.scala
index 8db62a914..f6b957d1d 100644
--- a/src/main/scala/leon/synthesis/Rules.scala
+++ b/src/main/scala/leon/synthesis/Rules.scala
@@ -20,17 +20,20 @@ object Rules {
     new EqualitySplit(_),
     new CEGIS(_),
     new Assert(_),
-//    new ADTSplit(_),
-    new IntegerEquation(_)
+    new ADTSplit(_),
+    new IntegerEquation(_),
+    new IntegerInequalities(_)
   )
 }
 
 sealed abstract class RuleResult
 case object RuleInapplicable extends RuleResult
 case class RuleSuccess(solution: Solution) extends RuleResult
+case class RuleAlternatives(steps: Set[RuleMultiSteps]) extends RuleResult
+
 case class RuleMultiSteps(subProblems: List[Problem],
-                          steps: List[List[Solution] => List[Problem]],
-                          onSuccess: List[Solution] => (Solution, Boolean)) extends RuleResult
+                          interSteps: List[List[Solution] => List[Problem]],
+                          onSuccess: List[Solution] => (Solution, Boolean))
 
 object RuleStep {
   def apply(subProblems: List[Problem], onSuccess: List[Solution] => Solution) = {
@@ -38,6 +41,12 @@ object RuleStep {
   }
 }
 
+object RuleOneStep {
+  def apply(subProblems: List[Problem], onSuccess: List[Solution] => Solution) = {
+    RuleAlternatives(Set(RuleStep(subProblems, onSuccess)))
+  }
+}
+
 abstract class Rule(val name: String, val synth: Synthesizer, val priority: Priority) {
   def applyOn(task: Task): RuleResult
 
diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala
index f33ffe5c9..d88d3e898 100644
--- a/src/main/scala/leon/synthesis/Synthesizer.scala
+++ b/src/main/scala/leon/synthesis/Synthesizer.scala
@@ -70,7 +70,7 @@ class Synthesizer(val reporter: Reporter,
 
       val prefix = "[%-20s] ".format(Option(task.rule).map(_.toString).getOrElse("root"))
 
-      if (!(firstOnly && (task.parent ne null) && task.parent.isSolved(task.problem))) {
+      if (!(firstOnly && (task.parent ne null) && task.parent.isSolvedFor(task.problem))) {
         val subProblems = task.run()
 
         if (task.minComplexity <= bestSolutionSoFar.complexity) {
diff --git a/src/main/scala/leon/synthesis/Task.scala b/src/main/scala/leon/synthesis/Task.scala
index a9d18d5b5..97b4a9b03 100644
--- a/src/main/scala/leon/synthesis/Task.scala
+++ b/src/main/scala/leon/synthesis/Task.scala
@@ -24,8 +24,9 @@ class Task(synth: Synthesizer,
     }
   }
 
-
   var solution: Option[Solution] = None
+  var alternatives = Set[RuleApplication]()
+
   var minComplexity: AbsSolComplexity = new FixedSolComplexity(0)
 
   class TaskStep(val subProblems: List[Problem]) {
@@ -34,52 +35,64 @@ class Task(synth: Synthesizer,
     var failures     = Set[Rule]()
   }
 
-  var steps: List[TaskStep]                            = Nil
-  var nextSteps: List[List[Solution] => List[Problem]] = Nil
-  var onSuccess: List[Solution] => (Solution, Boolean) = _
-
-  def currentStep                 = steps.head
-
-  def isSolved(problem: Problem): Boolean = parent.isSolved(this.problem) || (currentStep.subSolutions contains problem)
-
-  def partlySolvedBy(t: Task, s: Solution) {
-    if (isBetterSolutionThan(s, currentStep.subSolutions.get(t.problem))) {
-      currentStep.subSolutions += t.problem -> s
-      currentStep.subSolvers   += t.problem -> t
-
-      if (currentStep.subSolutions.size == currentStep.subProblems.size) {
-        val solutions = currentStep.subProblems map currentStep.subSolutions
-
-        if (!nextSteps.isEmpty) {
-          val newProblems = nextSteps.head.apply(solutions)
-          nextSteps = nextSteps.tail
-
-          synth.addProblems(this, newProblems)
-
-          steps = new TaskStep(newProblems) :: steps
-        } else {
-          onSuccess(solutions) match {
-            case (s, true) =>
-              solution = Some(s)
-
-              parent.partlySolvedBy(this, solution.get)
-            case _ =>
-              // solution is there, but it is incomplete (precondition not strongest)
-              //parent.partlySolvedBy(this, Solution.choose(problem))
+  class RuleApplication(
+    val initProblems: List[Problem],
+    val allNextSteps: List[List[Solution] => List[Problem]], 
+    val onSuccess: List[Solution] => (Solution, Boolean)) {
+
+    var allSteps    = List(new TaskStep(initProblems))
+    var nextSteps   = allNextSteps
+    def currentStep = allSteps.head
+
+    def isSolvedFor(p: Problem) = allSteps.exists(_.subSolutions contains p) 
+
+    def partlySolvedBy(t: Task, s: Solution) {
+      if (currentStep.subProblems.contains(t.problem)) {
+        if (isBetterSolutionThan(s, currentStep.subSolutions.get(t.problem))) {
+          currentStep.subSolutions += t.problem -> s
+          currentStep.subSolvers   += t.problem -> t
+
+          if (currentStep.subSolutions.size == currentStep.subProblems.size) {
+            val solutions = currentStep.subProblems map currentStep.subSolutions
+
+            if (!nextSteps.isEmpty) {
+              // Advance to the next step
+              val newProblems = nextSteps.head.apply(solutions)
+              nextSteps = nextSteps.tail
+
+              synth.addProblems(Task.this, newProblems)
+
+              allSteps = new TaskStep(newProblems) :: allSteps
+            } else {
+              onSuccess(solutions) match {
+                case (s, true) =>
+                  solution = Some(s)
+
+                  parent.partlySolvedBy(Task.this, solution.get)
+                case _ =>
+                  // solution is there, but it is incomplete (precondition not strongest)
+                  //parent.partlySolvedBy(this, Solution.choose(problem))
+              }
+            }
           }
         }
       }
     }
+
+    val minComplexity: AbsSolComplexity = {
+      val simplestSubSolutions = allNextSteps.foldLeft(initProblems.map(Solution.basic(_))){
+        (sols, step) => step(sols).map(Solution.basic(_)) 
+      }
+      val simplestSolution = onSuccess(simplestSubSolutions)._1
+      new FixedSolComplexity(parent.minComplexity.value + simplestSolution.complexity.value)
+    }
   }
 
-  def unsolvedBy(t: Task) {
-    currentStep.failures += t.rule
+  // Is this subproblem already fixed?
+  def isSolvedFor(problem: Problem): Boolean = parent.isSolvedFor(this.problem) || (alternatives.exists(_.isSolvedFor(problem)))
 
-    if (currentStep.failures.size == synth.rules.size) {
-      // We might want to report unsolved instead of solvedByChoose, depending
-      // on the cases
-      //parent.partlySolvedBy(this, Solution.choose(problem))
-    }
+  def partlySolvedBy(t: Task, s: Solution) {
+    alternatives.foreach(_.partlySolvedBy(t, s))
   }
 
   def run(): List[Problem] = {
@@ -91,22 +104,11 @@ class Task(synth: Synthesizer,
 
         Nil
 
-      case RuleMultiSteps(subProblems, interSteps, onSuccess) =>
-        this.steps = new TaskStep(subProblems) :: Nil
-        this.nextSteps = interSteps
-        this.onSuccess = onSuccess
-
-        // Compute simplest solution possible to evaluate whether this rule is worth it
-        // To each problem we assign the most basic solution, fold on all inner
-        // steps, and then reconstruct final solution
-        val simplestSubSolutions  = interSteps.foldLeft(subProblems.map(Solution.basic(_))){ (sols, step) => step(sols).map(Solution.basic(_)) }
-        val simplestSolution = onSuccess(simplestSubSolutions)._1
-        minComplexity = new FixedSolComplexity(parent.minComplexity.value + simplestSolution.complexity.value)
-
-        subProblems
+      case RuleAlternatives(steps) =>
+        this.alternatives = steps.map( step => new RuleApplication(step.subProblems, step.interSteps, step.onSuccess) )
 
+        this.alternatives.flatMap(_.initProblems).toList
       case RuleInapplicable =>
-        parent.unsolvedBy(this)
         Nil
     }
   }
@@ -121,7 +123,7 @@ class RootTask(synth: Synthesizer, problem: Problem) extends Task(synth, null, p
     List(problem)
   }
 
-  override def isSolved(problem: Problem) = solver.isDefined
+  override def isSolvedFor(problem: Problem) = solver.isDefined
 
   override def partlySolvedBy(t: Task, s: Solution) {
     if (isBetterSolutionThan(s, solution)) {
@@ -129,8 +131,4 @@ class RootTask(synth: Synthesizer, problem: Problem) extends Task(synth, null, p
       solver   = Some(t)
     }
   }
-
-  override def unsolvedBy(t: Task) {
-  }
-
 }
diff --git a/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala b/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala
index d321a61db..064163d0b 100644
--- a/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala
+++ b/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala
@@ -41,6 +41,7 @@ class ADTInduction(synth: Synthesizer) extends Rule("ADT Induction", synth, 80)
         val newFun = new FunDef(FreshIdentifier("rec", true), resType, VarDecl(inductOn, inductOn.getType) +: residualArgDefs)
 
         val innerPhi = substAll(residualMap + (origId -> Variable(inductOn)), p.phi)
+        val innerPC  = substAll(residualMap + (origId -> Variable(inductOn)), p.c)
 
         val subProblemsInfo = for (dcd <- cd.knownDescendents) yield dcd match {
           case ccd : CaseClassDef =>
@@ -64,10 +65,11 @@ class ADTInduction(synth: Synthesizer) extends Rule("ADT Induction", synth, 80)
             }).flatten
 
             val subPhi = substAll(Map(inductOn -> CaseClass(ccd, newIds.map(Variable(_)))), innerPhi)
+            val subPC  = substAll(Map(inductOn -> CaseClass(ccd, newIds.map(Variable(_)))), innerPC)
 
             val subPre = CaseClassInstanceOf(ccd, Variable(origId))
 
-            val subProblem = Problem(inputs ::: residualArgs, And(p.c :: postFs), subPhi, p.xs)
+            val subProblem = Problem(inputs ::: residualArgs, And(subPC :: postFs), subPhi, p.xs)
 
             (subProblem, subPre, ccd, newIds, recCalls)
           case _ =>
@@ -96,7 +98,7 @@ class ADTInduction(synth: Synthesizer) extends Rule("ADT Induction", synth, 80)
             Solution(Or(globalPre), sols.flatMap(_.defs).toSet+newFun, FunctionInvocation(newFun, p.as.map(Variable(_))))
         }
 
-        HeuristicStep(synth, p, subProblemsInfo.map(_._1).toList, onSuccess)
+        HeuristicOneStep(synth, p, subProblemsInfo.map(_._1).toList, onSuccess)
       } else {
         RuleInapplicable
       }
diff --git a/src/main/scala/leon/synthesis/heuristics/IntInduction.scala b/src/main/scala/leon/synthesis/heuristics/IntInduction.scala
index 2bf690f8c..c1000f824 100644
--- a/src/main/scala/leon/synthesis/heuristics/IntInduction.scala
+++ b/src/main/scala/leon/synthesis/heuristics/IntInduction.scala
@@ -56,7 +56,7 @@ class IntInduction(synth: Synthesizer) extends Rule("Int Induction", synth, 50)
             Solution.none
         }
 
-        HeuristicStep(synth, p, List(subBase, subGT, subLT), onSuccess)
+        HeuristicOneStep(synth, p, List(subBase, subGT, subLT), onSuccess)
       case _ =>
         RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala b/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala
index 9c76475e4..cb6682fcc 100644
--- a/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala
+++ b/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala
@@ -38,7 +38,7 @@ class OptimisticInjection(synth: Synthesizer) extends Rule("Opt. Injection", syn
 
       val sub = p.copy(phi = And(newExprs))
 
-      HeuristicStep(synth, p, List(sub), forward)
+      HeuristicOneStep(synth, p, List(sub), forward)
     } else {
       RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala b/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala
index 04d38f5fe..60c066e5f 100644
--- a/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala
+++ b/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala
@@ -38,7 +38,7 @@ class SelectiveInlining(synth: Synthesizer) extends Rule("Sel. Inlining", synth,
 
       val sub = p.copy(phi = And(newExprs))
 
-      HeuristicStep(synth, p, List(sub), forward)
+      HeuristicOneStep(synth, p, List(sub), forward)
     } else {
       RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/rules/ADTDual.scala b/src/main/scala/leon/synthesis/rules/ADTDual.scala
index 5cf2bdb3b..37d6cf63f 100644
--- a/src/main/scala/leon/synthesis/rules/ADTDual.scala
+++ b/src/main/scala/leon/synthesis/rules/ADTDual.scala
@@ -26,7 +26,7 @@ class ADTDual(synth: Synthesizer) extends Rule("ADTDual", synth, 200) {
     if (!toRemove.isEmpty) {
       val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq))
 
-      RuleStep(List(sub), forward)
+      RuleOneStep(List(sub), forward)
     } else {
       RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/rules/ADTSplit.scala b/src/main/scala/leon/synthesis/rules/ADTSplit.scala
index f4a90086a..6d43a4a52 100644
--- a/src/main/scala/leon/synthesis/rules/ADTSplit.scala
+++ b/src/main/scala/leon/synthesis/rules/ADTSplit.scala
@@ -72,7 +72,7 @@ class ADTSplit(synth: Synthesizer) extends Rule("ADT Split.", synth, 70) {
             Solution(Or(globalPre), sols.flatMap(_.defs).toSet, MatchExpr(Variable(id), cases))
         }
 
-        HeuristicStep(synth, p, subInfo.map(_._2).toList, onSuccess)
+        RuleOneStep(subInfo.map(_._2).toList, onSuccess)
       case _ =>
         RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/rules/Assert.scala b/src/main/scala/leon/synthesis/rules/Assert.scala
index cfd5f5a92..814632bce 100644
--- a/src/main/scala/leon/synthesis/rules/Assert.scala
+++ b/src/main/scala/leon/synthesis/rules/Assert.scala
@@ -22,7 +22,7 @@ class Assert(synth: Synthesizer) extends Rule("Assert", synth, 200) {
           } else {
             val sub = p.copy(c = And(p.c +: exprsA), phi = And(others))
 
-            RuleStep(List(sub), forward)
+            RuleOneStep(List(sub), forward)
           }
         } else {
           RuleInapplicable
diff --git a/src/main/scala/leon/synthesis/rules/CaseSplit.scala b/src/main/scala/leon/synthesis/rules/CaseSplit.scala
index a99b7a361..dcd7e6835 100644
--- a/src/main/scala/leon/synthesis/rules/CaseSplit.scala
+++ b/src/main/scala/leon/synthesis/rules/CaseSplit.scala
@@ -19,7 +19,7 @@ class CaseSplit(synth: Synthesizer) extends Rule("Case-Split", synth, 200) {
           case _ => Solution.none
         }
 
-        RuleStep(List(sub1, sub2), onSuccess)
+        RuleOneStep(List(sub1, sub2), onSuccess)
       case _ =>
         RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/rules/EqualitySplit.scala b/src/main/scala/leon/synthesis/rules/EqualitySplit.scala
index 04d4f8bf7..fe0214362 100644
--- a/src/main/scala/leon/synthesis/rules/EqualitySplit.scala
+++ b/src/main/scala/leon/synthesis/rules/EqualitySplit.scala
@@ -50,7 +50,7 @@ class EqualitySplit(synth: Synthesizer) extends Rule("Eq. Split.", synth, 90) {
             Solution.none
         }
 
-        RuleStep(List(sub1, sub2), onSuccess)
+        RuleOneStep(List(sub1, sub2), onSuccess)
       case _ =>
         RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
index 4ea9e93f5..bb2005f9d 100644
--- a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
+++ b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala
@@ -62,7 +62,7 @@ class IntegerEquation(synth: Synthesizer) extends Rule("Integer Equation", synth
             case _ => Solution.none
           }
 
-          RuleStep(List(newProblem), onSuccess)
+          RuleOneStep(List(newProblem), onSuccess)
 
         } else {
           val (eqPre0, eqWitness, freshxs) = elimVariable(eqas, normalizedEq)
@@ -105,7 +105,7 @@ class IntegerEquation(synth: Synthesizer) extends Rule("Integer Equation", synth
             case _ => Solution.none
           }
 
-          RuleStep(List(newProblem), onSuccess)
+          RuleOneStep(List(newProblem), onSuccess)
         }
 
 
diff --git a/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala b/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala
index e15291969..760891e0c 100644
--- a/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala
+++ b/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala
@@ -161,7 +161,7 @@ class IntegerInequalities(synth: Synthesizer) extends Rule("Integer Inequalities
           case _ => Solution.none
         }
 
-        RuleStep(List(subProblem), onSuccess)
+        RuleOneStep(List(subProblem), onSuccess)
       }
     }
   }
diff --git a/src/main/scala/leon/synthesis/rules/OnePoint.scala b/src/main/scala/leon/synthesis/rules/OnePoint.scala
index af047f3cb..1ee39b2d1 100644
--- a/src/main/scala/leon/synthesis/rules/OnePoint.scala
+++ b/src/main/scala/leon/synthesis/rules/OnePoint.scala
@@ -38,7 +38,7 @@ class OnePoint(synth: Synthesizer) extends Rule("One-point", synth, 300) {
         case _ => Solution.none
       }
 
-      RuleStep(List(newProblem), onSuccess)
+      RuleOneStep(List(newProblem), onSuccess)
     } else {
       RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala
index 0a24dff40..a804d9cfc 100644
--- a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala
+++ b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala
@@ -21,7 +21,7 @@ class UnconstrainedOutput(synth: Synthesizer) extends Rule("Unconstr.Output", sy
           Solution.none
       }
 
-      RuleStep(List(sub), onSuccess)
+      RuleOneStep(List(sub), onSuccess)
     } else {
       RuleInapplicable
     }
diff --git a/src/main/scala/leon/synthesis/rules/Unification.scala b/src/main/scala/leon/synthesis/rules/Unification.scala
index 7817d63d9..90a5c8741 100644
--- a/src/main/scala/leon/synthesis/rules/Unification.scala
+++ b/src/main/scala/leon/synthesis/rules/Unification.scala
@@ -29,7 +29,7 @@ object Unification {
         val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq))
 
 
-        RuleStep(List(sub), forward)
+        RuleOneStep(List(sub), forward)
       } else {
         RuleInapplicable
       }
diff --git a/src/main/scala/leon/synthesis/rules/UnusedInput.scala b/src/main/scala/leon/synthesis/rules/UnusedInput.scala
index cc9c35d6d..e9e0c18d6 100644
--- a/src/main/scala/leon/synthesis/rules/UnusedInput.scala
+++ b/src/main/scala/leon/synthesis/rules/UnusedInput.scala
@@ -14,7 +14,7 @@ class UnusedInput(synth: Synthesizer) extends Rule("UnusedInput", synth, 100) {
     if (!unused.isEmpty) {
       val sub = p.copy(as = p.as.filterNot(unused))
 
-      RuleStep(List(sub), forward)
+      RuleOneStep(List(sub), forward)
     } else {
       RuleInapplicable
     }
diff --git a/testcases/synthesis/BinaryTree.scala b/testcases/synthesis/BinaryTree.scala
index 02b4030fe..7409a2745 100644
--- a/testcases/synthesis/BinaryTree.scala
+++ b/testcases/synthesis/BinaryTree.scala
@@ -15,4 +15,12 @@ object BinaryTree {
   def delete(in : Tree, v : Int) = choose { (out : Tree) =>
     content(out) == (content(in) -- Set(v))
   }
+
+  def deleteSubProblem(left : Tree, leftR: Tree, value: Int, rightRleft: Tree, rightRvalue: Int, rightRright: Tree, rightR: Tree, right: Tree, toRemove : Int) = choose { (out : Tree) =>
+    content(out) == (content(Node(left, value, right)) -- Set(toRemove)) && content(leftR) == (content(left) -- Set(toRemove)) && rightR == Node(rightRleft, rightRvalue, rightRright) && content(Node(rightRleft, rightRvalue, rightRright)) == (content(right) -- Set(toRemove)) && value == toRemove
+  }
+
+  def deleteSubProblem2(left : Tree, leftR: Tree, value: Int, rightRleft: Tree, rightRvalue: Int, rightRright: Tree, right: Tree, toRemove : Int) = choose { (out : Tree) =>
+    content(out) == (content(Node(left, value, right)) -- Set(toRemove)) && content(leftR) == (content(left) -- Set(toRemove)) && content(Node(rightRleft, rightRvalue, rightRright)) == (content(right) -- Set(toRemove)) && value == toRemove
+  }
 }
-- 
GitLab