From f76dd21de31ef78cda37d6d518808152a2d5d69e Mon Sep 17 00:00:00 2001
From: Nicolas Voirol <voirol.nicolas@gmail.com>
Date: Sat, 24 Jan 2015 14:09:23 +0100
Subject: [PATCH] Extended termination to HO functions

---
 src/main/scala/leon/purescala/TypeTrees.scala |  3 ++
 .../solvers/z3/Z3ModelReconstruction.scala    |  2 +-
 .../scala/leon/termination/ChainBuilder.scala | 12 +++---
 .../leon/termination/ChainComparator.scala    |  8 ++--
 .../leon/termination/ChainProcessor.scala     |  6 +--
 .../leon/termination/LoopProcessor.scala      |  6 +--
 .../leon/termination/RelationBuilder.scala    | 14 +++----
 .../leon/termination/RelationProcessor.scala  |  4 +-
 .../scala/leon/termination/Strengthener.scala | 26 ++++++-------
 .../leon/termination/StructuralSize.scala     |  8 ++--
 .../termination/looping/Looping1.scala.BAK    | 33 ----------------
 .../termination/looping/Looping2.scala.BAK    | 32 ---------------
 .../termination/looping/Looping3.scala.BAK    | 32 ---------------
 .../termination/valid/HOTermination.scala     | 39 +++++++++++++++++++
 .../termination/valid/HardChains.scala.BAK    | 37 ------------------
 .../verification/purescala/valid/Lists2.scala | 19 +++++++++
 16 files changed, 103 insertions(+), 178 deletions(-)
 delete mode 100644 src/test/resources/regression/termination/looping/Looping1.scala.BAK
 delete mode 100644 src/test/resources/regression/termination/looping/Looping2.scala.BAK
 delete mode 100644 src/test/resources/regression/termination/looping/Looping3.scala.BAK
 create mode 100644 src/test/resources/regression/termination/valid/HOTermination.scala
 delete mode 100644 src/test/resources/regression/termination/valid/HardChains.scala.BAK

diff --git a/src/main/scala/leon/purescala/TypeTrees.scala b/src/main/scala/leon/purescala/TypeTrees.scala
index 16274e086..6b283b8ef 100644
--- a/src/main/scala/leon/purescala/TypeTrees.scala
+++ b/src/main/scala/leon/purescala/TypeTrees.scala
@@ -99,6 +99,9 @@ object TypeTrees {
       if (tmap.isEmpty) {
         classDef.fields
       } else {
+        // !! WARNING !!
+        // vd.id.getType will NOT match vd.tpe, but we kind of need this for selectorID2Index...
+        // See with Etienne about changing this!
         classDef.fields.map(vd => ValDef(vd.id, instantiateType(vd.tpe, tmap)))
       }
     }
diff --git a/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala b/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala
index 21f829502..5953fc422 100644
--- a/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala
+++ b/src/main/scala/leon/solvers/z3/Z3ModelReconstruction.scala
@@ -37,7 +37,7 @@ trait Z3ModelReconstruction {
 
     def completeID(id : Identifier) : Unit = {
       asMap = asMap + ((id -> simplestValue(id.getType)))
-      reporter.info("Completing variable '" + id + "' to simplest value")
+      reporter.debug("Completing variable '" + id + "' to simplest value")
     }
 
     for(id <- ids) {
diff --git a/src/main/scala/leon/termination/ChainBuilder.scala b/src/main/scala/leon/termination/ChainBuilder.scala
index 26c557608..5bbfc7cc9 100644
--- a/src/main/scala/leon/termination/ChainBuilder.scala
+++ b/src/main/scala/leon/termination/ChainBuilder.scala
@@ -31,20 +31,20 @@ final case class Chain(relations: List[Relation]) {
   lazy val size: Int = relations.size
 
   private lazy val inlining : Seq[(Seq[ValDef], Expr)] = {
-    def rec(list: List[Relation], funDef: TypedFunDef, subst: Map[Identifier, Expr]): Seq[(Seq[ValDef], Expr)] = list match {
-      case Relation(_, _, fi @ FunctionInvocation(fitfd, args), _) :: xs =>
+    def rec(list: List[Relation], funDef: TypedFunDef, args: Seq[Expr]): Seq[(Seq[ValDef], Expr)] = list match {
+      case Relation(_, _, fi @ FunctionInvocation(fitfd, nextArgs), _) :: xs =>
         val tfd = TypedFunDef(fitfd.fd, fitfd.tps.map(funDef.translated(_)))
+        val subst = (tfd.params.map(_.id) zip args).toMap
         val expr = replaceFromIDs(subst, hoistIte(expandLets(matchToIfThenElse(tfd.body.get))))
+        val mappedArgs = nextArgs.map(e => replaceFromIDs(subst, tfd.translated(e)))
 
-        val mappedArgs = args.map(e => replaceFromIDs(subst, tfd.translated(e)))
-        val newSubst = (tfd.params.map(_.id) zip mappedArgs).toMap
-        (tfd.params, expr) +: rec(xs, tfd, newSubst)
+        (tfd.params, expr) +: rec(xs, tfd, mappedArgs)
       case Nil => Seq.empty
     }
 
     val body = hoistIte(expandLets(matchToIfThenElse(funDef.body.get)))
     val tfd = funDef.typed(funDef.tparams.map(_.tp))
-    (tfd.params, body) +: rec(relations, tfd, funDef.params.map(arg => arg.id -> arg.toVariable).toMap)
+    (tfd.params, body) +: rec(relations, tfd, tfd.params.map(_.toVariable))
   }
 
   lazy val finalParams : Seq[ValDef] = inlining.last._1
diff --git a/src/main/scala/leon/termination/ChainComparator.scala b/src/main/scala/leon/termination/ChainComparator.scala
index 743db6335..91958f60d 100644
--- a/src/main/scala/leon/termination/ChainComparator.scala
+++ b/src/main/scala/leon/termination/ChainComparator.scala
@@ -184,9 +184,11 @@ trait ChainComparator { self : StructuralSize with TerminationChecker =>
       case _ => NoEndpoint
     }
 
-    cluster.foldLeft[NumericEndpoint](AnyEndpoint)((acc, chain) => {
-      acc min chain.inlined.foldLeft[NumericEndpoint](NoEndpoint)((acc, expr) => acc max endpoint(expr))
-    })
+    cluster.foldLeft[NumericEndpoint](AnyEndpoint) { (acc, chain) =>
+      acc min chain.inlined.foldLeft[NumericEndpoint](NoEndpoint) { (acc, expr) =>
+        acc max endpoint(expr)
+      }
+    }
   }
 
   def numericConverging(e1: Expr, e2s: Seq[(Seq[Expr], Expr)], cluster: Set[Chain]) : Seq[Expr] = flatType(e1.getType).toSeq.flatMap {
diff --git a/src/main/scala/leon/termination/ChainProcessor.scala b/src/main/scala/leon/termination/ChainProcessor.scala
index 0c4e1608a..d0e51f24b 100644
--- a/src/main/scala/leon/termination/ChainProcessor.scala
+++ b/src/main/scala/leon/termination/ChainProcessor.scala
@@ -20,8 +20,8 @@ class ChainProcessor(val checker: TerminationChecker with ChainBuilder with Chai
     reporter.debug("- Strengthening postconditions")
     checker.strengthenPostconditions(problem.funDefs)(this)
 
-//    reporter.debug("- Strengthening applications")
-//    checker.strengthenApplications(problem.funDefs)(this)
+    reporter.debug("- Strengthening applications")
+    checker.strengthenApplications(problem.funDefs)(this)
 
     reporter.debug("- Running ChainBuilder")
     val chainsMap : Map[FunDef, (Set[FunDef], Set[Chain])] = problem.funDefs.map { funDef =>
@@ -37,8 +37,6 @@ class ChainProcessor(val checker: TerminationChecker with ChainBuilder with Chai
 
       def exprs(fd: FunDef): (Expr, Seq[(Seq[Expr], Expr)], Set[Chain]) = {
         val fdChains = chainsMap(fd)._2
-        val nfdChains = chainsMap.filter(_._1 != fd).values.foldLeft(Set.empty[Chain])((set, p) => set ++ p._2)
-        assert(nfdChains.subsetOf(fdChains))
 
         val e1 = Tuple(fd.params.map(_.toVariable))
         val e2s = fdChains.toSeq.map { chain =>
diff --git a/src/main/scala/leon/termination/LoopProcessor.scala b/src/main/scala/leon/termination/LoopProcessor.scala
index 7ff9039af..1f28c405c 100644
--- a/src/main/scala/leon/termination/LoopProcessor.scala
+++ b/src/main/scala/leon/termination/LoopProcessor.scala
@@ -15,8 +15,8 @@ class LoopProcessor(val checker: TerminationChecker with ChainBuilder with Stren
   val name: String = "Loop Processor"
 
   def run(problem: Problem) = {
-//    reporter.debug("- Strengthening applications")
-//    checker.strengthenApplications(problem.funDefs)(this)
+    reporter.debug("- Strengthening applications")
+    checker.strengthenApplications(problem.funDefs)(this)
 
     reporter.debug("- Running ChainBuilder")
     val chains : Set[Chain] = problem.funDefs.flatMap(fd => checker.getChains(fd)(this)._2)
@@ -38,7 +38,7 @@ class LoopProcessor(val checker: TerminationChecker with ChainBuilder with Stren
         definitiveSATwithModel(And(path :+ Equals(srcTuple, resTuple))) match {
           case Some(map) =>
             val args = chain.funDef.params.map(arg => map(arg.id))
-            val res = if (chain.relations.exists(_.inAnon)) MaybeBroken(chain.funDef, args) else Broken(chain.funDef, args)
+            val res = if (chain.relations.exists(_.inLambda)) MaybeBroken(chain.funDef, args) else Broken(chain.funDef, args)
             nonTerminating(chain.funDef) = res
           case None =>
         }
diff --git a/src/main/scala/leon/termination/RelationBuilder.scala b/src/main/scala/leon/termination/RelationBuilder.scala
index a597ebde8..c9acd8ccf 100644
--- a/src/main/scala/leon/termination/RelationBuilder.scala
+++ b/src/main/scala/leon/termination/RelationBuilder.scala
@@ -11,8 +11,8 @@ import purescala.Definitions._
 
 import scala.collection.mutable.{Map => MutableMap}
 
-final case class Relation(funDef: FunDef, path: Seq[Expr], call: FunctionInvocation, inAnon: Boolean) {
-  override def toString : String = "Relation(" + funDef.id + "," + path + ", " + call.tfd.id + call.args.mkString("(",",",")") + "," + inAnon + ")"
+final case class Relation(funDef: FunDef, path: Seq[Expr], call: FunctionInvocation, inLambda: Boolean) {
+  override def toString : String = "Relation(" + funDef.id + "," + path + ", " + call.tfd.id + call.args.mkString("(",",",")") + "," + inLambda + ")"
 }
 
 trait RelationBuilder { self: TerminationChecker with Strengthener =>
@@ -30,13 +30,13 @@ trait RelationBuilder { self: TerminationChecker with Strengthener =>
     case Some((relations, signature)) if signature == funDefRelationSignature(funDef) => relations
     case _ => {
       val collector = new CollectorWithPaths[Relation] {
-        var inAnon: Boolean = false
+        var inLambda: Boolean = false
         def collect(e: Expr, path: Seq[Expr]): Option[Relation] = e match {
           case fi @ FunctionInvocation(f, args) if self.functions(f.fd) =>
-            Some(Relation(funDef, path, fi, inAnon))
-//          case af @ AnonymousFunction(args, body) =>
-//            inAnon = true
-//            None
+            Some(Relation(funDef, path, fi, inLambda))
+          case l @ Lambda(args, body) =>
+            inLambda = true
+            None
           case _ => None
         }
 
diff --git a/src/main/scala/leon/termination/RelationProcessor.scala b/src/main/scala/leon/termination/RelationProcessor.scala
index 4982968e2..4908ec1d4 100644
--- a/src/main/scala/leon/termination/RelationProcessor.scala
+++ b/src/main/scala/leon/termination/RelationProcessor.scala
@@ -20,8 +20,8 @@ class RelationProcessor(
     reporter.debug("- Strengthening postconditions")
     checker.strengthenPostconditions(problem.funDefs)(this)
 
-//    reporter.debug("- Strengthening applications")
-//    checker.strengthenApplications(problem.funDefs)(this)
+    reporter.debug("- Strengthening applications")
+    checker.strengthenApplications(problem.funDefs)(this)
 
     val formulas = problem.funDefs.map({ funDef =>
       funDef -> checker.getRelations(funDef).collect({
diff --git a/src/main/scala/leon/termination/Strengthener.scala b/src/main/scala/leon/termination/Strengthener.scala
index 31f076498..a827162ca 100644
--- a/src/main/scala/leon/termination/Strengthener.scala
+++ b/src/main/scala/leon/termination/Strengthener.scala
@@ -60,7 +60,6 @@ trait Strengthener { self : TerminationChecker with RelationComparator with Rela
   case object WeakDecreasing extends SizeConstraint
   case object NoConstraint extends SizeConstraint
 
-  /*
   private val strengthenedApp : MutableSet[FunDef] = MutableSet.empty
 
   protected def strengthened(fd: FunDef): Boolean = strengthenedApp(fd)
@@ -68,7 +67,7 @@ trait Strengthener { self : TerminationChecker with RelationComparator with Rela
   private val appConstraint   : MutableMap[(FunDef, Identifier), SizeConstraint] = MutableMap.empty
 
   def applicationConstraint(fd: FunDef, id: Identifier, arg: Expr, args: Seq[Expr]): Expr = arg match {
-    case AnonymousFunction(fargs, body) => appConstraint.get(fd -> id) match {
+    case Lambda(fargs, body) => appConstraint.get(fd -> id) match {
       case Some(StrongDecreasing) => self.sizeDecreasing(Tuple(args), Tuple(fargs.map(_.toVariable)))
       case Some(WeakDecreasing) => self.softDecreasing(Tuple(args), Tuple(fargs.map(_.toVariable)))
       case _ => BooleanLiteral(true)
@@ -84,14 +83,14 @@ trait Strengthener { self : TerminationChecker with RelationComparator with Rela
 
       val appCollector = new CollectorWithPaths[(Identifier,Expr,Expr)] {
         def collect(e: Expr, path: Seq[Expr]): Option[(Identifier, Expr, Expr)] = e match {
-          case FunctionApplication(Variable(id), args) => Some((id, And(path), Tuple(args)))
+          case Application(Variable(id), args) => Some((id, And(path), Tuple(args)))
           case _ => None
         }
       }
 
       val applications = appCollector.traverse(funDef).distinct
 
-      val funDefArgTuple = Tuple(funDef.args.map(_.toVariable))
+      val funDefArgTuple = Tuple(funDef.params.map(_.toVariable))
 
       val allFormulas = for ((id, path, appArgs) <- applications) yield {
         val soft = Implies(path, self.softDecreasing(funDefArgTuple, appArgs))
@@ -102,8 +101,8 @@ trait Strengthener { self : TerminationChecker with RelationComparator with Rela
       val formulaMap = allFormulas.groupBy(_._1).mapValues(_.map(_._2).unzip)
 
       val constraints = for ((id, (weakFormulas, strongFormulas)) <- formulaMap) yield id -> {
-        if (solver.definitiveALL(And(weakFormulas.toSeq), funDef.precondition)) {
-          if (solver.definitiveALL(And(strongFormulas.toSeq), funDef.precondition)) {
+        if (solver.definitiveALL(And(weakFormulas.toSeq))) {
+          if (solver.definitiveALL(And(strongFormulas.toSeq))) {
             StrongDecreasing
           } else {
             WeakDecreasing
@@ -113,13 +112,13 @@ trait Strengthener { self : TerminationChecker with RelationComparator with Rela
         }
       }
 
-      val funDefHOArgs = funDef.args.map(_.id).filter(_.getType.isInstanceOf[FunctionType]).toSet
+      val funDefHOArgs = funDef.params.map(_.id).filter(_.getType.isInstanceOf[FunctionType]).toSet
 
       val fiCollector = new CollectorWithPaths[(Expr, Expr, Seq[(Identifier,(FunDef, Identifier))])] {
         def collect(e: Expr, path: Seq[Expr]): Option[(Expr, Expr, Seq[(Identifier,(FunDef, Identifier))])] = e match {
           case FunctionInvocation(fd, args) if (funDefHOArgs intersect args.collect({ case Variable(id) => id }).toSet).nonEmpty =>
-            Some((And(path), Tuple(args), (args zip fd.args).collect {
-              case (Variable(id), vd) if funDefHOArgs(id) => id -> ((fd, vd.id))
+            Some((And(path), Tuple(args), (args zip fd.params).collect {
+              case (Variable(id), vd) if funDefHOArgs(id) => id -> ((fd.fd, vd.id))
             }))
           case _ => None
         }
@@ -145,14 +144,14 @@ trait Strengthener { self : TerminationChecker with RelationComparator with Rela
               case (_, None) => NoConstraint
               case (NoConstraint, _) => NoConstraint
               case (StrongDecreasing | WeakDecreasing, Some(StrongDecreasing)) =>
-                if (solver.definitiveALL(weakFormula, funDef.precondition)) StrongDecreasing
+                if (solver.definitiveALL(weakFormula)) StrongDecreasing
                 else NoConstraint
               case (StrongDecreasing, Some(WeakDecreasing)) =>
-                if (solver.definitiveALL(strongFormula, funDef.precondition)) StrongDecreasing
-                else if (solver.definitiveALL(weakFormula, funDef.precondition)) WeakDecreasing
+                if (solver.definitiveALL(strongFormula)) StrongDecreasing
+                else if (solver.definitiveALL(weakFormula)) WeakDecreasing
                 else NoConstraint
               case (WeakDecreasing, Some(WeakDecreasing)) =>
-                if (solver.definitiveALL(weakFormula, funDef.precondition)) WeakDecreasing
+                if (solver.definitiveALL(weakFormula)) WeakDecreasing
                 else NoConstraint
             }
         }
@@ -167,7 +166,6 @@ trait Strengthener { self : TerminationChecker with RelationComparator with Rela
       strengthenedApp += funDef
     }
   }
-  */
 }
 
 
diff --git a/src/main/scala/leon/termination/StructuralSize.scala b/src/main/scala/leon/termination/StructuralSize.scala
index e9753151b..fe966ed49 100644
--- a/src/main/scala/leon/termination/StructuralSize.scala
+++ b/src/main/scala/leon/termination/StructuralSize.scala
@@ -31,7 +31,7 @@ trait StructuralSize {
       sizeCache.get(argumentType) match {
         case Some(fd) => fd
         case None =>
-          val argument = ValDef(FreshIdentifier("x").setType(argumentType), argumentType)
+          val argument = ValDef(FreshIdentifier("x", true).setType(argumentType), argumentType)
           val formalTParams = typeParams.map(TypeParameterDef(_))
           val fd = new FunDef(FreshIdentifier("size", true), formalTParams, Int32Type, Seq(argument), DefType.MethodDef)
           sizeCache(argumentType) = fd
@@ -48,9 +48,9 @@ trait StructuralSize {
 
     def caseClassType2MatchCase(_c: ClassType): MatchCase = {
       val c = _c.asInstanceOf[CaseClassType] // required by leon framework
-      val arguments = c.fields.map(f => f -> f.id.freshen)
-      val argumentPatterns = arguments.map(p => WildcardPattern(Some(p._2)))
-      val sizes = arguments.map(p => size(Variable(p._2)))
+      val arguments = c.fields.map(vd => FreshIdentifier(vd.id.name).setType(vd.tpe))
+      val argumentPatterns = arguments.map(id => WildcardPattern(Some(id)))
+      val sizes = arguments.map(id => size(Variable(id)))
       val result = sizes.foldLeft[Expr](IntLiteral(1))(Plus(_,_))
       SimpleCase(CaseClassPattern(None, c, argumentPatterns), result)
     }
diff --git a/src/test/resources/regression/termination/looping/Looping1.scala.BAK b/src/test/resources/regression/termination/looping/Looping1.scala.BAK
deleted file mode 100644
index 04affc481..000000000
--- a/src/test/resources/regression/termination/looping/Looping1.scala.BAK
+++ /dev/null
@@ -1,33 +0,0 @@
-import leon.lang._
-
-object ComplexChains {
-  
-  abstract class List
-  case class Cons(head: Int, tail: List) extends List
-  case class Nil extends List
-
-  def looping1(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => looping2(tail)
-    case Cons(head, tail) if head < 0 => looping3(tail)
-    case _ => looping4(Cons(1, list))
-  }
-
-  def looping2(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => looping1(tail)
-    case _ => looping4(list)
-  }
-
-  def looping3(list: List): List = list match {
-    case Cons(head, tail) if head < 0 => looping1(tail)
-    case _ => looping2(Cons(1, list))
-  }
-
-  def looping4(list: List): List = list match {
-    case Cons(head, tail) if head == 0 => looping4(tail)
-    case Cons(_, Cons(_, tail)) => looping1(Cons(1, list))
-    case Cons(_, tail) => looping3(Nil())
-    case Nil() => Nil()
-  }
-}
-
-// vim: set ts=4 sw=4 et:
diff --git a/src/test/resources/regression/termination/looping/Looping2.scala.BAK b/src/test/resources/regression/termination/looping/Looping2.scala.BAK
deleted file mode 100644
index ac3cf66ae..000000000
--- a/src/test/resources/regression/termination/looping/Looping2.scala.BAK
+++ /dev/null
@@ -1,32 +0,0 @@
-import leon.lang._
-
-object ComplexChains {
-  
-  abstract class List
-  case class Cons(head: Int, tail: List) extends List
-  case class Nil extends List
-
-  def looping1(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => calling2(list)
-    case Cons(head, tail) if head < 0 => calling3(list)
-    case _ => looping4(Cons(1, list))
-  }
-
-  def calling2(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => looping1(tail)
-    case _ => looping4(list)
-  }
-
-  def calling3(list: List): List = list match {
-    case Cons(head, tail) if head < 0 => looping1(tail)
-    case _ => looping4(list)
-  }
-
-  def looping4(list: List): List = list match {
-    case Cons(_, Cons(_, tail)) => looping1(tail)
-    case Cons(_, tail) => looping1(tail)
-    case Nil() => calling2(Cons(1, Nil()))
-  }
-}
-
-// vim: set ts=4 sw=4 et:
diff --git a/src/test/resources/regression/termination/looping/Looping3.scala.BAK b/src/test/resources/regression/termination/looping/Looping3.scala.BAK
deleted file mode 100644
index 29b3c9f82..000000000
--- a/src/test/resources/regression/termination/looping/Looping3.scala.BAK
+++ /dev/null
@@ -1,32 +0,0 @@
-import leon.lang._
-
-object ComplexChains {
-  
-  abstract class List
-  case class Cons(head: Int, tail: List) extends List
-  case class Nil extends List
-
-  def looping1(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => looping2(list)
-    case Cons(head, tail) if head < 0 => looping3(list)
-    case _ => looping4(Cons(1, list))
-  }
-
-  def looping2(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => looping1(tail)
-    case _ => looping3(list)
-  }
-
-  def looping3(list: List): List = list match {
-    case Cons(head, tail) if head < 0 => looping1(tail)
-    case _ => looping2(list)
-  }
-
-  def looping4(list: List): List = list match {
-    case Cons(_, Cons(_, tail)) => looping1(tail)
-    case Cons(_, tail) => looping1(tail)
-    case Nil() => looping2(Cons(1, Nil()))
-  }
-}
-
-// vim: set ts=4 sw=4 et:
diff --git a/src/test/resources/regression/termination/valid/HOTermination.scala b/src/test/resources/regression/termination/valid/HOTermination.scala
new file mode 100644
index 000000000..f7e1b5694
--- /dev/null
+++ b/src/test/resources/regression/termination/valid/HOTermination.scala
@@ -0,0 +1,39 @@
+
+import leon.lang._
+
+object HOTermination {
+  abstract class List[T]
+  case class Cons[T](head: T, tail: List[T]) extends List[T]
+  case class Nil[T]() extends List[T]
+
+  abstract class Option[T]
+  case class Some[T](value: T) extends Option[T]
+  case class None[T]() extends Option[T]
+
+  def map[A,B](list: List[A], f: A => B): List[B] = list match {
+    case Cons(head, tail) => Cons(f(head), map(tail, f))
+    case Nil() => Nil()
+  }
+
+  abstract class Expr
+  case class Invocation(e: Expr, args: List[Expr]) extends Expr
+  case class Addition(e1: Expr, e2: Expr) extends Expr
+  case class Variable(i: Int) extends Expr
+
+  def transform(e: Expr, f: Expr => Option[Expr]): Expr = {
+    f(e) match {
+      case Some(newExpr) => newExpr
+      case None() => e match {
+        case Invocation(c, args) =>
+          val newC = transform(c, f)
+          val newArgs = map(args, (x: Expr) => transform(x, f))
+          Invocation(newC, newArgs)
+        case Addition(e1, e2) =>
+          Addition(transform(e1, f), transform(e2, f))
+        case Variable(i) => e
+      }
+    }
+  }
+}
+
+// vim: set ts=4 sw=4 et:
diff --git a/src/test/resources/regression/termination/valid/HardChains.scala.BAK b/src/test/resources/regression/termination/valid/HardChains.scala.BAK
deleted file mode 100644
index 9efb8304e..000000000
--- a/src/test/resources/regression/termination/valid/HardChains.scala.BAK
+++ /dev/null
@@ -1,37 +0,0 @@
-import leon.lang._
-
-object HardChains {
-  
-  abstract class List
-  case class Cons(head: Int, tail: List) extends List
-  case class Nil extends List
-
-  def f1(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => f2(list)
-    case Cons(head, tail) if head < 0 => f3(list)
-    case Nil() => Nil()
-    case _ => f4(list)
-  }
-
-  def f2(list: List): List = list match {
-    case Cons(head, tail) if head > 0 => f4(Cons(0, list))
-    case _ => f1(list)
-  }
-
-  def f3(list: List): List = list match {
-    case Cons(head, tail) if head < 0 => f4(Cons(0, list))
-    case Cons(head, tail) if head == 0 => f2(tail)
-    case _ => f4(list)
-  }
-
-  def f4(list: List): List = list match {
-    case Cons(head, tail) if head == 0 => f3(tail)
-    case Cons(head, Cons(head2, Cons(head3, tail))) => tail
-    case Cons(head, Cons(head2, tail)) => tail
-    case Cons(head, tail) if head > 0 => f4(Cons(0, tail))
-    case Cons(head, tail) => f4(tail)
-    case _ => f1(list)
-  }
-}
-
-// vim: set ts=4 sw=4 et:
diff --git a/src/test/resources/regression/verification/purescala/valid/Lists2.scala b/src/test/resources/regression/verification/purescala/valid/Lists2.scala
index da096d1d3..654046ba0 100644
--- a/src/test/resources/regression/verification/purescala/valid/Lists2.scala
+++ b/src/test/resources/regression/verification/purescala/valid/Lists2.scala
@@ -25,6 +25,25 @@ object Lists2 {
       case Cons(head, tail) => positive_lemma(list) && positive_lemma_induct(tail)
     }
   }.holds
+
+  def remove[T](list: List[T], e: T) : List[T] = {
+    list match {
+      case Nil() => Nil()
+      case Cons(x, xs) if x == e => remove(xs, e)
+      case Cons(x, xs)           => Cons(x, remove(xs, e))
+    }
+  } //ensuring { (res: List[T]) => forall(res, (x : T) => x != e) }
+
+  def remove_lemma[T](list: List[T], e: T): Boolean = {
+    forall(remove(list, e), (x : T) => x != e)
+  }
+
+  def remove_lemma_induct[T](list: List[T], e: T): Boolean = {
+    list match {
+      case Nil() => remove_lemma(list, e)
+      case Cons(head, tail) => remove_lemma(list, e) && remove_lemma_induct(tail, e)
+    }
+  }.holds
 }
 
 // vim: set ts=4 sw=4 et:
-- 
GitLab