diff --git a/library/lazy/package.scala b/library/lazy/package.scala index f329b589abb38457cd8f53e1ebe15209c311f201..7c0f575828c3bd96cb32c069c43e1444373075a5 100644 --- a/library/lazy/package.scala +++ b/library/lazy/package.scala @@ -1,32 +1,39 @@ /* Copyright 2009-2013 EPFL, Lausanne */ -package leon.lazyeval +package leon -import leon.annotation._ -import leon.lang._ +import annotation._ +import lang._ import scala.language.implicitConversions import scala.annotation.StaticAnnotation -@library -object $ { - def apply[T](f: => T) = new $(Unit => f) - def eager[T](x: => T) = new $(Unit => x) +package object lazyeval { + + @library + def $[T](f: => T) = Lazy(Unit => f) + //def apply[T](f: => T) = new $(Unit => f) + + @library + def eager[T](x: => T) = Lazy(Unit => x) + /** * implicit conversion from eager evaluation to lazy evaluation */ + @library @inline implicit def eagerToLazy[T](x: T) = eager(x) /** - * accessing in and out states. + * For accessing in and out states. * Should be used only in ensuring. - * TODO: enforce this. */ + @library @extern - def inState[T] : Set[$[T]] = sys.error("inState method is not executable!") + def inState[T]: Set[Lazy[T]] = sys.error("inState method is not executable!") + @library @extern - def outState[T] : Set[$[T]] = sys.error("outState method is not executable") + def outState[T]: Set[Lazy[T]] = sys.error("outState method is not executable") /** * Helper class for invoking with a given state instead of the implicit state @@ -34,30 +41,23 @@ object $ { @library case class WithState[T](v: T) { @extern - def withState[U](u: Set[$[U]]): T = sys.error("withState method is not executable!") + def withState[U](u: Set[Lazy[U]]): T = sys.error("withState method is not executable!") @extern - def withState[U, V](u: Set[$[U]], v: Set[$[V]]): T = sys.error("withState method is not executable!") + def withState[U, V](u: Set[Lazy[U]], v: Set[Lazy[V]]): T = sys.error("withState method is not executable!") @extern - def withState[U, V, W](u: Set[$[U]], v: Set[$[V]], w: Set[$[W]]): T = sys.error("withState method is not executable!") + def withState[U, V, W](u: Set[Lazy[U]], v: Set[Lazy[V]], w: Set[Lazy[W]]): T = sys.error("withState method is not executable!") @extern - def withState[U, V, W, X](u: Set[$[U]], v: Set[$[V]], w: Set[$[W]], x: Set[$[X]]): T = sys.error("withState method is not executable!") + def withState[U, V, W, X](u: Set[Lazy[U]], v: Set[Lazy[V]], w: Set[Lazy[W]], x: Set[Lazy[X]]): T = sys.error("withState method is not executable!") // extend this to more arguments if needed } + @library @inline implicit def toWithState[T](x: T) = new WithState(x) - /* @library - case class Mem[T](v: T) { - @extern - def isCached: Boolean = sys.error("not implemented!") - } - @inline - implicit def toMem[T](x: T) = new Mem(x)*/ - /** * annotations for monotonicity proofs. * Note implemented as of now. @@ -67,72 +67,24 @@ object $ { */ @ignore class monoproof(methodname: String) extends StaticAnnotation -} - -@library -case class $[T](f: Unit => T) { - @extern - lazy val value = { - val x = f(()) - eval = true - x - } - def * = f(()) - - @ignore - var eval = false // is this thread safe ? - - @extern - def isEvaluated = eval - @extern - def isSuspension[T](f: T) : Boolean = sys.error("isSuspensionOf method is not executable") -} - -/** - * The following are set of methods meant for `Memoized` closures - */ -@library -object Mem { - @inline - implicit def toMem[T](x: T) = new Mem(x) - - /** - * accessing in and out states of mems - * Should be used only in ensuring. - * TODO: enforce this. - */ - @extern - def inState[T] : Set[Mem[T]] = sys.error("inState method is not executable!") - - @extern - def outState[T] : Set[Mem[T]] = sys.error("outState method is not executable") - - /** - * Helper class for invoking with a given state instead of the implicit state - */ @library - case class memWithState[T](v: T) { + case class Lazy[T](f: Unit => T) { @extern - def withState[U](u: Set[Mem[U]]): T = sys.error("withState method is not executable!") + lazy val value = { + val x = f(()) + eval = true + x + } + def * = f(()) - @extern - def withState[U, V](u: Set[Mem[U]], v: Set[Mem[V]]): T = sys.error("withState method is not executable!") + @ignore + var eval = false // is this thread safe ? @extern - def withState[U, V, W](u: Set[Mem[U]], v: Set[Mem[V]], w: Set[Mem[W]]): T = sys.error("withState method is not executable!") + def isEvaluated = eval @extern - def withState[U, V, W, X](u: Set[Mem[U]], v: Set[Mem[V]], w: Set[Mem[W]], x: Set[Mem[X]]): T = sys.error("withState method is not executable!") - // extend this to more arguments if needed + def isSuspension[T](f: T): Boolean = sys.error("isSuspensionOf method is not executable") } - - @inline - implicit def toWithState[T](x: T) = new memWithState(x) -} - -@library -case class Mem[T](v: T) { - @extern - def isCached: Boolean = sys.error("not implemented!") } \ No newline at end of file diff --git a/library/mem/package.scala b/library/mem/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..416b737acae9b34287dc0f54ee3e6202fe72721e --- /dev/null +++ b/library/mem/package.scala @@ -0,0 +1,56 @@ +/* Copyright 2009-2013 EPFL, Lausanne */ + +package leon + +import annotation._ +import lang._ +import scala.language.implicitConversions +import scala.annotation.StaticAnnotation + +package object mem { + + @library + @inline + implicit def toMem[T](x: T) = new Mem(x) + + /** + * accessing in and out states of mems + * Should be used only in ensuring. + */ + @library + @extern + def inState[T]: Set[Mem[T]] = sys.error("inState method is not executable!") + + @library + @extern + def outState[T]: Set[Mem[T]] = sys.error("outState method is not executable") + + /** + * Helper class for invoking with a given state instead of the implicit state + */ + @library + case class memWithState[T](v: T) { + @extern + def withState[U](u: Set[Mem[U]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V](u: Set[Mem[U]], v: Set[Mem[V]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V, W](u: Set[Mem[U]], v: Set[Mem[V]], w: Set[Mem[W]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V, W, X](u: Set[Mem[U]], v: Set[Mem[V]], w: Set[Mem[W]], x: Set[Mem[X]]): T = sys.error("withState method is not executable!") + // extend this to more arguments if needed + } + + @library + @inline + implicit def toWithState[T](x: T) = new memWithState(x) + + @library + case class Mem[T](v: T) { + @extern + def isCached: Boolean = sys.error("not implemented!") + } +} \ No newline at end of file diff --git a/library/stats/package.scala b/library/stats/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..17d6286b62c3e4de4be5d4c430ea74e26a3abef0 --- /dev/null +++ b/library/stats/package.scala @@ -0,0 +1,28 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import leon.annotation._ +import java.lang.management._ +import scala.collection.JavaConversions._ + +/** + * A collection of methods that can be used to collect run-time statistics about Leon programs. + * This is mostly used to test the resources properties of Leon programs + */ +package object stats { + @ignore + def timed[T](code: => T)(cont: Long => Unit): T = { + var t1 = System.currentTimeMillis() + val r = code + cont((System.currentTimeMillis() - t1)) + r + } + + @ignore + def withTime[T](code: => T): (T, Long) = { + var t1 = System.currentTimeMillis() + val r = code + (r, (System.currentTimeMillis() - t1)) + } +} diff --git a/libtest/build.sbt b/libtest/build.sbt new file mode 100644 index 0000000000000000000000000000000000000000..9e8ce202fd5b412d33ac2c8c4af85fbbfcaa78fc --- /dev/null +++ b/libtest/build.sbt @@ -0,0 +1,17 @@ +name := "LazyBenchmarkExecution" + +version := "1.0" + +organization := "ch.epfl.lara" + +scalaVersion := "2.11.7" + +fork in run := true + +unmanagedJars in Compile += file("lib/macmemo.jar") + +javaOptions in run ++= Seq("-Xmx5G", "-Xms3G", "-Xss500M") + +scalacOptions ++= Seq("-optimise") + +libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.5" diff --git a/libtest/project/Build.scala b/libtest/project/Build.scala new file mode 100644 index 0000000000000000000000000000000000000000..19f93de52fda4fc681b0fb28e0c97ca0593c0ebd --- /dev/null +++ b/libtest/project/Build.scala @@ -0,0 +1,35 @@ +import sbt._ +import Keys._ + +object BuildSettings { + val paradiseVersion = "2.1.0-M5" + val buildSettings = Defaults.defaultSettings ++ Seq( + resolvers += Resolver.sonatypeRepo("snapshots"), + resolvers += Resolver.sonatypeRepo("releases"), + addCompilerPlugin("org.scalamacros" % "paradise" % paradiseVersion cross CrossVersion.full) + ) +} + +object MyBuild extends Build { + import BuildSettings._ + + lazy val root = Project( + "root", + file("."), + settings = buildSettings + ) + +// aggregate (macros, benchmarks) +// +// lazy val macros = Project( +// "leon", +// file("src/main/scala/leon"), +// settings = buildSettings +// ) +// +// lazy val benchmarks = Project( +// "benchmark", +// file("src/main/scala/benchmarks"), +// settings = buildSettings +// ) dependsOn macros +} diff --git a/libtest/src/main/scala/leon/annotation/isabelle.scala b/libtest/src/main/scala/leon/annotation/isabelle.scala new file mode 100644 index 0000000000000000000000000000000000000000..bfb4b39ec1499f270907a861c4d339d82a2d963d --- /dev/null +++ b/libtest/src/main/scala/leon/annotation/isabelle.scala @@ -0,0 +1,31 @@ +package leon.annotation + +import scala.annotation.StaticAnnotation + +object isabelle { + + @ignore + class typ(name: String) extends StaticAnnotation + + @ignore + class constructor(name: String) extends StaticAnnotation + + @ignore + class function(term: String) extends StaticAnnotation + + @ignore + class script(name: String, source: String) extends StaticAnnotation + + @ignore + class proof(method: String, kind: String = "") extends StaticAnnotation + + @ignore + class fullBody() extends StaticAnnotation + + @ignore + class noBody() extends StaticAnnotation + + @ignore + class lemma(about: String) extends StaticAnnotation + +} diff --git a/libtest/src/main/scala/leon/annotation/package.scala b/libtest/src/main/scala/leon/annotation/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..a355a8557072fa277a025f97bb77ebff35302ce8 --- /dev/null +++ b/libtest/src/main/scala/leon/annotation/package.scala @@ -0,0 +1,43 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import scala.annotation.StaticAnnotation +import scala.language.experimental.macros + +package object annotation { + @ignore + class library extends StaticAnnotation + @ignore + class induct extends StaticAnnotation + @ignore + class traceInduct(name: String = "") extends StaticAnnotation + @ignore + class ignore extends StaticAnnotation + @ignore + class extern extends StaticAnnotation + @ignore + class inline extends StaticAnnotation + @ignore + class internal extends StaticAnnotation + + // Orb annotations + @ignore + class monotonic extends StaticAnnotation + @ignore + class compose extends StaticAnnotation + @ignore + class axiom extends StaticAnnotation + @ignore + class invstate extends StaticAnnotation + @ignore + class memoize extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro com.softwaremill.macmemo.memoizeMacro.impl + } + @ignore + class invisibleBody extends StaticAnnotation // do not unfold the body of the function + @ignore + class usePost extends StaticAnnotation // assume the post-condition while proving time bounds + @ignore + class unfoldFactor(f: Int=0) extends StaticAnnotation // 0 implies no bound on unfolding +} \ No newline at end of file diff --git a/libtest/src/main/scala/leon/collection/List.scala b/libtest/src/main/scala/leon/collection/List.scala new file mode 100644 index 0000000000000000000000000000000000000000..74a0f0c66e3bc23780076e16acdc6f7035c6986b --- /dev/null +++ b/libtest/src/main/scala/leon/collection/List.scala @@ -0,0 +1,897 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.collection + +import leon._ +import leon.lang._ +import leon.annotation._ +import leon.math._ +import leon.proof._ + +@library +@isabelle.typ(name = "List.list") +sealed abstract class List[T] { + + @isabelle.function(term = "Int.int o List.length") + def size: BigInt = (this match { + case Nil() => BigInt(0) + case Cons(h, t) => 1 + t.size + }) ensuring (_ >= 0) + + def length = size + + @isabelle.function(term = "List.list.set") + def content: Set[T] = this match { + case Nil() => Set() + case Cons(h, t) => Set(h) ++ t.content + } + + @isabelle.function(term = "List.member") + def contains(v: T): Boolean = (this match { + case Cons(h, t) => h == v || t.contains(v) + case Nil() => false + }) ensuring { _ == (content contains v) } + + @isabelle.function(term = "List.append") + def ++(that: List[T]): List[T] = (this match { + case Nil() => that + case Cons(x, xs) => Cons(x, xs ++ that) + }) ensuring { res => + (res.content == this.content ++ that.content) && + (res.size == this.size + that.size) && + (that != Nil[T]() || res == this) + } + + def head: T = { + require(this != Nil[T]()) + val Cons(h, _) = this + h + } + + def tail: List[T] = { + require(this != Nil[T]()) + val Cons(_, t) = this + t + } + + @isabelle.fullBody + def apply(index: BigInt): T = { + require(0 <= index && index < size) + if (index == BigInt(0)) { + head + } else { + tail(index-1) + } + } + + @isabelle.function(term = "%xs x. x # xs") + def ::(t:T): List[T] = Cons(t, this) + + @isabelle.function(term = "%xs x. xs @ [x]") + def :+(t:T): List[T] = { + this match { + case Nil() => Cons(t, this) + case Cons(x, xs) => Cons(x, xs :+ (t)) + } + } ensuring(res => (res.size == size + 1) && (res.content == content ++ Set(t))) + + @isabelle.function(term = "List.rev") + def reverse: List[T] = { + this match { + case Nil() => this + case Cons(x,xs) => xs.reverse :+ x + } + } ensuring (res => (res.size == size) && (res.content == content)) + + def take(i: BigInt): List[T] = { (this, i) match { + case (Nil(), _) => Nil[T]() + case (Cons(h, t), i) => + if (i <= BigInt(0)) { + Nil[T]() + } else { + Cons(h, t.take(i-1)) + } + }} ensuring { res => + res.content.subsetOf(this.content) && (res.size == ( + if (i <= 0) BigInt(0) + else if (i >= this.size) this.size + else i + )) + } + + def drop(i: BigInt): List[T] = { (this, i) match { + case (Nil(), _) => Nil[T]() + case (Cons(h, t), i) => + if (i <= BigInt(0)) { + Cons[T](h, t) + } else { + t.drop(i-1) + } + }} ensuring { res => + res.content.subsetOf(this.content) && (res.size == ( + if (i <= 0) this.size + else if (i >= this.size) BigInt(0) + else this.size - i + )) + } + + def slice(from: BigInt, to: BigInt): List[T] = { + require(0 <= from && from <= to && to <= size) + drop(from).take(to-from) + } + + def replace(from: T, to: T): List[T] = { this match { + case Nil() => Nil[T]() + case Cons(h, t) => + val r = t.replace(from, to) + if (h == from) { + Cons(to, r) + } else { + Cons(h, r) + } + }} ensuring { (res: List[T]) => + res.size == this.size && + res.content == ( + (this.content -- Set(from)) ++ + (if (this.content contains from) Set(to) else Set[T]()) + ) + } + + private def chunk0(s: BigInt, l: List[T], acc: List[T], res: List[List[T]], s0: BigInt): List[List[T]] = { + require(s > 0 && s0 >= 0) + l match { + case Nil() => + if (acc.size > 0) { + res :+ acc + } else { + res + } + case Cons(h, t) => + if (s0 == BigInt(0)) { + chunk0(s, t, Cons(h, Nil()), res :+ acc, s-1) + } else { + chunk0(s, t, acc :+ h, res, s0-1) + } + } + } + + def chunks(s: BigInt): List[List[T]] = { + require(s > 0) + + chunk0(s, this, Nil(), Nil(), s) + } + + @isabelle.function(term = "List.zip") + def zip[B](that: List[B]): List[(T, B)] = { (this, that) match { + case (Cons(h1, t1), Cons(h2, t2)) => + Cons((h1, h2), t1.zip(t2)) + case _ => + Nil[(T, B)]() + }} ensuring { _.size == ( + if (this.size <= that.size) this.size else that.size + )} + + @isabelle.function(term = "%xs x. removeAll x xs") + def -(e: T): List[T] = { this match { + case Cons(h, t) => + if (e == h) { + t - e + } else { + Cons(h, t - e) + } + case Nil() => + Nil[T]() + }} ensuring { res => + res.size <= this.size && + res.content == this.content -- Set(e) + } + + def --(that: List[T]): List[T] = { this match { + case Cons(h, t) => + if (that.contains(h)) { + t -- that + } else { + Cons(h, t -- that) + } + case Nil() => + Nil[T]() + }} ensuring { res => + res.size <= this.size && + res.content == this.content -- that.content + } + + def &(that: List[T]): List[T] = { this match { + case Cons(h, t) => + if (that.contains(h)) { + Cons(h, t & that) + } else { + t & that + } + case Nil() => + Nil[T]() + }} ensuring { res => + res.size <= this.size && + res.content == (this.content & that.content) + } + + def padTo(s: BigInt, e: T): List[T] = { (this, s) match { + case (_, s) if s <= 0 => + this + case (Nil(), s) => + Cons(e, Nil().padTo(s-1, e)) + case (Cons(h, t), s) => + Cons(h, t.padTo(s-1, e)) + }} ensuring { res => + if (s <= this.size) + res == this + else + res.size == s && + res.content == this.content ++ Set(e) + } + + def find(e: T): Option[BigInt] = { this match { + case Nil() => None[BigInt]() + case Cons(h, t) => + if (h == e) { + Some[BigInt](0) + } else { + t.find(e) match { + case None() => None[BigInt]() + case Some(i) => Some(i+1) + } + } + }} ensuring { res => !res.isDefined || (this.content contains e) } + + def init: List[T] = { + require(!isEmpty) + ((this : @unchecked) match { + case Cons(h, Nil()) => + Nil[T]() + case Cons(h, t) => + Cons[T](h, t.init) + }) + } ensuring ( (r: List[T]) => + r.size == this.size - 1 && + r.content.subsetOf(this.content) + ) + + def last: T = { + require(!isEmpty) + (this : @unchecked) match { + case Cons(h, Nil()) => h + case Cons(_, t) => t.last + } + } ensuring { this.contains _ } + + def lastOption: Option[T] = { this match { + case Cons(h, t) => + t.lastOption.orElse(Some(h)) + case Nil() => + None[T]() + }} ensuring { _.isDefined != this.isEmpty } + + def headOption: Option[T] = { this match { + case Cons(h, t) => + Some(h) + case Nil() => + None[T]() + }} ensuring { _.isDefined != this.isEmpty } + + def tailOption: Option[List[T]] = { this match { + case Cons(h, t) => + Some(t) + case Nil() => + None[List[T]]() + }} ensuring { _.isDefined != this.isEmpty } + + + def unique: List[T] = this match { + case Nil() => Nil() + case Cons(h, t) => + Cons(h, t.unique - h) + } + + def splitAt(e: T): List[List[T]] = split(Cons(e, Nil())) + + def split(seps: List[T]): List[List[T]] = this match { + case Cons(h, t) => + if (seps.contains(h)) { + Cons(Nil(), t.split(seps)) + } else { + val r = t.split(seps) + Cons(Cons(h, r.head), r.tail) + } + case Nil() => + Cons(Nil(), Nil()) + } + + def evenSplit: (List[T], List[T]) = { + val c = size/2 + (take(c), drop(c)) + } + + def splitAtIndex(index: BigInt) : (List[T], List[T]) = { this match { + case Nil() => (Nil[T](), Nil[T]()) + case Cons(h, rest) => { + if (index <= BigInt(0)) { + (Nil[T](), this) + } else { + val (left,right) = rest.splitAtIndex(index - 1) + (Cons[T](h,left), right) + } + } + }} ensuring { (res:(List[T],List[T])) => + res._1 ++ res._2 == this && + res._1 == take(index) && res._2 == drop(index) + } + + def updated(i: BigInt, y: T): List[T] = { + require(0 <= i && i < this.size) + this match { + case Cons(x, tail) if i == 0 => + Cons[T](y, tail) + case Cons(x, tail) => + Cons[T](x, tail.updated(i - 1, y)) + } + } + + private def insertAtImpl(pos: BigInt, l: List[T]): List[T] = { + require(0 <= pos && pos <= size) + if(pos == BigInt(0)) { + l ++ this + } else { + this match { + case Cons(h, t) => + Cons(h, t.insertAtImpl(pos-1, l)) + case Nil() => + l + } + } + } ensuring { res => + res.size == this.size + l.size && + res.content == this.content ++ l.content + } + + def insertAt(pos: BigInt, l: List[T]): List[T] = { + require(-pos <= size && pos <= size) + if(pos < 0) { + insertAtImpl(size + pos, l) + } else { + insertAtImpl(pos, l) + } + } ensuring { res => + res.size == this.size + l.size && + res.content == this.content ++ l.content + } + + def insertAt(pos: BigInt, e: T): List[T] = { + require(-pos <= size && pos <= size) + insertAt(pos, Cons[T](e, Nil())) + } ensuring { res => + res.size == this.size + 1 && + res.content == this.content ++ Set(e) + } + + private def replaceAtImpl(pos: BigInt, l: List[T]): List[T] = { + require(0 <= pos && pos <= size) + if (pos == BigInt(0)) { + l ++ this.drop(l.size) + } else { + this match { + case Cons(h, t) => + Cons(h, t.replaceAtImpl(pos-1, l)) + case Nil() => + l + } + } + } ensuring { res => + res.content.subsetOf(l.content ++ this.content) + } + + def replaceAt(pos: BigInt, l: List[T]): List[T] = { + require(-pos <= size && pos <= size) + if(pos < 0) { + replaceAtImpl(size + pos, l) + } else { + replaceAtImpl(pos, l) + } + } ensuring { res => + res.content.subsetOf(l.content ++ this.content) + } + + def rotate(s: BigInt): List[T] = { + if (isEmpty) { + Nil[T]() + } else { + drop(s mod size) ++ take(s mod size) + } + } ensuring { res => + res.size == this.size + } + + @isabelle.function(term = "List.null") + def isEmpty = this match { + case Nil() => true + case _ => false + } + + def nonEmpty = !isEmpty + + // Higher-order API + @isabelle.function(term = "%xs f. List.list.map f xs") + def map[R](f: T => R): List[R] = { this match { + case Nil() => Nil[R]() + case Cons(h, t) => f(h) :: t.map(f) + }} ensuring { _.size == this.size } + + @isabelle.function(term = "%bs a f. List.foldl f a bs") + def foldLeft[R](z: R)(f: (R,T) => R): R = this match { + case Nil() => z + case Cons(h,t) => t.foldLeft(f(z,h))(f) + } + + @isabelle.function(term = "%as b f. List.foldr f as b") + def foldRight[R](z: R)(f: (T,R) => R): R = this match { + case Nil() => z + case Cons(h, t) => f(h, t.foldRight(z)(f)) + } + + def scanLeft[R](z: R)(f: (R,T) => R): List[R] = { this match { + case Nil() => z :: Nil() + case Cons(h,t) => z :: t.scanLeft(f(z,h))(f) + }} ensuring { !_.isEmpty } + + def scanRight[R](z: R)(f: (T,R) => R): List[R] = { this match { + case Nil() => z :: Nil[R]() + case Cons(h, t) => + val rest@Cons(h1,_) = t.scanRight(z)(f) + f(h, h1) :: rest + }} ensuring { !_.isEmpty } + + @isabelle.function(term = "List.bind") + def flatMap[R](f: T => List[R]): List[R] = + ListOps.flatten(this map f) + + def filter(p: T => Boolean): List[T] = { this match { + case Nil() => Nil[T]() + case Cons(h, t) if p(h) => Cons(h, t.filter(p)) + case Cons(_, t) => t.filter(p) + }} ensuring { res => + res.size <= this.size && + res.content.subsetOf(this.content) && + res.forall(p) + } + + def filterNot(p: T => Boolean): List[T] = + filter(!p(_)) ensuring { res => + res.size <= this.size && + res.content.subsetOf(this.content) && + res.forall(!p(_)) + } + + def partition(p: T => Boolean): (List[T], List[T]) = { this match { + case Nil() => (Nil[T](), Nil[T]()) + case Cons(h, t) => + val (l1, l2) = t.partition(p) + if (p(h)) (h :: l1, l2) + else (l1, h :: l2) + }} ensuring { res => + res._1 == filter(p) && + res._2 == filterNot(p) + } + + // In case we implement for-comprehensions + def withFilter(p: T => Boolean) = filter(p) + + @isabelle.function(term = "%xs P. List.list_all P xs") + def forall(p: T => Boolean): Boolean = this match { + case Nil() => true + case Cons(h, t) => p(h) && t.forall(p) + } + + @isabelle.function(term = "%xs P. List.list_ex P xs") + def exists(p: T => Boolean) = !forall(!p(_)) + + @isabelle.function(term = "%xs P. List.find P xs") + def find(p: T => Boolean): Option[T] = { this match { + case Nil() => None[T]() + case Cons(h, t) => if (p(h)) Some(h) else t.find(p) + }} ensuring { res => res match { + case Some(r) => (content contains r) && p(r) + case None() => true + }} + + def groupBy[R](f: T => R): Map[R, List[T]] = this match { + case Nil() => Map.empty[R, List[T]] + case Cons(h, t) => + val key: R = f(h) + val rest: Map[R, List[T]] = t.groupBy(f) + val prev: List[T] = if (rest isDefinedAt key) rest(key) else Nil[T]() + (rest ++ Map((key, h :: prev))) : Map[R, List[T]] + } + + def takeWhile(p: T => Boolean): List[T] = { this match { + case Cons(h,t) if p(h) => Cons(h, t.takeWhile(p)) + case _ => Nil[T]() + }} ensuring { res => + (res forall p) && + (res.size <= this.size) && + (res.content subsetOf this.content) + } + + def dropWhile(p: T => Boolean): List[T] = { this match { + case Cons(h,t) if p(h) => t.dropWhile(p) + case _ => this + }} ensuring { res => + (res.size <= this.size) && + (res.content subsetOf this.content) && + (res.isEmpty || !p(res.head)) + } + + def count(p: T => Boolean): BigInt = { this match { + case Nil() => BigInt(0) + case Cons(h, t) => + (if (p(h)) BigInt(1) else BigInt(0)) + t.count(p) + }} ensuring { + _ == this.filter(p).size + } + + def indexWhere(p: T => Boolean): BigInt = { this match { + case Nil() => BigInt(-1) + case Cons(h, _) if p(h) => BigInt(0) + case Cons(_, t) => + val rec = t.indexWhere(p) + if (rec >= 0) rec + BigInt(1) + else BigInt(-1) + }} ensuring { + _ >= BigInt(0) == (this exists p) + } + + + // Translation to other collections + def toSet: Set[T] = foldLeft(Set[T]()){ + case (current, next) => current ++ Set(next) + } + +} + +@isabelle.constructor(name = "List.list.Cons") +case class Cons[T](h: T, t: List[T]) extends List[T] + +@isabelle.constructor(name = "List.list.Nil") +case class Nil[T]() extends List[T] + +object List { + @ignore + def apply[T](elems: T*): List[T] = { + var l: List[T] = Nil[T]() + for (e <- elems) { + l = Cons(e, l) + } + l.reverse + } + + @library + def fill[T](n: BigInt)(x: T) : List[T] = { + if (n <= 0) Nil[T] + else Cons[T](x, fill[T](n-1)(x)) + } ensuring(res => (res.content == (if (n <= BigInt(0)) Set.empty[T] else Set(x))) && + res.size == (if (n <= BigInt(0)) BigInt(0) else n)) +} + +@library +object ListOps { + @isabelle.function(term = "List.concat") + def flatten[T](ls: List[List[T]]): List[T] = ls match { + case Cons(h, t) => h ++ flatten(t) + case Nil() => Nil() + } + + def isSorted(ls: List[BigInt]): Boolean = ls match { + case Nil() => true + case Cons(_, Nil()) => true + case Cons(h1, Cons(h2, _)) if(h1 > h2) => false + case Cons(_, t) => isSorted(t) + } + + def sorted(ls: List[BigInt]): List[BigInt] = { ls match { + case Cons(h, t) => sortedIns(sorted(t), h) + case Nil() => Nil[BigInt]() + }} ensuring { isSorted _ } + + private def sortedIns(ls: List[BigInt], v: BigInt): List[BigInt] = { + require(isSorted(ls)) + ls match { + case Nil() => Cons(v, Nil()) + case Cons(h, t) => + if (v <= h) { + Cons(v, t) + } else { + Cons(h, sortedIns(t, v)) + } + } + } ensuring { isSorted _ } + + def toMap[K, V](l: List[(K, V)]): Map[K, V] = l.foldLeft(Map[K, V]()){ + case (current, (k, v)) => current ++ Map(k -> v) + } +} + +// 'Cons' Extractor +object :: { + @library + def unapply[A](l: List[A]): Option[(A, List[A])] = l match { + case Nil() => None() + case Cons(x, xs) => Some((x, xs)) + } +} + + +import ListOps._ + +@library +object ListSpecs { + def snocIndex[T](l: List[T], t: T, i: BigInt): Boolean = { + require(0 <= i && i < l.size + 1) + ((l :+ t).apply(i) == (if (i < l.size) l(i) else t)) because + (l match { + case Nil() => true + case Cons(x, xs) => if (i > 0) snocIndex[T](xs, t, i-1) else true + }) + }.holds + + @induct + @isabelle.lemma(about = "leon.collection.List.apply") + def consIndex[T](h: T, t: List[T], i: BigInt): Boolean = { + require(0 <= i && i < t.size + 1) + (h :: t).apply(i) == (if (i == 0) h else t.apply(i - 1)) + }.holds + + def reverseIndex[T](l: List[T], i: BigInt): Boolean = { + require(0 <= i && i < l.size) + (l.reverse.apply(i) == l.apply(l.size - 1 - i)) because + (l match { + case Nil() => true + case Cons(x,xs) => snocIndex(xs.reverse, x, i) && (if (i < xs.size) consIndex(x, xs, l.size - 1 - i) && reverseIndex[T](xs, i) else true) + }) + }.holds + + def snocLast[T](l: List[T], x: T): Boolean = { + ((l :+ x).last == x) because { + l match { + case Nil() => true + case Cons(y, ys) => { + ((y :: ys) :+ x).last ==| trivial | + (y :: (ys :+ x)).last ==| trivial | + (ys :+ x).last ==| snocLast(ys, x) | + x + }.qed + } + } + }.holds + + def headReverseLast[T](l: List[T]): Boolean = { + require (!l.isEmpty) + (l.head == l.reverse.last) because { + val Cons(x, xs) = l; + { + (x :: xs).head ==| trivial | + x ==| snocLast(xs.reverse, x) | + (xs.reverse :+ x).last ==| trivial | + (x :: xs).reverse.last + }.qed + } + }.holds + + def appendIndex[T](l1: List[T], l2: List[T], i: BigInt): Boolean = { + require(0 <= i && i < l1.size + l2.size) + ( (l1 ++ l2).apply(i) == (if (i < l1.size) l1(i) else l2(i - l1.size)) ) because { + l1 match { + case Nil() => true + case Cons(x,xs) => + (i != BigInt(0)) ==> appendIndex[T](xs, l2, i - 1) + } + } + }.holds + + @induct + def appendAssoc[T](l1: List[T], l2: List[T], l3: List[T]): Boolean = { + (l1 ++ l2) ++ l3 == l1 ++ (l2 ++ l3) + }.holds + + @induct + def rightUnitAppend[T](l1: List[T]): Boolean = { + l1 ++ Nil() == l1 + }.holds + + // This follows immediately from the definition of `++` but we + // include it here anyway for completeness. + def leftUnitAppend[T](l1: List[T]): Boolean = { + Nil() ++ l1 == l1 + }.holds + + def snocIsAppend[T](l: List[T], t: T): Boolean = { + ( (l :+ t) == l ++ Cons[T](t, Nil()) ) because { + l match { + case Nil() => true + case Cons(x,xs) => snocIsAppend(xs,t) + } + } + }.holds + + def snocAfterAppend[T](l1: List[T], l2: List[T], t: T): Boolean = { + ( (l1 ++ l2) :+ t == l1 ++ (l2 :+ t) ) because { + l1 match { + case Nil() => true + case Cons(x,xs) => snocAfterAppend(xs,l2,t) + } + } + }.holds + + def snocReverse[T](l: List[T], t: T): Boolean = { + ( (l :+ t).reverse == Cons(t, l.reverse) ) because { + l match { + case Nil() => true + case Cons(x,xs) => snocReverse(xs,t) + } + } + }.holds + + def reverseReverse[T](l: List[T]): Boolean = { + l.reverse.reverse == l because { + l match { + case Nil() => trivial + case Cons(x, xs) => { + (xs.reverse :+ x).reverse ==| snocReverse[T](xs.reverse, x) | + x :: xs.reverse.reverse ==| reverseReverse[T](xs) | + (x :: xs) + }.qed + } + } + }.holds + + def reverseAppend[T](l1: List[T], l2: List[T]): Boolean = { + ( (l1 ++ l2).reverse == l2.reverse ++ l1.reverse ) because { + l1 match { + case Nil() => { + (Nil() ++ l2).reverse ==| trivial | + l2.reverse ==| rightUnitAppend(l2.reverse) | + l2.reverse ++ Nil() ==| trivial | + l2.reverse ++ Nil().reverse + }.qed + case Cons(x, xs) => { + ((x :: xs) ++ l2).reverse ==| trivial | + (x :: (xs ++ l2)).reverse ==| trivial | + (xs ++ l2).reverse :+ x ==| reverseAppend(xs, l2) | + (l2.reverse ++ xs.reverse) :+ x ==| + snocAfterAppend(l2.reverse, xs.reverse, x) | + l2.reverse ++ (xs.reverse :+ x) ==| trivial | + l2.reverse ++ (x :: xs).reverse + }.qed + } + } + }.holds + + def snocFoldRight[A, B](xs: List[A], y: A, z: B, f: (A, B) => B): Boolean = { + ( (xs :+ y).foldRight(z)(f) == xs.foldRight(f(y, z))(f) ) because { + xs match { + case Nil() => true + case Cons(x, xs) => snocFoldRight(xs, y, z, f) + } + } + }.holds + + def folds[A, B](xs: List[A], z: B, f: (B, A) => B): Boolean = { + val f2 = (x: A, z: B) => f(z, x) + ( xs.foldLeft(z)(f) == xs.reverse.foldRight(z)(f2) ) because { + xs match { + case Nil() => true + case Cons(x, xs) => { + (x :: xs).foldLeft(z)(f) ==| trivial | + xs.foldLeft(f(z, x))(f) ==| folds(xs, f(z, x), f) | + xs.reverse.foldRight(f(z, x))(f2) ==| trivial | + xs.reverse.foldRight(f2(x, z))(f2) ==| + snocFoldRight(xs.reverse, x, z, f2) | + (xs.reverse :+ x).foldRight(z)(f2) ==| trivial | + (x :: xs).reverse.foldRight(z)(f2) + }.qed + } + } + }.holds + + def scanVsFoldLeft[A, B](l: List[A], z: B, f: (B, A) => B): Boolean = { + ( l.scanLeft(z)(f).last == l.foldLeft(z)(f) ) because { + l match { + case Nil() => true + case Cons(x, xs) => scanVsFoldLeft(xs, f(z, x), f) + } + } + }.holds + + //// my hand calculation shows this should work, but it does not seem to be found + //def associative[T,U](l1: List[T], l2: List[T], f: List[T] => U, op: (U,U) => U) = { + // f(l1 ++ l2) == op(f(l1), f(l2)) + //} + // + //def existsAssoc[T](l1: List[T], l2: List[T], p: T => Boolean) = { + // associative[T, Boolean](l1, l2, _.exists(p), _ || _ ) + //}.holds + // + //def forallAssoc[T](l1: List[T], l2: List[T], p: T => Boolean) = { + // associative[T, Boolean](l1, l2, _.exists(p), _ && _ ) + //}.holds + + @induct + def scanVsFoldRight[A,B](l: List[A], z: B, f: (A,B) => B): Boolean = { + l.scanRight(z)(f).head == l.foldRight(z)(f) + }.holds + + def appendContent[A](l1: List[A], l2: List[A]) = { + l1.content ++ l2.content == (l1 ++ l2).content + }.holds + + def flattenPreservesContent[T](ls: List[List[T]]): Boolean = { + val f: (List[T], Set[T]) => Set[T] = _.content ++ _ + ( flatten(ls).content == ls.foldRight(Set[T]())(f) ) because { + ls match { + case Nil() => true + case Cons(h, t) => { + flatten(h :: t).content ==| trivial | + (h ++ flatten(t)).content ==| appendContent(h, flatten(t)) | + h.content ++ flatten(t).content ==| flattenPreservesContent(t) | + h.content ++ t.foldRight(Set[T]())(f) ==| trivial | + f(h, Set[T]()) ++ t.foldRight(Set[T]())(f) ==| trivial | + (h :: t).foldRight(Set[T]())(f) + }.qed + } + } + }.holds + + // A lemma about `append` and `updated` + def appendUpdate[T](l1: List[T], l2: List[T], i: BigInt, y: T): Boolean = { + require(0 <= i && i < l1.size + l2.size) + // induction scheme + (l1 match { + case Nil() => true + case Cons(x, xs) => if (i == 0) true else appendUpdate[T](xs, l2, i - 1, y) + }) && + // lemma + ((l1 ++ l2).updated(i, y) == ( + if (i < l1.size) + l1.updated(i, y) ++ l2 + else + l1 ++ l2.updated(i - l1.size, y))) + }.holds + + // a lemma about `append`, `take` and `drop` + def appendTakeDrop[T](l1: List[T], l2: List[T], n: BigInt): Boolean = { + //induction scheme + (l1 match { + case Nil() => true + case Cons(x, xs) => if (n <= 0) true else appendTakeDrop[T](xs, l2, n - 1) + }) && + // lemma + ((l1 ++ l2).take(n) == ( + if (n < l1.size) l1.take(n) + else if (n > l1.size) l1 ++ l2.take(n - l1.size) + else l1)) && + ((l1 ++ l2).drop(n) == ( + if (n < l1.size) l1.drop(n) ++ l2 + else if (n > l1.size) l2.drop(n - l1.size) + else l2)) + }.holds + + // A lemma about `append` and `insertAt` + def appendInsert[T](l1: List[T], l2: List[T], i: BigInt, y: T): Boolean = { + require(0 <= i && i <= l1.size + l2.size) + (l1 match { + case Nil() => true + case Cons(x, xs) => if (i == 0) true else appendInsert[T](xs, l2, i - 1, y) + }) && + // lemma + ((l1 ++ l2).insertAt(i, y) == ( + if (i < l1.size) l1.insertAt(i, y) ++ l2 + else l1 ++ l2.insertAt((i - l1.size), y))) + }.holds + +} diff --git a/libtest/src/main/scala/leon/collection/package.scala b/libtest/src/main/scala/leon/collection/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..8a5ae170763909c20153f373915d6a4e9e726ca0 --- /dev/null +++ b/libtest/src/main/scala/leon/collection/package.scala @@ -0,0 +1,25 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import leon.annotation._ +import leon.collection.List +import leon.lang._ +import leon.lang.synthesis.choose + +package object collection { + + @internal @library + def setToList[A](set: Set[A]): List[A] = choose { + (x: List[A]) => x.content == set + } + + @library + def setForall[A](set: Set[A], p: A => Boolean): Boolean = + setToList(set).forall(p) + + @library + def setExists[A](set: Set[A], p: A => Boolean): Boolean = + setToList(set).exists(p) + +} diff --git a/libtest/src/main/scala/leon/instrumentation/package.scala b/libtest/src/main/scala/leon/instrumentation/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..a6e7545c0b4ab2bae468b0cc6556f67784487b17 --- /dev/null +++ b/libtest/src/main/scala/leon/instrumentation/package.scala @@ -0,0 +1,24 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import leon.annotation._ +import leon.lang._ +import scala.language.implicitConversions + +package object instrumentation { + @library + def time: BigInt = 0 + + @library + def stack: BigInt = 0 + + @library + def rec: BigInt = 0 + + @library + def depth: BigInt = 0 + + @library + def tpr: BigInt = 0 +} diff --git a/libtest/src/main/scala/leon/invariant/package.scala b/libtest/src/main/scala/leon/invariant/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..0cc7919d0194a5dff25457c38db8cd00c78db8bd --- /dev/null +++ b/libtest/src/main/scala/leon/invariant/package.scala @@ -0,0 +1,26 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import leon.annotation._ +import leon.lang._ +import scala.language.implicitConversions + +package object invariant { + @library + def tmpl(templateFunc: BigInt => Boolean): Boolean = true + @library + def tmpl(templateFunc: (BigInt, BigInt) => Boolean): Boolean = true + @library + def tmpl(templateFunc: (BigInt, BigInt, BigInt) => Boolean): Boolean = true + @library + def tmpl(templateFunc: (BigInt, BigInt, BigInt, BigInt) => Boolean): Boolean = true + @library + def tmpl(templateFunc: (BigInt, BigInt, BigInt, BigInt, BigInt) => Boolean): Boolean = true + + @library + def ? : BigInt = 0 + + @library + def ?(id: BigInt) = id +} diff --git a/libtest/src/main/scala/leon/lang/Dummy.scala b/libtest/src/main/scala/leon/lang/Dummy.scala new file mode 100644 index 0000000000000000000000000000000000000000..41dbe4f90ccea298a17de70caa2f3d04ebdc0def --- /dev/null +++ b/libtest/src/main/scala/leon/lang/Dummy.scala @@ -0,0 +1,3 @@ +package leon.lang + +case class Dummy[T]() diff --git a/libtest/src/main/scala/leon/lang/Either.scala b/libtest/src/main/scala/leon/lang/Either.scala new file mode 100644 index 0000000000000000000000000000000000000000..9cc2ea4e9537b424cbce6963e2a4038f3480d01a --- /dev/null +++ b/libtest/src/main/scala/leon/lang/Either.scala @@ -0,0 +1,27 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.lang + +import leon.annotation._ + +/** + * @author Viktor + */ +@library +sealed abstract class Either[A,B] { + def isLeft : Boolean + def isRight : Boolean + def swap : Either[B,A] +} +@library +case class Left[A,B](content: A) extends Either[A,B] { + def isLeft = true + def isRight = false + def swap = Right[B,A](content) +} +@library +case class Right[A,B](content: B) extends Either[A,B] { + def isLeft = false + def isRight = true + def swap = Left[B,A](content) +} \ No newline at end of file diff --git a/libtest/src/main/scala/leon/lang/Map.scala b/libtest/src/main/scala/leon/lang/Map.scala new file mode 100644 index 0000000000000000000000000000000000000000..aff6f26f80d5e4abc7d8fa70d3a6553ebdff1c17 --- /dev/null +++ b/libtest/src/main/scala/leon/lang/Map.scala @@ -0,0 +1,33 @@ +package leon.lang +import leon.annotation._ + +object Map { + @library + @isabelle.function(term = "Map.empty") + def empty[A,B] = Map[A,B]() + + @ignore + def apply[A,B](elems: (A,B)*) = { + new Map[A,B](scala.collection.immutable.Map[A,B](elems : _*)) + } +} + +@ignore +case class Map[A, B] (theMap: scala.collection.immutable.Map[A,B]) { + def apply(k: A): B = theMap.apply(k) + def ++(b: Map[A, B]): Map[A,B] = new Map (theMap ++ b.theMap) + def updated(k: A, v: B): Map[A,B] = new Map(theMap.updated(k, v)) + def contains(a: A): Boolean = theMap.contains(a) + def isDefinedAt(a: A): Boolean = contains(a) + + def +(kv: (A, B)): Map[A,B] = updated(kv._1, kv._2) + def +(k: A, v: B): Map[A,B] = updated(k, v) + + def getOrElse(k: A, default: B): B = get(k).getOrElse(default) + + def get(k: A): Option[B] = if (contains(k)) { + Some[B](apply(k)) + } else { + None[B]() + } +} diff --git a/libtest/src/main/scala/leon/lang/Option.scala b/libtest/src/main/scala/leon/lang/Option.scala new file mode 100644 index 0000000000000000000000000000000000000000..a6f09db68f3fecc7ad8095506a49d21e4d8ac7ad --- /dev/null +++ b/libtest/src/main/scala/leon/lang/Option.scala @@ -0,0 +1,84 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.lang + +import leon.annotation._ +import leon.collection._ + +@library +@isabelle.typ(name = "Option.option") +sealed abstract class Option[T] { + + def get : T = { + require(this.isDefined) + (this : @unchecked) match { + case Some(x) => x + } + } + + def getOrElse(default: T) = this match { + case Some(v) => v + case None() => default + } + + def orElse(or: Option[T]) = { this match { + case Some(v) => this + case None() => or + }} ensuring { + _.isDefined == this.isDefined || or.isDefined + } + + def isEmpty = this match { + case Some(v) => false + case None() => true + } + + def nonEmpty = !isEmpty + + def isDefined = !isEmpty + + + // Higher-order API + @isabelle.function(term = "%x f. Option.map_option f x") + def map[R](f: T => R) = { this match { + case None() => None[R]() + case Some(x) => Some(f(x)) + }} ensuring { _.isDefined == this.isDefined } + + @isabelle.function(term = "Option.bind") + def flatMap[R](f: T => Option[R]) = this match { + case None() => None[R]() + case Some(x) => f(x) + } + + def filter(p: T => Boolean) = this match { + case Some(x) if p(x) => Some(x) + case _ => None[T]() + } + + def withFilter(p: T => Boolean) = filter(p) + + def forall(p: T => Boolean) = this match { + case Some(x) if !p(x) => false + case _ => true + } + + def exists(p: T => Boolean) = !forall(!p(_)) + + // Transformation to other collections + def toList: List[T] = this match { + case None() => Nil[T]() + case Some(x) => List(x) + } + + def toSet: Set[T] = this match { + case None() => Set[T]() + case Some(x) => Set(x) + } +} + +@isabelle.constructor(name = "Option.option.Some") +case class Some[T](v: T) extends Option[T] + +@isabelle.constructor(name = "Option.option.None") +case class None[T]() extends Option[T] diff --git a/libtest/src/main/scala/leon/lang/Rational.scala b/libtest/src/main/scala/leon/lang/Rational.scala new file mode 100644 index 0000000000000000000000000000000000000000..1bcb679a0d4abf6b168117b2af819fa41e027315 --- /dev/null +++ b/libtest/src/main/scala/leon/lang/Rational.scala @@ -0,0 +1,85 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.lang + +import leon.annotation._ + +import scala.language.implicitConversions + +@library +case class Rational(numerator: BigInt, denominator: BigInt) { + + require(this.isRational) + + def +(that: Rational): Rational = { + Rational(this.numerator*that.denominator + that.numerator*this.denominator, this.denominator*that.denominator) + } ensuring(res => res.isRational) + + def -(that: Rational): Rational = { + Rational(this.numerator*that.denominator - that.numerator*this.denominator, this.denominator*that.denominator) + } ensuring(res => res.isRational) + + def unary_- : Rational = { + Rational(-this.numerator, this.denominator) + } ensuring(res => res.isRational) + + def *(that: Rational): Rational = { + Rational(this.numerator*that.numerator, this.denominator*that.denominator) + } ensuring(res => res.isRational) + + def /(that: Rational): Rational = { + require(that.nonZero) + val newNumerator = this.numerator*that.denominator + val newDenominator = this.denominator*that.numerator + normalize(newNumerator, newDenominator) + } ensuring(res => res.isRational) + + def reciprocal: Rational = { + require(this.nonZero) + normalize(this.denominator, this.numerator) + } ensuring(res => res.isRational) + + + def ~(that: Rational): Boolean = { + this.numerator*that.denominator == that.numerator*this.denominator + } + + def <(that: Rational): Boolean = { + this.numerator*that.denominator < that.numerator*this.denominator + } + + def <=(that: Rational): Boolean = { + this.numerator*that.denominator <= that.numerator*this.denominator + } + + def >(that: Rational): Boolean = { + this.numerator*that.denominator > that.numerator*this.denominator + } + + def >=(that: Rational): Boolean = { + this.numerator*that.denominator >= that.numerator*this.denominator + } + + def nonZero: Boolean = { + numerator != 0 + } + + private def isRational: Boolean = denominator > 0 + + private def normalize(num: BigInt, den: BigInt): Rational = { + require(den != 0) + if(den < 0) + Rational(-num, -den) + else + Rational(num, den) + } +} + +@library +object Rational { + + implicit def bigIntToRat(n: BigInt): Rational = Rational(n, 1) + + def apply(n: BigInt): Rational = Rational(n, 1) + +} diff --git a/libtest/src/main/scala/leon/lang/Real.scala b/libtest/src/main/scala/leon/lang/Real.scala new file mode 100644 index 0000000000000000000000000000000000000000..9d600b3b6c584355dacfc10ed6d4d6b0c58fd631 --- /dev/null +++ b/libtest/src/main/scala/leon/lang/Real.scala @@ -0,0 +1,25 @@ +package leon.lang +import leon.annotation._ + +@ignore +class Real { + + def +(a: Real): Real = ??? + def -(a: Real): Real = ??? + def *(a: Real): Real = ??? + def /(a: Real): Real = ??? + + def unary_- : Real = ??? + + def > (a: Real): Boolean = ??? + def >=(a: Real): Boolean = ??? + def < (a: Real): Boolean = ??? + def <=(a: Real): Boolean = ??? + +} + +@ignore +object Real { + def apply(n: BigInt, d: BigInt): Real = ??? + def apply(n: BigInt): Real = ??? +} diff --git a/libtest/src/main/scala/leon/lang/Set.scala b/libtest/src/main/scala/leon/lang/Set.scala new file mode 100644 index 0000000000000000000000000000000000000000..8f4595d33fada35acfa38435c3cd542116cf3cbd --- /dev/null +++ b/libtest/src/main/scala/leon/lang/Set.scala @@ -0,0 +1,26 @@ +package leon.lang +import leon.annotation._ + +object Set { + @library + def empty[T] = Set[T]() + + @ignore + def apply[T](elems: T*) = { + new Set[T](scala.collection.immutable.Set[T](elems : _*)) + } +} + +@ignore +case class Set[T](val theSet: scala.collection.immutable.Set[T]) { + def +(a: T): Set[T] = new Set[T](theSet + a) + def ++(a: Set[T]): Set[T] = new Set[T](theSet ++ a.theSet) + def -(a: T): Set[T] = new Set[T](theSet - a) + def --(a: Set[T]): Set[T] = new Set[T](theSet -- a.theSet) + def size: BigInt = theSet.size + def contains(a: T): Boolean = theSet.contains(a) + def isEmpty: Boolean = theSet.isEmpty + def subsetOf(b: Set[T]): Boolean = theSet.subsetOf(b.theSet) + def &(a: Set[T]): Set[T] = new Set[T](theSet & a.theSet) +} + diff --git a/libtest/src/main/scala/leon/lang/StrOps.scala b/libtest/src/main/scala/leon/lang/StrOps.scala new file mode 100644 index 0000000000000000000000000000000000000000..723ce5be82d7a841c743640ba4f10284076c0dfe --- /dev/null +++ b/libtest/src/main/scala/leon/lang/StrOps.scala @@ -0,0 +1,23 @@ +package leon.lang + +import leon.annotation._ + +/** + * @author Mikael + */ +object StrOps { + @ignore + def concat(a: String, b: String): String = { + a + b + } + @ignore + def length(a: String): BigInt = { + BigInt(a.length) + } + @ignore + def substring(a: String, start: BigInt, end: BigInt): String = { + if(start > end || start >= length(a) || end <= 0) "" else a.substring(start.toInt, end.toInt) + } + @internal @library + def escape(s: String): String = s // Wrong definition, but it will eventually use StringEscapeUtils.escapeJava(s) at parsing and compile time. +} \ No newline at end of file diff --git a/libtest/src/main/scala/leon/lang/package.scala b/libtest/src/main/scala/leon/lang/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..ab713a588e5451ba8ab61533d6037bd2bd572de8 --- /dev/null +++ b/libtest/src/main/scala/leon/lang/package.scala @@ -0,0 +1,85 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import leon.annotation._ +import scala.language.implicitConversions + +package object lang { + + implicit class BooleanDecorations(val underlying: Boolean) { + @inline + def holds : Boolean = { + underlying + } ensuring { + _ == true + } + + @inline + def ==>(that: => Boolean): Boolean = { + if (underlying) that else true + } + } + + @ignore def forall[A](p: A => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B](p: (A,B) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B,C](p: (A,B,C) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B,C,D](p: (A,B,C,D) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B,C,D,E](p: (A,B,C,D,E) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + + @ignore + object InvariantFunction { + def invariant(x: Boolean): Unit = () + } + + @ignore + implicit def while2Invariant(u: Unit) = InvariantFunction + + @ignore + def error[T](reason: java.lang.String): T = sys.error(reason) + + @ignore + def old[T](value: T): T = value + + @ignore + implicit class Passes[A,B](io : (A,B)) { + val (in, out) = io + def passes(tests : A => B ) : Boolean = + try { tests(in) == out } catch { case _ : MatchError => true } + } + + @ignore + def byExample[A, B](in: A, out: B): Boolean = { + sys.error("Can't execute by example proposition") + } + + implicit class SpecsDecorations[A](val underlying: A) { + @ignore + def computes(target: A) = { + underlying + } ensuring { + res => res == target + } + + @ignore // Programming by example: ???[String] ask input + def ask[I](input : I) = { + underlying + } ensuring { + (res: A) => byExample(input, res) + } + } + + @ignore + object BigInt { + def apply(b: Int): scala.math.BigInt = scala.math.BigInt(b) + def apply(b: String): scala.math.BigInt = scala.math.BigInt(b) + + def unapply(b: scala.math.BigInt): scala.Option[Int] = { + if(b >= Integer.MIN_VALUE && b <= Integer.MAX_VALUE) { + scala.Some(b.intValue()) + } else { + scala.None + } + } + } +} diff --git a/libtest/src/main/scala/leon/lang/synthesis/Oracle.scala b/libtest/src/main/scala/leon/lang/synthesis/Oracle.scala new file mode 100644 index 0000000000000000000000000000000000000000..7af697638880232c9248cbe98e01e0327552bccb --- /dev/null +++ b/libtest/src/main/scala/leon/lang/synthesis/Oracle.scala @@ -0,0 +1,33 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.lang.synthesis + +import leon.annotation._ +import leon.lang._ + +import scala.annotation._ + +@implicitNotFound("No Oracle available for this source of non-determinism, please provide an implicit arg <: Oracle[T]") +@ignore +@library +abstract class Oracle[T] { + def head: T = this match { + case Node(_, h, _) => h + case Leaf(h) => h + } + + def left: Oracle[T] = this match { + case Node(l, _, _) => l + case Leaf(_) => this + } + + def right: Oracle[T] = this match { + case Node(_, _, r) => r + case Leaf(_) => this + } +} + +@ignore +case class Node[T](l: Oracle[T], v: T, r: Oracle[T]) extends Oracle[T]; +@ignore +case class Leaf[T](v: T) extends Oracle[T]; diff --git a/libtest/src/main/scala/leon/lang/synthesis/package.scala b/libtest/src/main/scala/leon/lang/synthesis/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..cb56992797715423aa1b0c45f94946778cb5780e --- /dev/null +++ b/libtest/src/main/scala/leon/lang/synthesis/package.scala @@ -0,0 +1,41 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.lang + +import leon.annotation._ + +import scala.language.implicitConversions +import scala.annotation.implicitNotFound + +package object synthesis { + @ignore + private def noImpl = throw new RuntimeException("Synthesis construct implementation not supported") + + @ignore + def choose[A](predicate: A => Boolean): A = noImpl + @ignore + def choose[A, B](predicate: (A, B) => Boolean): (A, B) = noImpl + @ignore + def choose[A, B, C](predicate: (A, B, C) => Boolean): (A, B, C) = noImpl + @ignore + def choose[A, B, C, D](predicate: (A, B, C, D) => Boolean): (A, B, C, D) = noImpl + @ignore + def choose[A, B, C, D, E](predicate: (A, B, C, D, E) => Boolean): (A, B, C, D, E) = noImpl + + @ignore + def ???[T]: T = noImpl + + @ignore + def ?[T](e1: T): T = if(???[Boolean]) e1 else ???[T] + + @ignore + def ?[T](e1: T, es: T*): T = noImpl + + // Repair with Holes + @ignore + def ?: T = noImpl + + @ignore + def withOracle[A, R](body: Oracle[A] => R): R = noImpl + +} diff --git a/libtest/src/main/scala/leon/lang/xlang/package.scala b/libtest/src/main/scala/leon/lang/xlang/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..6f29c5d33e3fc8dff8a26d526cf7ef2c6f2f0837 --- /dev/null +++ b/libtest/src/main/scala/leon/lang/xlang/package.scala @@ -0,0 +1,13 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.lang + +import leon.annotation._ + +package object xlang { + @ignore + def waypoint[A](i: Int, expr: A): A = expr + + @ignore + def epsilon[A](pred: (A) => Boolean): A = throw new RuntimeException("Implementation not supported") +} diff --git a/libtest/src/main/scala/leon/lazy/package.scala b/libtest/src/main/scala/leon/lazy/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..7c0f575828c3bd96cb32c069c43e1444373075a5 --- /dev/null +++ b/libtest/src/main/scala/leon/lazy/package.scala @@ -0,0 +1,90 @@ +/* Copyright 2009-2013 EPFL, Lausanne */ + +package leon + +import annotation._ +import lang._ +import scala.language.implicitConversions +import scala.annotation.StaticAnnotation + +package object lazyeval { + + @library + def $[T](f: => T) = Lazy(Unit => f) + //def apply[T](f: => T) = new $(Unit => f) + + @library + def eager[T](x: => T) = Lazy(Unit => x) + + /** + * implicit conversion from eager evaluation to lazy evaluation + */ + @library + @inline + implicit def eagerToLazy[T](x: T) = eager(x) + + /** + * For accessing in and out states. + * Should be used only in ensuring. + */ + @library + @extern + def inState[T]: Set[Lazy[T]] = sys.error("inState method is not executable!") + + @library + @extern + def outState[T]: Set[Lazy[T]] = sys.error("outState method is not executable") + + /** + * Helper class for invoking with a given state instead of the implicit state + */ + @library + case class WithState[T](v: T) { + @extern + def withState[U](u: Set[Lazy[U]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V](u: Set[Lazy[U]], v: Set[Lazy[V]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V, W](u: Set[Lazy[U]], v: Set[Lazy[V]], w: Set[Lazy[W]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V, W, X](u: Set[Lazy[U]], v: Set[Lazy[V]], w: Set[Lazy[W]], x: Set[Lazy[X]]): T = sys.error("withState method is not executable!") + // extend this to more arguments if needed + } + + @library + @inline + implicit def toWithState[T](x: T) = new WithState(x) + + /** + * annotations for monotonicity proofs. + * Note implemented as of now. + * Let foo be the method that has the annotation. + * The given name `methodname` should have the following form: + * m(arg1, ..., argn, substate-Set1,..., substate-Setn, superstate-Set1, ..., superstate-Setn) + */ + @ignore + class monoproof(methodname: String) extends StaticAnnotation + + @library + case class Lazy[T](f: Unit => T) { + @extern + lazy val value = { + val x = f(()) + eval = true + x + } + def * = f(()) + + @ignore + var eval = false // is this thread safe ? + + @extern + def isEvaluated = eval + + @extern + def isSuspension[T](f: T): Boolean = sys.error("isSuspensionOf method is not executable") + } +} \ No newline at end of file diff --git a/libtest/src/main/scala/leon/math/package.scala b/libtest/src/main/scala/leon/math/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..8eb5f347963f4599faeca8e1c58adda74b259aef --- /dev/null +++ b/libtest/src/main/scala/leon/math/package.scala @@ -0,0 +1,18 @@ +package leon +import leon.annotation._ + +package object math { + + @library + def min(i1: Int, i2: Int) = if (i1 <= i2) i1 else i2 + + @library + def max(i1: Int, i2: Int) = if (i1 >= i2) i1 else i2 + + @library + def min(i1: BigInt, i2: BigInt) = if (i1 <= i2) i1 else i2 + + @library + def max(i1: BigInt, i2: BigInt) = if (i1 >= i2) i1 else i2 + +} diff --git a/libtest/src/main/scala/leon/mem/package.scala b/libtest/src/main/scala/leon/mem/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..416b737acae9b34287dc0f54ee3e6202fe72721e --- /dev/null +++ b/libtest/src/main/scala/leon/mem/package.scala @@ -0,0 +1,56 @@ +/* Copyright 2009-2013 EPFL, Lausanne */ + +package leon + +import annotation._ +import lang._ +import scala.language.implicitConversions +import scala.annotation.StaticAnnotation + +package object mem { + + @library + @inline + implicit def toMem[T](x: T) = new Mem(x) + + /** + * accessing in and out states of mems + * Should be used only in ensuring. + */ + @library + @extern + def inState[T]: Set[Mem[T]] = sys.error("inState method is not executable!") + + @library + @extern + def outState[T]: Set[Mem[T]] = sys.error("outState method is not executable") + + /** + * Helper class for invoking with a given state instead of the implicit state + */ + @library + case class memWithState[T](v: T) { + @extern + def withState[U](u: Set[Mem[U]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V](u: Set[Mem[U]], v: Set[Mem[V]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V, W](u: Set[Mem[U]], v: Set[Mem[V]], w: Set[Mem[W]]): T = sys.error("withState method is not executable!") + + @extern + def withState[U, V, W, X](u: Set[Mem[U]], v: Set[Mem[V]], w: Set[Mem[W]], x: Set[Mem[X]]): T = sys.error("withState method is not executable!") + // extend this to more arguments if needed + } + + @library + @inline + implicit def toWithState[T](x: T) = new memWithState(x) + + @library + case class Mem[T](v: T) { + @extern + def isCached: Boolean = sys.error("not implemented!") + } +} \ No newline at end of file diff --git a/libtest/src/main/scala/leon/monads/state/State.scala b/libtest/src/main/scala/leon/monads/state/State.scala new file mode 100644 index 0000000000000000000000000000000000000000..ad47c2464f54c9e40fc430528c561ad40032ac1f --- /dev/null +++ b/libtest/src/main/scala/leon/monads/state/State.scala @@ -0,0 +1,215 @@ +package leon.monads.state + +import leon.collection._ +import leon.lang._ +import leon.annotation._ +import State._ + + +@library +case class State[S, A](runState: S => (A, S)) { + + /** Basic monadic methods */ + + // Monadic bind + @inline + def flatMap[B](f: A => State[S, B]): State[S, B] = { + State { s => + val (a, newS) = runState(s) + f(a).runState(newS) + } + } + + // All monads are also functors, and they define the map function + @inline + def map[B](f: A => B): State[S, B] = { + State { s => + val (a, newS) = runState(s) + (f(a), newS) + } + } + + @inline + def >>=[B](f: A => State[S, B]): State[S, B] = flatMap(f) + + @inline + def >>[B](that: State[S, B]) = >>= ( _ => that ) + + @inline + // This is unfortunate, but implementing properly would just lead to an error + def withFilter(f: A => Boolean): State[S, A] = this + + @inline + def eval(s: S) = runState(s)._1 + @inline + def exec(s: S) = runState(s)._2 + + // eval with arguments flipped + @inline + def >:: (s: S) = eval(s) + + def apply(s: S) = runState(s) + +} + +@library +object State { + + @inline + def get[A]: State[A, A] = State( a => (a, a) ) + + @inline + def gets[S, A](f: S => A): State[S, A] = for { + s <- get[S] + } yield f(s) + + @inline + def put[S](s: S): State[S, Unit] = State { _ => ((), s) } + + @inline + def modify[S](f: S => S): State[S, Unit] = for { + s <- get[S] + s2 <- put(f(s)) + } yield s2 + + // Monadic unit + @inline + def unit[S, A](a: A): State[S, A] = State { s => (a, s) } + + @inline + def state[S]: State[S, S] = State(s => (s, s)) + + @inline + // Left-to-right Kleisli composition of monads + def >=>[S, A, B, C](f: A => State[S, B], g: B => State[S, C], a: A): State[S, C] = { + for { + b <- f(a) + c <- g(b) + } yield c + } + + /* Monadic-List functions */ + + def mapM[A, B, S](f: A => State[S, B], l: List[A]): State[S, List[B]] = { + l match { + case Nil() => unit(Nil[B]()) + case Cons(x, xs) => for { + mx <- f(x) + mxs <- mapM(f,xs) + } yield mx :: mxs + } + } + + def mapM_ [A, B, S] (f: A => State[S, B], l: List[A]): State[S, Unit] = { + l match { + case Nil() => unit(()) + case Cons(x, xs) => for { + mx <- f(x) + mxs <- mapM_ (f, xs) + } yield () + } + } + + def sequence[S, A](l: List[State[S, A]]): State[S, List[A]] = { + l.foldRight[State[S, List[A]]](unit(Nil[A]())){ + case (x, xs) => for { + v <- x + vs <- xs + } yield v :: vs + } + } + + def filterM[S, A](f: A => State[S, Boolean], l: List[A]): State[S, List[A]] = { + l match { + case Nil() => unit(Nil[A]()) + case Cons(x, xs) => for { + flg <- f(x) + rest <- filterM(f, xs) + } yield (if (flg) x :: rest else rest) + } + } + + //(b -> a -> m b) -> b -> t a -> m b + def foldLeftM[S, A, B](f: (B, A) => State[S, B], z: B, l: List[A]): State[S, B] = { + l match { + case Nil() => unit(z) + case Cons(x, xs) => + f(z, x) >>= ( z0 => foldLeftM(f,z0,xs) ) + } + } + + //(b -> a -> m b) -> b -> t a -> m () + def foldLeftM_ [S, A](f: A => State[S, Unit], l: List[A]): State[S, Unit] = { + l match { + case Nil() => unit(()) + case Cons(x, xs) => + f(x) >> foldLeftM_ (f, xs) + } + } + +} + +@library +object MonadStateLaws { + /* Monadic laws: + * + * return a >>= k = k a + * m >>= return = m + * m >>= (x -> k x >>= h) = (m >>= k) >>= h + * + * Note: We cannot compare State's directly because State contains an anonymous function. + * So we have to run them on an initial state. + */ + + def unitLeftId[A, B, S](a: A, k: A => State[S, B])(s: S): Boolean = { + (unit(a) >>= (k))(s) == k(a)(s) + }.holds + + def unitRightId[S, A](m: State[S,A])(s:S): Boolean = { + (m >>= unit).runState(s) == m.runState(s) + }.holds + + def assoc[S, A, B, C](m: State[S,A], k: A => State[S, B], h: B => State[S, C])(s: S) = { + (m >>= (x => k(x) >>= h)).runState(s) == (m >>= k >>= h).runState(s) + }.holds + + /* This is the same as assoc, but proves in 42sec instead of 50ms! + def assoc2[S, A, B, C](m: State[S,A], k: A => State[S, B], h: B => State[S, C])(s: S) = { + val lhs = for { + x <- m + y <- for { + z <- k(x) + w <- h(z) + } yield w + } yield y + + val rhs = for { + x <- m + y <- k(x) + z <- h(y) + } yield z + + lhs.runState(s) == rhs.runState(s) + }.holds + */ + + + /* The law connecting monad and functor instances: + * + * m >>= (return . f) = m map f + */ + + def mapToFlatMap[S, A, B](m: State[S, A], f: A => B)(s: S): Boolean = { + (m >>= (x => unit(f(x)))).runState(s) == (m map f).runState(s) + }.holds + + + /* Different theorems */ + + //@induct + //def mapMVsSequenceM[S, A, B](f: A => State[S, B], l: List[A])(s: S) = { + // mapM(f, l).runState(s) == sequence(l map f).runState(s) + //}.holds + + +} diff --git a/libtest/src/main/scala/leon/package.scala b/libtest/src/main/scala/leon/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..357010d11f8b1b4ae4fe0e9076a087029a0fd089 --- /dev/null +++ b/libtest/src/main/scala/leon/package.scala @@ -0,0 +1,23 @@ +import leon.annotation._ +import scala.annotation.elidable +import scala.annotation.elidable._ + +/** + * A collection of methods that can be used to collect run-time statistics about Leon programs. + * This is mostly used to test the resources properties of Leon programs + */ +package object leon { + + /** + * Stubs of ensuring and require that do not check contracts. + * Enable this if you want running time statistics without executing contracts. + */ + @ignore + implicit class Ensuring[A](private val self: A) extends AnyVal { + @inline + def ensuring(cond: A => Boolean): A = { self } + } + + @ignore + def require(arg: => Boolean) {} +} diff --git a/libtest/src/main/scala/leon/par/package.scala b/libtest/src/main/scala/leon/par/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..5842210fce915df2c02a0f69b6000703c8865af9 --- /dev/null +++ b/libtest/src/main/scala/leon/par/package.scala @@ -0,0 +1,24 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import leon.annotation._ +import leon.lang._ +import leon.lang.synthesis.choose + +package object par { + + @library + @inline + def parallel[A,B](x: => A, y: => B) : (A,B) = { + (x,y) + } + + @library + case class Task[A](c: A) { + def join: A = c + } + + @library + def task[A](c: A) = Task(c) +} diff --git a/libtest/src/main/scala/leon/proof/Internal.scala b/libtest/src/main/scala/leon/proof/Internal.scala new file mode 100644 index 0000000000000000000000000000000000000000..0962c371c670d2b08db797a9f24542195c78bac8 --- /dev/null +++ b/libtest/src/main/scala/leon/proof/Internal.scala @@ -0,0 +1,63 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ +package leon.proof + +import leon.lang._ +import leon.annotation._ + +/** Internal helper classes and methods for the 'proof' package. */ +object Internal { + + /*** Helper classes for relational reasoning ***/ + @library + case class WithRel[A, B](x: A, r: (A, B) => Boolean, prop: Boolean) { + + /** Continue with the next relation. */ + def ^^(y: B): RelReasoning[B] = { + require(prop ==> r(x, y)) + RelReasoning(y, prop && r(x, y)) + } + + /** Add a proof. */ + def ^^|(proof: Boolean): WithProof[A, B] = { + require(proof) + WithProof(x, r, proof, prop) + } + + /** Short-hand for equational reasoning. */ + def ==|(proof: Boolean): WithProof[A, A] = { + require(proof) + WithProof(x, _ == _, proof, prop) + } + + def qed: Boolean = prop + } + + @library + case class WithProof[A, B]( + x: A, r: (A, B) => Boolean, proof: Boolean, prop: Boolean) { + + /** Close a proof. */ + def |[C](that: WithProof[B, C]): WithProof[B, C] = { + require(this.prop && this.proof ==> this.r(this.x, that.x)) + WithProof( + that.x, that.r, that.proof, + this.prop && this.proof && this.r(this.x, that.x)) + } + + /** Close a proof. */ + def |[C](that: WithRel[B, C]): WithRel[B, C] = { + require(this.prop && this.proof ==> this.r(this.x, that.x)) + WithRel( + that.x, that.r, + this.prop && this.proof && this.r(this.x, that.x)) + } + + /** Close a proof. */ + def |(that: RelReasoning[B]): RelReasoning[B] = { + require(this.prop && this.proof ==> this.r(this.x, that.x)) + RelReasoning( + that.x, + this.prop && this.proof && this.r(this.x, that.x)) + } + } +} diff --git a/libtest/src/main/scala/leon/proof/package.scala b/libtest/src/main/scala/leon/proof/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..3f097864964a8d2d724a8ec1256dd803c5c048c5 --- /dev/null +++ b/libtest/src/main/scala/leon/proof/package.scala @@ -0,0 +1,66 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ +package leon + +import leon.annotation._ +import leon.lang._ +import scala.language.implicitConversions + +import leon.proof.Internal._ + +package object proof { + + @library + case class ProofOps(prop: Boolean) { + def because(proof: Boolean): Boolean = proof && prop + def neverHolds: Boolean = { + require(!prop) + !prop + } + } + + @library + implicit def boolean2ProofOps(prop: Boolean): ProofOps = ProofOps(prop) + + @library + def trivial: Boolean = true + + @library + def by(proof: Boolean)(prop: Boolean): Boolean = + proof && prop + + @library + def check(prop: Boolean): Boolean = { + require(prop) + prop + } + + /** + * Relational reasoning. + * + * { + * ((y :: ys) :+ x).last ^^ _ == _ ^^| trivial | + * (y :: (ys :+ x)).last ==| trivial | + * (ys :+ x).last ==| snocLast(ys, x) | + * x + * }.qed + */ + @library + case class RelReasoning[A](x: A, prop: Boolean) { + + def ^^[B](r: (A, B) => Boolean): WithRel[A, B] = WithRel(x, r, prop) + + /** Short-hand for equational reasoning. */ + def ==|(proof: Boolean): WithProof[A, A] = { + require(proof) + WithProof(x, _ == _, proof, prop) + } + + def qed: Boolean = prop + } + + @library + implicit def any2RelReasoning[A](x: A): RelReasoning[A] = + RelReasoning(x, true) + +} + diff --git a/libtest/src/main/scala/leon/stats/package.scala b/libtest/src/main/scala/leon/stats/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..17d6286b62c3e4de4be5d4c430ea74e26a3abef0 --- /dev/null +++ b/libtest/src/main/scala/leon/stats/package.scala @@ -0,0 +1,28 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon + +import leon.annotation._ +import java.lang.management._ +import scala.collection.JavaConversions._ + +/** + * A collection of methods that can be used to collect run-time statistics about Leon programs. + * This is mostly used to test the resources properties of Leon programs + */ +package object stats { + @ignore + def timed[T](code: => T)(cont: Long => Unit): T = { + var t1 = System.currentTimeMillis() + val r = code + cont((System.currentTimeMillis() - t1)) + r + } + + @ignore + def withTime[T](code: => T): (T, Long) = { + var t1 = System.currentTimeMillis() + val r = code + (r, (System.currentTimeMillis() - t1)) + } +} diff --git a/run-benchs.sh b/run-benchs.sh deleted file mode 100755 index f269ac5649d2d70489ce664f80808d43a96a4c55..0000000000000000000000000000000000000000 --- a/run-benchs.sh +++ /dev/null @@ -1,12 +0,0 @@ -./leon --lazy ./testcases/lazy-datastructures/RealTimeQueue.scala | tee RTQ.out -./leon --lazy ./testcases/lazy-datastructures/BottomUpMegeSort.scala | tee MsortBU.out -./leon --lazy ./testcases/lazy-datastructures/SortingnConcat.scala | tee Sort.out -./leon --lazy ./testcases/lazy-datastructures/Esop15Benchmarks.scala | tee Esop.out -./leon --lazy ./testcases/lazy-datastructures/Deque.scala --unfoldFactor=2 | tee RDQ.out -./leon --lazy ./testcases/lazy-datastructures/LazyNumericalRep.scala --unfoldFactor=3 | tee Num.out -./leon --lazy ./testcases/lazy-datastructures/conc/Conqueue.scala --unfoldFactor=3 | tee Conqueue.out -./leon --lazy ./testcases/lazy-datastructures/memoization/Knapsack.scala | tee Knapsack.out -./leon --lazy ./testcases/lazy-datastructures/memoization/WeightedScheduling.scala | tee Sched.out -./leon --lazy ./testcases/lazy-datastructures/memoization/HammingMemoized.scala | tee Hamming.out -./leon --lazy ./testcases/lazy-datastructures/memoization/PackratParsing.scala | tee Packrat.out - diff --git a/src/main/scala/leon/invariant/engine/CompositionalTimeBoundSolver.scala b/src/main/scala/leon/invariant/engine/CompositionalTimeBoundSolver.scala index 1992607fa3dfed9e64ae4e920fd683e973f62f17..7713195169d4732fba1e2961b548a908516d16ec 100644 --- a/src/main/scala/leon/invariant/engine/CompositionalTimeBoundSolver.scala +++ b/src/main/scala/leon/invariant/engine/CompositionalTimeBoundSolver.scala @@ -45,7 +45,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, prog: Program, rootFd: val origProg = prog // add only rec templates for all functions - val funToRecTmpl = origProg.definedFunctions.collect { + val funToRecTmpl = userLevelFunctions(origProg).collect { case fd if fd.hasTemplate && fd == rootFd => fd -> recTmpl case fd if fd.hasTemplate => @@ -54,7 +54,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, prog: Program, rootFd: val recProg = assignTemplateAndCojoinPost(funToRecTmpl, origProg) // add only tpr template for all functions - val funToNonRecTmpl = origProg.definedFunctions.collect { + val funToNonRecTmpl = userLevelFunctions(origProg).collect { case fd if fd.hasTemplate && fd == rootFd => fd -> tprTmpl case fd if fd.hasTemplate => @@ -73,7 +73,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, prog: Program, rootFd: case (Some(InferResult(true, Some(recModel), _)), Some(InferResult(true, Some(tprModel), _))) => // create a new program by omitting the templates of the root function - val funToTmpl = origProg.definedFunctions.collect { + val funToTmpl = userLevelFunctions(origProg).collect { case fd if fd.hasTemplate && fd != rootFd => (fd -> fd.getTemplate) }.toMap @@ -83,14 +83,14 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, prog: Program, rootFd: // construct the instantiated tpr bound and check if it monotonically decreases val Operator(Seq(_, tprFun), _) = tprTmpl - val tprFunInst = (new RealToInt()).mapRealToInt( + val tprFunInst = (new RealToInt()).mapExpr( replace(tprModel.map { case (k, v) => (k.toVariable -> v) }.toMap, tprFun)) // TODO: this would fail on non-integers, handle these by approximating to the next bigger integer // Upper bound on time time <= recFun * tprFun + tprFun val (_, multFun) = MultFuncs.getMultFuncs(if (ctx.usereals) RealType else IntegerType) val Operator(Seq(_, recFun), _) = recTmpl - val recFunInst = (new RealToInt()).mapRealToInt( + val recFunInst = (new RealToInt()).mapExpr( replace(recModel.map { case (k, v) => (k.toVariable -> v) }.toMap, recFun)) val timeUpperBound = ExpressionTransformer.normalizeMultiplication( @@ -141,21 +141,6 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, prog: Program, rootFd: } } - /*def combineMapsUsingConjunction(maps: List[Map[FunDef, Expr]], prog: Program) = { - val combMap = new OrderedMultiMap[FunDef, Expr] - maps.foreach { - _.foreach { - case (k, v) => - val origFun = functionByName(k.id.name, prog).get - combMap.addBinding(origFun, v) - } - } - combMap.foldLeft(Map[FunDef, Expr]()) { - case (acc, (k, vs)) if vs.size == 1 => acc + (k -> vs(0)) - case (acc, (k, vs)) => acc + (k -> And(vs.toSeq)) - } - }*/ - def extractSeparateTemplates(funDef: FunDef): (Option[Expr], Option[Expr], Option[Expr], Seq[Expr]) = { if (!funDef.hasTemplate) (None, None, None, Seq[Expr]()) else { diff --git a/src/main/scala/leon/invariant/engine/InferenceContext.scala b/src/main/scala/leon/invariant/engine/InferenceContext.scala index 48a45b048a33691f368242b63b45ae5bc062881c..c8020acb5f9ff6e3582a258fe0c8806745a533a5 100644 --- a/src/main/scala/leon/invariant/engine/InferenceContext.scala +++ b/src/main/scala/leon/invariant/engine/InferenceContext.scala @@ -45,9 +45,9 @@ class InferenceContext(val initProgram: Program, val leonContext: LeonContext) { val statsSuffix = leonContext.findOption(optStatsSuffix).getOrElse("-stats" + FileCountGUID.getID) val instrumentedProg = InstrumentationPhase(leonContext, initProgram) - // convets qmarks to templates + // converts qmarks to templates val qMarksRemovedProg = { - val funToTmpl = instrumentedProg.definedFunctions.collect { + val funToTmpl = userLevelFunctions(instrumentedProg).collect { case fd if fd.hasTemplate => fd -> fd.getTemplate }.toMap @@ -58,9 +58,7 @@ class InferenceContext(val initProgram: Program, val leonContext: LeonContext) { val inferProgram = { // convert nonlinearity to recursive functions - nlelim(if (usereals) - (new IntToRealProgram())(qMarksRemovedProg) - else qMarksRemovedProg) + nlelim(if (usereals) (new IntToRealProgram())(qMarksRemovedProg) else qMarksRemovedProg) } // other utilities diff --git a/src/main/scala/leon/invariant/engine/InferenceEngine.scala b/src/main/scala/leon/invariant/engine/InferenceEngine.scala index 3e7f2fa3fe143c18ddbf4d4cc43294e9f824caa1..17c246b47c9cbca0dc70f2c8832ae9d8172d6180 100644 --- a/src/main/scala/leon/invariant/engine/InferenceEngine.scala +++ b/src/main/scala/leon/invariant/engine/InferenceEngine.scala @@ -140,9 +140,9 @@ class InferenceEngine(ctx: InferenceContext) extends Interruptible { // not usef for now /*val tempFactory = new TemplateFactory(Some(new TemplateEnumerator(ctx, startProg)), startProg, ctx.reporter)*/ - startProg.definedFunctions.map(fd => fd -> getOrCreateTemplateForFun(fd)).toMap + userLevelFunctions(startProg).map(fd => fd -> getOrCreateTemplateForFun(fd)).toMap } else - startProg.definedFunctions.collect { case fd if fd.hasTemplate => fd -> fd.getTemplate }.toMap + userLevelFunctions(startProg).collect { case fd if fd.hasTemplate => fd -> fd.getTemplate }.toMap val progWithTemplates = assignTemplateAndCojoinPost(funToTmpl, startProg) var analyzedSet = Map[FunDef, InferenceCondition]() @@ -191,7 +191,7 @@ class InferenceEngine(ctx: InferenceContext) extends Interruptible { // now the templates of these functions will be replaced by inferred invariants val invs = TemplateInstantiator.getAllInvariants(model.get, funsWithTemplates) // collect templates of remaining functions - val funToTmpl = prog.definedFunctions.collect { + val funToTmpl = userLevelFunctions(prog).collect { case fd if !invs.contains(fd) && fd.hasTemplate => fd -> fd.getTemplate }.toMap diff --git a/src/main/scala/leon/invariant/factories/TemplateFactory.scala b/src/main/scala/leon/invariant/factories/TemplateFactory.scala index ea0f0c63368aaaac74470811265b9f99d46ea883..110ac75cb47dd73d2f8b871faaa18105b5d1a825 100644 --- a/src/main/scala/leon/invariant/factories/TemplateFactory.scala +++ b/src/main/scala/leon/invariant/factories/TemplateFactory.scala @@ -30,7 +30,7 @@ object TemplateIdFactory { } def copyIdentifier(id: Identifier) : Identifier = { - val freshid = FreshIdentifier(id.name, RealType, false) + val freshid = FreshIdentifier(id.name, id.getType, false) ids += freshid freshid } diff --git a/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala b/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala index aee689be4bd1a1f89760272220e71132cbba8549..0f2c84350f95c9f5f3383a6bf55af3f5cfdf9500 100644 --- a/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala +++ b/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala @@ -204,7 +204,7 @@ object LinearConstraintUtil { || e.isInstanceOf[GreaterEquals])) => { //check if the expression has real valued sub-expressions - val isReal = hasReals(e1) || hasReals(e2) + val isReal = hasRealsOrTemplates(e1) || hasRealsOrTemplates(e2) val (newe, newop) = e match { case t: Equals => (Minus(e1, e2), Equals) case t: LessEquals => (Minus(e1, e2), LessEquals) diff --git a/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala b/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala index a3eca287b0d221af93120b1578020e8baa7a5fc9..0465199421bf61a5721fad33e2a94795f6ef8406 100644 --- a/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala @@ -120,8 +120,6 @@ class CegisCore(ctx: InferenceContext, val spuriousTempIds = variablesOf(instFormula).intersect(TemplateIdFactory.getTemplateIds) if (spuriousTempIds.nonEmpty) throw new IllegalStateException("Found a template variable in instFormula: " + spuriousTempIds) - if (hasReals(instFormula)) - throw new IllegalStateException("Reals in instFormula: " + instFormula) //println("solving instantiated vcs...") val solver1 = new ExtendedUFSolver(context, program) @@ -158,16 +156,11 @@ class CegisCore(ctx: InferenceContext, val solver2 = SimpleSolverAPI(new TimeoutSolverFactory(SolverFactory(() => new ExtendedUFSolver(context, program) with TimeoutSolver), timeoutMillis - elapsedTime)) val (res1, newModel) = if (solveAsInt) { - //convert templates to integers and solve. Finally, re-convert integer models for templates to real models - val rti = new RealToInt() - val intctr = rti.mapRealToInt(And(newctr, initRealCtr)) - val intObjective = rti.mapRealToInt(tempVarSum) - val (res1, intModel) = - if (minimizeSum) - minimizeIntegers(intctr, intObjective) - else - solver2.solveSAT(intctr) - (res1, rti.unmapModel(intModel)) + val intctr = And(newctr, initRealCtr) + if (minimizeSum) + minimizeIntegers(intctr, tempVarSum) + else + solver2.solveSAT(intctr) } else { if (minimizeSum) { minimizeReals(And(newctr, initRealCtr), tempVarSum) diff --git a/src/main/scala/leon/invariant/templateSolvers/DisjunctChooser.scala b/src/main/scala/leon/invariant/templateSolvers/DisjunctChooser.scala index b3a935124bbe609bc423e49404336fd7cf20ef7c..27d83ef562a671045d8c7091caa2437af8af0d3c 100644 --- a/src/main/scala/leon/invariant/templateSolvers/DisjunctChooser.scala +++ b/src/main/scala/leon/invariant/templateSolvers/DisjunctChooser.scala @@ -181,7 +181,7 @@ class DisjunctChooser(ctx: InferenceContext, program: Program, ctrTracker: Const if (debugElimination && verifyInvariant) { println("checking invariant for disjunct before elimination...") checkInvariant(createAnd((lnctrs ++ temps).map(_.template)), leonctx, program) - } + } // for debugging val debugger = if (debugElimination && verifyInvariant) { diff --git a/src/main/scala/leon/invariant/templateSolvers/ExistentialQuantificationSolver.scala b/src/main/scala/leon/invariant/templateSolvers/ExistentialQuantificationSolver.scala index 5af100ed7037832291416229fc690b215a6acca4..a2ad660104332fdce5c436fae93dbbfc692f9724 100644 --- a/src/main/scala/leon/invariant/templateSolvers/ExistentialQuantificationSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/ExistentialQuantificationSolver.scala @@ -33,7 +33,7 @@ import SolverUtil._ /** * This class uses Farkas' lemma to try and falsify numerical disjuncts with templates provided one by one */ -class ExistentialQuantificationSolver(ctx: InferenceContext, program: Program, +class ExistentialQuantificationSolver(ctx: InferenceContext, program: Program, ctrTracker: ConstraintTracker, defaultEval: DefaultEvaluator) { import NLTemplateSolver._ val reporter = ctx.reporter @@ -41,9 +41,9 @@ class ExistentialQuantificationSolver(ctx: InferenceContext, program: Program, var currentCtr: Expr = tru private val farkasSolver = new FarkasLemmaSolver(ctx, program) val disjunctChooser = new DisjunctChooser(ctx, program, ctrTracker, defaultEval) - - def getSolvedCtrs = currentCtr - + + def getSolvedCtrs = currentCtr + def generateCtrsForUNSAT(fd: FunDef, univModel: LazyModel, tempModel: Model) = { // chooose a sat numerical disjunct from the model val (lnctrs, temps, calls) = @@ -53,11 +53,11 @@ class ExistentialQuantificationSolver(ctx: InferenceContext, program: Program, updateCounterTime(chTime, "Disj-choosing-time", "disjuncts") updateCumTime(chTime, "Total-Choose-Time") } - val disjunct = (lnctrs ++ temps) + val disjunct = (lnctrs ++ temps) if (temps.isEmpty) { //here ants ^ conseq is sat (otherwise we wouldn't reach here) and there is no way to falsify this path (fls, disjunct, calls) - } else + } else (farkasSolver.constraintsForUnsat(lnctrs, temps), disjunct, calls) } @@ -68,7 +68,6 @@ class ExistentialQuantificationSolver(ctx: InferenceContext, program: Program, val newPart = createAnd(newctrs) val newSize = atomNum(newPart) val currSize = atomNum(currentCtr) - Stats.updateCounterStats((newSize + currSize), "NLsize", "disjuncts") if (verbose) reporter.info("# of atomic predicates: " + newSize + " + " + currSize) @@ -86,7 +85,7 @@ class ExistentialQuantificationSolver(ctx: InferenceContext, program: Program, (Some(false), Model.empty) case Some(true) => currentCtr = combCtr - //new model may not have mappings for all the template variables, hence, use the mappings from earlier models + //new model may not have mappings for all the template variables, hence, use the mappings from earlier models (Some(true), completeWithRefModel(newModel, oldModel)) } } diff --git a/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala b/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala index 62eabca927059aa61c54a341d5339385a5c26e42..96d57a83403a9f4b43a21de0173a4bd25adf1aeb 100644 --- a/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala @@ -180,13 +180,11 @@ class FarkasLemmaSolver(ctx: InferenceContext, program: Program) { //the final constraint is a conjunction of lambda constraints and disjunction of enabled and disabled parts if (disableAnts) And(createAnd(lambdaCtrs), disabledPart) else { - //And(And(lambdaCtrs), enabledPart) And(createAnd(lambdaCtrs), Or(enabledPart, disabledPart)) } } val ctrs = if (disableAnts) { - //here conseqs are empty createCtrs(None) } else { val Seq(head, tail @ _*) = conseqs @@ -316,10 +314,8 @@ class FarkasLemmaSolver(ctx: InferenceContext, program: Program) { } val fullmodel = model ++ newassignments if (this.verifyModel) { - //println("Fullmodel: "+fullmodel) - assert(evaluateRealFormula(replace( - fullmodel.map { case (k, v) => (k.toVariable, v) }.toMap, - nlctrs))) + val formula = replace(fullmodel.map { case (k, v) => (k.toVariable, v)}.toMap, nlctrs) + assert(evaluateRealFormula(formula)) } (res, fullmodel) case _ => diff --git a/src/main/scala/leon/invariant/templateSolvers/UniversalQuantificationSolver.scala b/src/main/scala/leon/invariant/templateSolvers/UniversalQuantificationSolver.scala index bb39630784f3af54776b9f884acf1028c17cd139..b22f402ae5180edcb7efb5ec7e9997e9c1eaf174 100644 --- a/src/main/scala/leon/invariant/templateSolvers/UniversalQuantificationSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/UniversalQuantificationSolver.scala @@ -2,6 +2,7 @@ package leon package invariant.templateSolvers import z3.scala._ +import purescala._ import purescala.Common._ import purescala.Definitions._ import purescala.Expressions._ @@ -245,8 +246,9 @@ class UniversalQuantificationSolver(ctx: InferenceContext, program: Program, Stats.updateCounter(1, "disjuncts") if (verbose) { reporter.info("Candidate invariants") - TemplateInstantiator.getAllInvariants(tempModel, ctrTracker.getFuncs).foreach( - entry => reporter.info(entry._1.id + "-->" + entry._2)) + TemplateInstantiator.getAllInvariants(tempModel, ctrTracker.getFuncs).foreach{ + case(f, inv) => reporter.info(f.id + "-->" + PrettyPrinter(inv)) + } } val modRefiner = new ModelRefiner(tempModel) sat = modRefiner.nextCandidate match { diff --git a/src/main/scala/leon/invariant/util/ExpressionTransformer.scala b/src/main/scala/leon/invariant/util/ExpressionTransformer.scala index 5de31ddac39d0529ea1e715a747581df831ae198..716b977bf9efe379d451a4421ef6781ba3faccae 100644 --- a/src/main/scala/leon/invariant/util/ExpressionTransformer.scala +++ b/src/main/scala/leon/invariant/util/ExpressionTransformer.scala @@ -141,38 +141,6 @@ object ExpressionTransformer { val (resvalue, valuecjs) = transform(value, true) (resbody, (valuecjs + Equals(binder.toVariable, resvalue)) ++ bodycjs) - //the value is a tuple in the following case - /*case LetTuple(binders, value, body) => - //TODO: do we have to consider reuse of let variables ? - val (resbody, bodycjs) = transform(body, true) - val (resvalue, valuecjs) = transform(value, true) - //here we optimize the case where resvalue itself has tuples - val newConjuncts = resvalue match { - case Tuple(args) => { - binders.zip(args).map((elem) => { - val (bind, arg) = elem - Equals(bind.toVariable, arg) - }) - } - case _ => { - //may it is better to assign resvalue to a temporary variable (if it is not already a variable) - val (resvalue2, cjs) = resvalue match { - case t: Terminal => (t, Seq()) - case _ => { - val freshres = createTemp("tres", resvalue.getType, langContext).toVariable - (freshres, Seq(Equals(freshres, resvalue))) - } - } - var i = 0 - val cjs2 = binders.map((bind) => { - i += 1 - Equals(bind.toVariable, TupleSelect(resvalue2, i)) - }) - (cjs ++ cjs2) - } - } - (resbody, (valuecjs ++ newConjuncts) ++ bodycjs)*/ - case _ => conjoinWithinClause(e, transform, insideFunction) } } diff --git a/src/main/scala/leon/invariant/util/RealIntMap.scala b/src/main/scala/leon/invariant/util/RealIntMap.scala new file mode 100644 index 0000000000000000000000000000000000000000..12ba7d97ceffc033b73e42a33ed8b49c65c30fff --- /dev/null +++ b/src/main/scala/leon/invariant/util/RealIntMap.scala @@ -0,0 +1,111 @@ +package leon +package invariant.util + +import purescala.Common._ +import purescala.Expressions._ +import purescala.ExprOps._ +import purescala.Types._ +import invariant.factories._ +import solvers._ +import TemplateIdFactory._ + +/** + * An abstract class for mapping integer typed variables to reals and vice-versa. + * Note: this preserves the template identifier property + */ +abstract class IntRealMap { + var oldToNew = Map[Identifier, Identifier]() + var newToOld = Map[Identifier, Identifier]() + + def mapLiteral(l: Literal[_]): Literal[_] + def unmapLiteral(l: Literal[_]): Literal[_] + def mapIdentifier(v: Identifier): Identifier + + def mapExpr(inexpr: Expr): Expr = { + val transformer = (e: Expr) => e match { + case l : Literal[_] => mapLiteral(l) + case v@Variable(id) => + Variable(oldToNew.getOrElse(id, { + val mid = mapIdentifier(id) + if (mid != id) { + oldToNew += (id -> mid) + newToOld += (mid -> id) + } + mid + })) + case _ => e + } + simplePostTransform(transformer)(inexpr) + } + + def unmapModel(model: Model): Model = { + new Model(model.map { + case (key, value) if (newToOld.contains(key)) => + (newToOld(key), + value match { + case l: Literal[_] => unmapLiteral(l) + case e => e + }) + case other => other + }.toMap) + } + + def mapModel(model: Model): Model = { + new Model(model.map { + case (k, v) if (oldToNew.contains(k)) => + (oldToNew(k), v match { + case l: Literal[_] => mapLiteral(l) + case e => e + }) + case other => other + }.toMap) + } +} + +/** + * maps all real valued variables and literals to new integer variables/literals and + * performs the reverse mapping + */ +class RealToInt extends IntRealMap { + + val bone = BigInt(1) + def mapLiteral(l: Literal[_]): Literal[_] = l match { + case FractionalLiteral(num, `bone`) => InfiniteIntegerLiteral(num) + case FractionalLiteral(_, _) => throw new IllegalStateException("Real literal with non-unit denominator") + case other => other + } + + def unmapLiteral(l: Literal[_]): Literal[_] = l match { + case InfiniteIntegerLiteral(v) => FractionalLiteral(v.toInt, 1) + case other => other + } + + def mapIdentifier(v: Identifier): Identifier = + if (v.getType == RealType) { + if (IsTemplateIdentifier(v)) freshIdentifier(v.name) + else FreshIdentifier(v.name, IntegerType, true) + } else v +} + +/** + * Maps integer literal and identifiers to real literal and identifiers + */ +/*class IntToReal extends IntRealMap { + + def mapLiteral(l: Literal[_]): Literal[_] = l match { + case InfiniteIntegerLiteral(v) => FractionalLiteral(v.toInt, 1) + case other => other + } + + *//** + * Here, we return fractional literals for integer-valued variables, + * and leave to the client to handle them + *//* + def unmapLiteral(l: Literal[_]): Literal[_] = l + + def mapIdentifier(v: Identifier): Identifier = + if (v.getType == IntegerType) { + if (IsTemplateIdentifier(v)) freshIdentifier(v.name, RealType) + else FreshIdentifier(v.name, RealType, true) + } else v +}*/ \ No newline at end of file diff --git a/src/main/scala/leon/invariant/util/RealToInt.scala b/src/main/scala/leon/invariant/util/RealToInt.scala deleted file mode 100644 index 6c1860160f2bebc5bf0cb574b9c9934985246f45..0000000000000000000000000000000000000000 --- a/src/main/scala/leon/invariant/util/RealToInt.scala +++ /dev/null @@ -1,70 +0,0 @@ -package leon -package invariant.util - -import purescala.Common._ -import purescala.Expressions._ -import purescala.ExprOps._ -import purescala.Types._ -import invariant.factories._ -import solvers._ - -/** - * maps all real valued variables and literals to new integer variables/literals and - * performs the reverse mapping - * Note: this should preserve the template identifier property - */ -class RealToInt { - - val bone = BigInt(1) - var realToIntId = Map[Identifier, Identifier]() - var intToRealId = Map[Identifier, Identifier]() - - def mapRealToInt(inexpr: Expr): Expr = { - val transformer = (e: Expr) => e match { - case FractionalLiteral(num, `bone`) => InfiniteIntegerLiteral(num) - case FractionalLiteral(_, _) => throw new IllegalStateException("Real literal with non-unit denominator") - case v @ Variable(realId) if (v.getType == RealType) => { - val newId = realToIntId.getOrElse(realId, { - //note: the fresh identifier has to be a template identifier if the original one is a template identifier - val freshId = if (TemplateIdFactory.IsTemplateIdentifier(realId)) - TemplateIdFactory.freshIdentifier(realId.name, IntegerType) - else - FreshIdentifier(realId.name, IntegerType, true) - - realToIntId += (realId -> freshId) - intToRealId += (freshId -> realId) - freshId - }) - Variable(newId) - } - case _ => e - } - simplePostTransform(transformer)(inexpr) - } - - def unmapModel(model: Model): Model = { - new Model(model.map(pair => { - val (key, value) = if (intToRealId.contains(pair._1)) { - (intToRealId(pair._1), - pair._2 match { - case InfiniteIntegerLiteral(v) => FractionalLiteral(v.toInt, 1) - case _ => pair._2 - }) - } else pair - (key -> value) - }).toMap) - } - - def mapModel(model: Model): Model = { - new Model(model.collect { - case (k, FractionalLiteral(n, bone)) => - (realToIntId(k), InfiniteIntegerLiteral(n)) - case (k, v) => - if (realToIntId.contains(k)) { - (realToIntId(k), v) - } else { - (k, v) - } - }.toMap) - } -} \ No newline at end of file diff --git a/src/main/scala/leon/invariant/util/RealValuedExprEvaluator.scala b/src/main/scala/leon/invariant/util/RealValuedExprEvaluator.scala index d591de24533d0f26fdfaae35386a87d1b936b617..9fd5c324eab5719fe47981d807ed8005138e67ff 100644 --- a/src/main/scala/leon/invariant/util/RealValuedExprEvaluator.scala +++ b/src/main/scala/leon/invariant/util/RealValuedExprEvaluator.scala @@ -47,12 +47,14 @@ object RealValuedExprEvaluator { case _ => throw new IllegalStateException("Not an evaluatable expression: " + expr) } - def evaluateRealPredicate(expr: Expr): Boolean = expr match { - case Equals(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isEQZ(evaluate(Minus(a, b))) - case LessEquals(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isLEZ(evaluate(Minus(a, b))) - case LessThan(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isLTZ(evaluate(Minus(a, b))) - case GreaterEquals(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isGEZ(evaluate(Minus(a, b))) - case GreaterThan(a @ FractionalLiteral(n1, d1), b @ FractionalLiteral(n2, d2)) => isGTZ(evaluate(Minus(a, b))) + def evaluateRealPredicate(expr: Expr): Boolean = { + expr match { + case Equals(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isEQZ(evaluate(Minus(a, b))) + case LessEquals(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isLEZ(evaluate(Minus(a, b))) + case LessThan(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isLTZ(evaluate(Minus(a, b))) + case GreaterEquals(a @ FractionalLiteral(_, _), b @ FractionalLiteral(_, _)) => isGEZ(evaluate(Minus(a, b))) + case GreaterThan(a @ FractionalLiteral(n1, d1), b @ FractionalLiteral(n2, d2)) => isGTZ(evaluate(Minus(a, b))) + } } def isEQZ(rlit: FractionalLiteral): Boolean = { @@ -95,6 +97,9 @@ object RealValuedExprEvaluator { case Not(arg) => !evaluateRealFormula(arg) case BooleanLiteral(b) => b case Operator(args, op) => - evaluateRealPredicate(op(args map evaluate)) + op(args map evaluate) match { + case BooleanLiteral(b) => b + case p => evaluateRealPredicate(p) + } } } diff --git a/src/main/scala/leon/invariant/util/TreeUtil.scala b/src/main/scala/leon/invariant/util/TreeUtil.scala index 88dabc505faf476cef5e6fae516e6d90e093d676..df6f64b0c7eda9640a77de2b4b9d2bf847eccc4c 100644 --- a/src/main/scala/leon/invariant/util/TreeUtil.scala +++ b/src/main/scala/leon/invariant/util/TreeUtil.scala @@ -117,22 +117,23 @@ object ProgramUtil { } /** - * For functions for which `funToTmpl` is not defined, their templates - * will be removed + * For functions for which `funToTmpl` is not defined, their templates will be removed. + * Will only consider user-level functions. */ def assignTemplateAndCojoinPost(funToTmpl: Map[FunDef, Expr], prog: Program, funToPost: Map[FunDef, Expr] = Map(), uniqueIdDisplay: Boolean = false): Program = { - val funMap = functionsWOFields(prog.definedFunctions).foldLeft(Map[FunDef, FunDef]()) { - case (accMap, fd) if fd.isTheoryOperation => - accMap + (fd -> fd) + val keys = funToTmpl.keySet ++ funToPost.keySet + val userLevelFuns = userLevelFunctions(prog).toSet + if(!keys.diff(userLevelFuns).isEmpty) + throw new IllegalStateException("AssignTemplate function called on library functions: "+ keys.diff(userLevelFuns)) + + val funMap = userLevelFuns.foldLeft(Map[FunDef, FunDef]()) { case (accMap, fd) => { val freshId = FreshIdentifier(fd.id.name, fd.returnType, uniqueIdDisplay) - val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) - accMap.updated(fd, newfd) + accMap + (fd -> new FunDef(freshId, fd.tparams, fd.params, fd.returnType)) } } - val mapExpr = mapFunctionsInExpr(funMap) _ for ((from, to) <- funMap) { to.fullBody = if (!funToTmpl.contains(from)) { @@ -185,17 +186,12 @@ object ProgramUtil { newprog } - def updatePost(funToPost: Map[FunDef, Lambda], prog: Program, - uniqueIdDisplay: Boolean = true, excludeLibrary: Boolean = true): Program = { + def updatePost(funToPost: Map[FunDef, Lambda], prog: Program, uniqueIdDisplay: Boolean = true): Program = { - val funMap = functionsWOFields(prog.definedFunctions).foldLeft(Map[FunDef, FunDef]()) { - case (accMap, fd) if fd.isTheoryOperation || fd.isLibrary || fd.isInvariant => - accMap + (fd -> fd) - case (accMap, fd) => { + val funMap = userLevelFunctions(prog).foldLeft(Map[FunDef, FunDef]()) { + case (accMap, fd) => val freshId = FreshIdentifier(fd.id.name, fd.returnType, uniqueIdDisplay) - val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) - accMap.updated(fd, newfd) - } + accMap + (fd -> new FunDef(freshId, fd.tparams, fd.params, fd.returnType)) } val mapExpr = mapFunctionsInExpr(funMap) _ for ((from, to) <- funMap) { @@ -235,6 +231,15 @@ object ProgramUtil { fds.filter(fd => fd.isRealFunction) } + /** + * Functions that are not theory-operations or library methods that are not a part of the main unit + */ + def userLevelFunctions(program: Program): Seq[FunDef] = { + program.units.flatMap { u => + u.definedFunctions.filter(fd => !fd.isTheoryOperation && (u.isMainUnit || !(fd.isLibrary || fd.isInvariant))) + } + } + def translateExprToProgram(ine: Expr, currProg: Program, newProg: Program): Expr = { var funCache = Map[String, Option[FunDef]]() def funInNewprog(fn: String) = @@ -340,16 +345,29 @@ object PredicateUtil { */ def hasReals(expr: Expr): Boolean = { var foundReal = false - simplePostTransform { - case e => { - if (e.getType == RealType) + postTraversal { + case e if e.getType == RealType => foundReal = true - e - } + case _ => }(expr) foundReal } + /** + * Checks if the expression has real valued sub-expressions. + */ + def hasRealsOrTemplates(expr: Expr): Boolean = { + var found = false + postTraversal { + case Variable(id) if id.getType == RealType || TemplateIdFactory.IsTemplateIdentifier(id) => + found = true + case e if e.getType == RealType => + found = true + case _ => + }(expr) + found + } + /** * Checks if the expression has real valued sub-expressions. * Note: important, <, <=, > etc have default int type. @@ -358,12 +376,10 @@ object PredicateUtil { */ def hasInts(expr: Expr): Boolean = { var foundInt = false - simplePostTransform { - case e: Terminal if (e.getType == Int32Type || e.getType == IntegerType) => { + postTraversal { + case e: Terminal if (e.getType == Int32Type || e.getType == IntegerType) => foundInt = true - e - } - case e => e + case _ => }(expr) foundInt } diff --git a/src/main/scala/leon/laziness/LazinessEliminationPhase.scala b/src/main/scala/leon/laziness/LazinessEliminationPhase.scala index 9c1abab5381882451a3a0769b590301c94d4f81a..a3256232264b208b2e780f7bc9352c203cff0906 100644 --- a/src/main/scala/leon/laziness/LazinessEliminationPhase.scala +++ b/src/main/scala/leon/laziness/LazinessEliminationPhase.scala @@ -44,7 +44,6 @@ import java.io. */ object LazinessEliminationPhase extends TransformationPhase { val dumpInputProg = false - val debugLifting = false val dumpLiftProg = false val dumpProgramWithClosures = false val dumpTypeCorrectProg = false @@ -79,7 +78,7 @@ object LazinessEliminationPhase extends TransformationPhase { // refEq is by default false val nprog = LazyExpressionLifter.liftLazyExpressions(prog, ctx.findOption(optRefEquality).getOrElse(false)) if (dumpLiftProg) - prettyPrintProgramToFile(nprog, ctx, "-lifted") + prettyPrintProgramToFile(nprog, ctx, "-lifted", true) val funsManager = new LazyFunctionsManager(nprog) val closureFactory = new LazyClosureFactory(nprog) diff --git a/src/main/scala/leon/laziness/LazinessUtil.scala b/src/main/scala/leon/laziness/LazinessUtil.scala index 903ef54e9959cc5165f2d3fdda4cedae8ad23186..79f6fae0b62b334948051146106626da07671008 100644 --- a/src/main/scala/leon/laziness/LazinessUtil.scala +++ b/src/main/scala/leon/laziness/LazinessUtil.scala @@ -69,14 +69,14 @@ object LazinessUtil { def isLazyInvocation(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq(_)) => - fullName(fd)(p) == "leon.lazyeval.$.apply" + fullName(fd)(p) == "leon.lazyeval.$" case _ => false } def isEagerInvocation(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq(_)) => - fullName(fd)(p) == "leon.lazyeval.$.eager" + fullName(fd)(p) == "leon.lazyeval.eager" case _ => false } @@ -84,7 +84,7 @@ object LazinessUtil { def isInStateCall(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq()) => val fn = fullName(fd)(p) - (fn == "leon.lazyeval.$.inState" || fn == "leon.lazyeval.Mem.inState") + (fn == "leon.lazyeval.inState" || fn == "leon.mem.inState") case _ => false } @@ -92,33 +92,33 @@ object LazinessUtil { def isOutStateCall(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq()) => val fn = fullName(fd)(p) - (fn == "leon.lazyeval.$.outState" || fn == "leon.lazyeval.Mem.outState") + (fn == "leon.lazyeval.outState" || fn == "leon.mem.outState") case _ => false } def isEvaluatedInvocation(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq(_)) => - fullName(fd)(p) == "leon.lazyeval.$.isEvaluated" + fullName(fd)(p) == "leon.lazyeval.Lazy.isEvaluated" case _ => false } def isSuspInvocation(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq(_, _)) => - fullName(fd)(p) == "leon.lazyeval.$.isSuspension" + fullName(fd)(p) == "leon.lazyeval.Lazy.isSuspension" case _ => false } def isWithStateCons(e: Expr)(implicit p: Program): Boolean = e match { case CaseClass(cct, Seq(_)) => val fn = fullName(cct.classDef)(p) - (fn == "leon.lazyeval.$.WithState" || fn == "leon.lazyeval.Mem.memWithState") + (fn == "leon.lazyeval.WithState" || fn == "leon.mem.memWithState") case _ => false } def isMemCons(e: Expr)(implicit p: Program): Boolean = e match { case CaseClass(cct, Seq(_)) => - fullName(cct.classDef)(p) == "leon.lazyeval.Mem" + fullName(cct.classDef)(p) == "leon.mem.Mem" case _ => false } @@ -130,31 +130,31 @@ object LazinessUtil { case FunctionInvocation(TypedFunDef(fd, _), _) => val fn = fullName(fd)(p) (fn == "leon.lazyeval.WithState.withState" || - fn == "leon.lazyeval.memWithState.withState") + fn == "leon.mem.memWithState.withState") case _ => false } def isCachedInv(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq(_)) => - fullName(fd)(p) == "leon.lazyeval.Mem.isCached" + fullName(fd)(p) == "leon.mem.Mem.isCached" case _ => false } def isValueInvocation(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq(_)) => - fullName(fd)(p) == "leon.lazyeval.$.value" + fullName(fd)(p) == "leon.lazyeval.Lazy.value" case _ => false } def isStarInvocation(e: Expr)(implicit p: Program): Boolean = e match { case FunctionInvocation(TypedFunDef(fd, _), Seq(_)) => - fullName(fd)(p) == "leon.lazyeval.$.*" + fullName(fd)(p) == "leon.lazyeval.Lazy.*" case _ => false } def isLazyType(tpe: TypeTree): Boolean = tpe match { case CaseClassType(CaseClassDef(cid, _, None, false), Seq(_)) => - cid.name == "$" + cid.name == "Lazy" case _ => false } diff --git a/src/main/scala/leon/laziness/LazyExpressionLifter.scala b/src/main/scala/leon/laziness/LazyExpressionLifter.scala index 96683d6114b2d58344b1d091098a431962cd1cb2..bed858694e3efff10742daf73f0638cec21b6ec3 100644 --- a/src/main/scala/leon/laziness/LazyExpressionLifter.scala +++ b/src/main/scala/leon/laziness/LazyExpressionLifter.scala @@ -61,8 +61,8 @@ object LazyExpressionLifter { lazy val needsId = funsMan.callersnTargetOfLazyCons var newfuns = Map[ExprStructure, (FunDef, ModuleDef)]() - val fdmap = prog.definedFunctions.collect { - case fd if !fd.isLibrary && !fd.isInvariant => + val fdmap = ProgramUtil.userLevelFunctions(prog).collect { + case fd if fd.hasBody => val nname = FreshIdentifier(fd.id.name) val nfd = if (createUniqueIds && needsId(fd)) { @@ -74,15 +74,14 @@ object LazyExpressionLifter { (fd -> nfd) }.toMap - lazy val lazyFun = ProgramUtil.functionByFullName("leon.lazyeval.$.apply", prog).get - lazy val valueFun = ProgramUtil.functionByFullName("leon.lazyeval.$.value", prog).get + lazy val lazyFun = ProgramUtil.functionByFullName("leon.lazyeval.$", prog).get + lazy val valueFun = ProgramUtil.functionByFullName("leon.lazyeval.Lazy.value", prog).get var anchorDef: Option[FunDef] = None // a hack to find anchors prog.modules.foreach { md => def exprLifter(inmem: Boolean)(fl: Option[FreeVarListIterator])(expr: Expr) = expr match { - // is this $(e) where e is not a funtion case finv @ FunctionInvocation(lazytfd, Seq(callByNameArg)) if isLazyInvocation(finv)(prog) => - val Lambda(Seq(), arg) = callByNameArg // extract the call-by-name parameter + val Lambda(Seq(), arg) = callByNameArg // extract the call-by-name parameter arg match { case _: FunctionInvocation => finv @@ -116,11 +115,11 @@ object LazyExpressionLifter { fvVars :+ fl.get.nextExpr else fvVars FunctionInvocation(lazytfd, Seq(Lambda(Seq(), - FunctionInvocation(TypedFunDef(argfun, tparams), params)))) + FunctionInvocation(TypedFunDef(argfun, tparams), params)))) } // is the argument of eager invocation not a variable ? - case finv @ FunctionInvocation(TypedFunDef(fd, Seq(tp)), cbn@Seq(Lambda(Seq(), arg))) if isEagerInvocation(finv)(prog) => + case finv @ FunctionInvocation(TypedFunDef(fd, Seq(tp)), cbn @ Seq(Lambda(Seq(), arg))) if isEagerInvocation(finv)(prog) => val rootType = bestRealType(tp) val ntps = Seq(rootType) arg match { @@ -129,7 +128,7 @@ object LazyExpressionLifter { case _ => val freshid = FreshIdentifier("t", rootType) Let(freshid, arg, FunctionInvocation(TypedFunDef(fd, ntps), - Seq(Lambda(Seq(), freshid.toVariable)))) + Seq(Lambda(Seq(), freshid.toVariable)))) } // is this an invocation of a memoized function ? @@ -169,8 +168,12 @@ object LazyExpressionLifter { val nargs = args map rec(inmem || isMemCons(e)(prog)) exprLifter(inmem)(fliter)(op(nargs)) } - nfd.fullBody = rec(false)(fd.fullBody) - case _ => ; + if(fd.hasPrecondition) + nfd.precondition = Some(rec(true)(fd.precondition.get)) + if (fd.hasPostcondition) + nfd.postcondition = Some(rec(true)(fd.postcondition.get)) + nfd.body = Some(rec(false)(fd.body.get)) + case fd => } } val progWithFuns = copyProgram(prog, (defs: Seq[Definition]) => defs.map { diff --git a/src/main/scala/leon/laziness/LazyVerificationPhase.scala b/src/main/scala/leon/laziness/LazyVerificationPhase.scala index dcc0384d73175de68d1c4984e594c275dbd3e99b..ac4f04faef23e903787792aaf74b40c96da872af 100644 --- a/src/main/scala/leon/laziness/LazyVerificationPhase.scala +++ b/src/main/scala/leon/laziness/LazyVerificationPhase.scala @@ -114,8 +114,9 @@ object LazyVerificationPhase { val inferctx = new InferenceContext(p, ctxForInf) val vcSolver = (funDef: FunDef, prog: Program) => new VCSolver(inferctx, prog, funDef) - if (debugInferProgram) + if (debugInferProgram){ prettyPrintProgramToFile(inferctx.inferProgram, checkCtx, "-inferProg", true) + } val results = (new InferenceEngine(inferctx)).analyseProgram(inferctx.inferProgram, funsToCheck.map(InstUtil.userFunctionName), vcSolver, None) diff --git a/src/main/scala/leon/transformations/InstProgSimplifier.scala b/src/main/scala/leon/transformations/InstProgSimplifier.scala index ebb1abe008c066479c719fad4567399dfbe02939..56eecde2a89dc6f52cc966829417ba172d8f38ba 100644 --- a/src/main/scala/leon/transformations/InstProgSimplifier.scala +++ b/src/main/scala/leon/transformations/InstProgSimplifier.scala @@ -26,8 +26,14 @@ import invariant.util.LetTupleSimplification._ object ProgramSimplifier { val debugSimplify = false - def mapProgram(funMap: Map[FunDef, FunDef]): Map[FunDef, FunDef] = { - + def apply(program: Program, instFuncs: Seq[FunDef]): Program = { + val funMap = ((userLevelFunctions(program) ++ instFuncs).distinct).foldLeft(Map[FunDef, FunDef]()) { + case (accMap, fd) => { + val freshId = FreshIdentifier(fd.id.name, fd.returnType) + val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) + accMap + (fd -> newfd) + } + } def mapExpr(ine: Expr, fd: FunDef): Expr = { val replaced = simplePostTransform((e: Expr) => e match { case FunctionInvocation(tfd, args) if funMap.contains(tfd.fd) => @@ -54,39 +60,13 @@ object ProgramSimplifier { //copy annotations from.flags.foreach(to.addFlag(_)) } - funMap - } - - def createNewFunDefs(program: Program): Map[FunDef, FunDef] = { - val allFuncs = functionsWOFields(program.definedFunctions) - - allFuncs.foldLeft(Map[FunDef, FunDef]()) { - case (accMap, fd) if fd.isTheoryOperation => - accMap + (fd -> fd) - case (accMap, fd) => { - //here we need not augment the return types - val freshId = FreshIdentifier(fd.id.name, fd.returnType) - val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) - accMap.updated(fd, newfd) - } - } - } - - def createNewProg(mappedFuncs: Map[FunDef, FunDef], prog: Program): Program = { - val newprog = copyProgram(prog, (defs: Seq[Definition]) => defs.map { - case fd: FunDef if mappedFuncs.contains(fd) => - mappedFuncs(fd) - case d => d + val newprog = copyProgram(program, (defs: Seq[Definition]) => defs.map { + case fd: FunDef if funMap.contains(fd) => funMap(fd) + case d => d }) if (debugSimplify) println("After Simplifications: \n" + ScalaPrinter.apply(newprog)) newprog } - - def apply(program: Program): Program = { - val newFuncs = createNewFunDefs(program) - val mappedFuncs = mapProgram(newFuncs) - createNewProg(mappedFuncs, program) - } } \ No newline at end of file diff --git a/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala b/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala index 6d740de6aff8b077d271377740e86c7fa5d327ab..68b89227251439c35ca338bf19867a21112d4f72 100644 --- a/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala +++ b/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala @@ -100,13 +100,12 @@ class NonlinearityEliminator(skipAxioms: Boolean, domain: TypeTree) { //TOOD: note associativity property of multiplication is not taken into account def apply(program: Program): Program = { - //create a fundef for each function in the program - val newFundefs = program.definedFunctions.map((fd) => { - val newFunType = FunctionType(fd.tparams.map((currParam) => currParam.tp), fd.returnType) + val newFundefs = userLevelFunctions(program).map { fd => + val newFunType = FunctionType(fd.tparams.map(_.tp), fd.returnType) val newfd = new FunDef(FreshIdentifier(fd.id.name, newFunType, false), fd.tparams, fd.params, fd.returnType) - (fd, newfd) - }).toMap + (fd -> newfd) + }.toMap //note, handling templates variables is slightly tricky as we need to preserve a*x as it is val tmult = TypedFunDef(multFun, Seq()) @@ -135,49 +134,44 @@ class NonlinearityEliminator(skipAxioms: Boolean, domain: TypeTree) { } //create a body, pre, post for each newfundef - newFundefs.foreach((entry) => { - val (fd, newfd) = entry - - //add a new precondition - newfd.precondition = - if (fd.precondition.isDefined) - Some(replaceFun(fd.precondition.get)) - else None - - //add a new body - newfd.body = if (fd.hasBody) { - //replace variables by constants if possible - val simpBody = simplifyLets(fd.body.get) - Some(replaceFun(simpBody)) - } else None - - //add a new postcondition - newfd.postcondition = if (fd.postcondition.isDefined) { - //we need to handle template and postWoTemplate specially - val Lambda(resultBinders, _) = fd.postcondition.get - val tmplExpr = fd.templateExpr - val newpost = if (fd.hasTemplate) { - val FunctionInvocation(tmpfd, Seq(Lambda(tmpvars, tmpbody))) = tmplExpr.get - val newtmp = FunctionInvocation(tmpfd, Seq(Lambda(tmpvars, - replaceFun(tmpbody, tmpvars.map(_.id).toSet)))) - fd.postWoTemplate match { - case None => - newtmp - case Some(postExpr) => - And(replaceFun(postExpr), newtmp) - } - } else - replaceFun(fd.getPostWoTemplate) - - Some(Lambda(resultBinders, newpost)) - } else None - - fd.flags.foreach(newfd.addFlag(_)) - }) + newFundefs.foreach { + case (fd, newfd) => + //add a new precondition + newfd.precondition = + if (fd.precondition.isDefined) + Some(replaceFun(fd.precondition.get)) + else None + //add a new body + newfd.body = if (fd.hasBody) { + //replace variables by constants if possible + val simpBody = simplifyLets(fd.body.get) + Some(replaceFun(simpBody)) + } else None + //add a new postcondition + newfd.postcondition = if (fd.postcondition.isDefined) { + //we need to handle template and postWoTemplate specially + val Lambda(resultBinders, _) = fd.postcondition.get + val tmplExpr = fd.templateExpr + val newpost = if (fd.hasTemplate) { + val FunctionInvocation(tmpfd, Seq(Lambda(tmpvars, tmpbody))) = tmplExpr.get + val newtmp = FunctionInvocation(tmpfd, Seq(Lambda(tmpvars, + replaceFun(tmpbody, tmpvars.map(_.id).toSet)))) + fd.postWoTemplate match { + case None => + newtmp + case Some(postExpr) => + And(replaceFun(postExpr), newtmp) + } + } else + replaceFun(fd.getPostWoTemplate) + Some(Lambda(resultBinders, newpost)) + } else None + fd.flags.foreach(newfd.addFlag(_)) + } val transProg = copyProgram(program, (defs: Seq[Definition]) => { defs.map { - case fd: FunDef => newFundefs(fd) - case d => d + case fd: FunDef if newFundefs.contains(fd) => newFundefs(fd) + case d => d } }) val newprog = diff --git a/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala b/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala index cee20aca2c56e6c7c17bc4dd9c5405fa49f77d85..7e60625c62e344792c3a5caa66b7a3b4045915e7 100644 --- a/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala +++ b/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala @@ -26,8 +26,10 @@ object InstrumentationPhase extends TransformationPhase { val description = "Instruments the program for all counters needed" def apply(ctx: LeonContext, program: Program): Program = { - val instprog = new SerialInstrumenter(program) - instprog.apply + val si = new SerialInstrumenter(program) + val instprog = si.apply + //println("Instrumented Program: "+ScalaPrinter.apply(instprog, purescala.PrinterOptions(printUniqueIds = true))) + instprog } } @@ -36,6 +38,7 @@ class SerialInstrumenter(program: Program, val debugInstrumentation = false val exprInstFactory = exprInstOpt.getOrElse((x: Map[FunDef, FunDef], y: SerialInstrumenter, z: FunDef) => new ExprInstrumenter(x, y)(z)) + val instToInstrumenter: Map[Instrumentation, Instrumenter] = Map(Time -> new TimeInstrumenter(program, this), Depth -> new DepthInstrumenter(program, this), @@ -44,7 +47,7 @@ class SerialInstrumenter(program: Program, TPR -> new TPRInstrumenter(program, this)) // a map from functions to the list of instrumentations to be performed for the function - lazy val funcInsts = { + val funcInsts = { var emap = MutableMap[FunDef, List[Instrumentation]]() def update(fd: FunDef, inst: Instrumentation) { if (emap.contains(fd)) @@ -59,7 +62,7 @@ class SerialInstrumenter(program: Program, } emap.toMap } - lazy val instFuncs = funcInsts.keySet //should we exclude theory operations ? + val instFuncs = funcInsts.keySet def instrumenters(fd: FunDef) = funcInsts(fd) map instToInstrumenter.apply _ def instTypes(fd: FunDef) = funcInsts(fd).map(_.getType) @@ -72,121 +75,118 @@ class SerialInstrumenter(program: Program, def apply: Program = { - //create new functions. Augment the return type of a function iff the postcondition uses - //the instrumentation variable or if the function is transitively called from such a function - //note: need not instrument fields - val funMap = functionsWOFields(program.definedFunctions).foldLeft(Map[FunDef, FunDef]()) { - case (accMap, fd: FunDef) if fd.isTheoryOperation => - accMap + (fd -> fd) - case (accMap, fd) => { + if (instFuncs.isEmpty) program + else { + //create new functions. Augment the return type of a function iff the postcondition uses + //the instrumentation variable or if the function is transitively called from such a function + var funMap = Map[FunDef, FunDef]() + (userLevelFunctions(program) ++ instFuncs).distinct.foreach { fd => if (instFuncs.contains(fd)) { val newRetType = TupleType(fd.returnType +: instTypes(fd)) // let the names of the function encode the kind of instrumentations performed val freshId = FreshIdentifier(fd.id.name + "-" + funcInsts(fd).map(_.name).mkString("-"), newRetType) val newfd = new FunDef(freshId, fd.tparams, fd.params, newRetType) - accMap + (fd -> newfd) + funMap += (fd -> newfd) } else { //here we need not augment the return types but do need to create a new copy val freshId = FreshIdentifier(fd.id.name, fd.returnType) val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) - accMap + (fd -> newfd) + funMap += (fd -> newfd) } } - } - def mapExpr(ine: Expr): Expr = { - simplePostTransform((e: Expr) => e match { - case FunctionInvocation(tfd, args) if funMap.contains(tfd.fd) => - if (instFuncs.contains(tfd.fd)) - TupleSelect(FunctionInvocation(TypedFunDef(funMap(tfd.fd), tfd.tps), args), 1) - else - FunctionInvocation(TypedFunDef(funMap(tfd.fd), tfd.tps), args) - case _ => e - })(ine) - } + def mapExpr(ine: Expr): Expr = { + simplePostTransform((e: Expr) => e match { + case FunctionInvocation(tfd, args) if funMap.contains(tfd.fd) => + if (instFuncs.contains(tfd.fd)) + TupleSelect(FunctionInvocation(TypedFunDef(funMap(tfd.fd), tfd.tps), args), 1) + else + FunctionInvocation(TypedFunDef(funMap(tfd.fd), tfd.tps), args) + case _ => e + })(ine) + } - def mapBody(body: Expr, from: FunDef, to: FunDef) = { - val res = - if (from.isExtern) { - // this is an extern function, we must only rely on the specs - // so make the body empty - NoTree(to.returnType) - } else if (instFuncs.contains(from)) { - //(new ExprInstrumenter(funMap, this)(from)(body)) - exprInstFactory(funMap, this, from)(body) - } else - mapExpr(body) - res - } + def mapBody(body: Expr, from: FunDef, to: FunDef) = { + val res = + if (from.isExtern) { + // this is an extern function, we must only rely on the specs + // so make the body empty + NoTree(to.returnType) + } else if (instFuncs.contains(from)) { + exprInstFactory(funMap, this, from)(body) + } else + mapExpr(body) + res + } - def mapPost(pred: Expr, from: FunDef, to: FunDef) = { - pred match { - case Lambda(Seq(ValDef(fromRes)), postCond) if (instFuncs.contains(from)) => - val toResId = FreshIdentifier(fromRes.name, to.returnType, true) - val newpost = postMap((e: Expr) => e match { - case Variable(`fromRes`) => - Some(TupleSelect(toResId.toVariable, 1)) - - case _ if funcInsts(from).exists(_.isInstVariable(e)) => - val inst = funcInsts(from).find(_.isInstVariable(e)).get - Some(TupleSelect(toResId.toVariable, instIndex(from)(inst))) - - case _ => - None - })(postCond) - Lambda(Seq(ValDef(toResId)), mapExpr(newpost)) - case _ => - mapExpr(pred) + def mapPost(pred: Expr, from: FunDef, to: FunDef) = { + pred match { + case Lambda(Seq(ValDef(fromRes)), postCond) if (instFuncs.contains(from)) => + val toResId = FreshIdentifier(fromRes.name, to.returnType, true) + val newpost = postMap((e: Expr) => e match { + case Variable(`fromRes`) => + Some(TupleSelect(toResId.toVariable, 1)) + + case _ if funcInsts(from).exists(_.isInstVariable(e)) => + val inst = funcInsts(from).find(_.isInstVariable(e)).get + Some(TupleSelect(toResId.toVariable, instIndex(from)(inst))) + + case _ => + None + })(postCond) + Lambda(Seq(ValDef(toResId)), mapExpr(newpost)) + case _ => + mapExpr(pred) + } } - } - // Map the bodies and preconditions - for ((from, to) <- funMap) { - //copy annotations - from.flags.foreach(to.addFlag(_)) - to.fullBody = from.fullBody match { - case b @ NoTree(_) => NoTree(to.returnType) - case Require(pre, body) => - //here 'from' does not have a postcondition but 'to' will always have a postcondition - val toPost = - Lambda(Seq(ValDef(FreshIdentifier("res", to.returnType))), BooleanLiteral(true)) - val bodyPre = - Require(mapExpr(pre), mapBody(body, from, to)) - Ensuring(bodyPre, toPost) - - case Ensuring(Require(pre, body), post) => - Ensuring(Require(mapExpr(pre), mapBody(body, from, to)), - mapPost(post, from, to)) - - case Ensuring(body, post) => - Ensuring(mapBody(body, from, to), mapPost(post, from, to)) - - case body => - val toPost = - Lambda(Seq(ValDef(FreshIdentifier("res", to.returnType))), BooleanLiteral(true)) - Ensuring(mapBody(body, from, to), toPost) + // Map the bodies and preconditions + for ((from, to) <- funMap) { + //copy annotations + from.flags.foreach(to.addFlag(_)) + to.fullBody = from.fullBody match { + case b @ NoTree(_) => NoTree(to.returnType) + case Require(pre, body) => + //here 'from' does not have a postcondition but 'to' will always have a postcondition + val toPost = + Lambda(Seq(ValDef(FreshIdentifier("res", to.returnType))), BooleanLiteral(true)) + val bodyPre = + Require(mapExpr(pre), mapBody(body, from, to)) + Ensuring(bodyPre, toPost) + + case Ensuring(Require(pre, body), post) => + Ensuring(Require(mapExpr(pre), mapBody(body, from, to)), + mapPost(post, from, to)) + + case Ensuring(body, post) => + Ensuring(mapBody(body, from, to), mapPost(post, from, to)) + + case body => + val toPost = + Lambda(Seq(ValDef(FreshIdentifier("res", to.returnType))), BooleanLiteral(true)) + Ensuring(mapBody(body, from, to), toPost) + } } - } - val additionalFuncs = funMap.flatMap { - case (k, _) => - if (instFuncs(k)) - instrumenters(k).flatMap(_.additionalfunctionsToAdd) - else List() - }.toList.distinct - - val newprog = copyProgram(program, (defs: Seq[Definition]) => - defs.map { - case fd: FunDef if funMap.contains(fd) => - funMap(fd) - case d => d - } ++ additionalFuncs) - if (debugInstrumentation) - println("After Instrumentation: \n" + ScalaPrinter.apply(newprog)) - - ProgramSimplifier(newprog) + val additionalFuncs = funMap.flatMap { + case (k, _) => + if (instFuncs(k)) + instrumenters(k).flatMap(_.additionalfunctionsToAdd) + else List() + }.toList.distinct + + val newprog = copyProgram(program, (defs: Seq[Definition]) => + defs.map { + case fd: FunDef if funMap.contains(fd) => + funMap(fd) + case d => d + } ++ additionalFuncs) + if (debugInstrumentation) + println("After Instrumentation: \n" + ScalaPrinter.apply(newprog)) + + ProgramSimplifier(newprog, instFuncs.toSeq) + } } - } class ExprInstrumenter(funMap: Map[FunDef, FunDef], serialInst: SerialInstrumenter)(implicit currFun: FunDef) { @@ -250,11 +250,9 @@ class ExprInstrumenter(funMap: Map[FunDef, FunDef], serialInst: SerialInstrument val valexpr = TupleSelect(resvar, 1) val instexprs = instrumenters.map { m => val calleeInst = - if (serialInst.funcInsts(fd).contains(m.inst) && - fd.isUserFunction) { - // ignoring fields here + if (serialInst.funcInsts(fd).contains(m.inst) && fd.isUserFunction) { List(serialInst.selectInst(fd)(resvar, m.inst)) - } else List() + } else List() // ignoring fields here //Note we need to ensure that the last element of list is the instval of the finv m.instrument(e, subeInsts.getOrElse(m.inst, List()) ++ calleeInst, Some(resvar)) } @@ -411,12 +409,6 @@ class ExprInstrumenter(funMap: Map[FunDef, FunDef], serialInst: SerialInstrument case n @ Operator(ss, recons) => tupleify(e, ss, recons) - /* case b @ BinaryOperator(s1, s2, recons) => - tupleify(e, Seq(s1, s2), { case Seq(s1, s2) => recons(s1, s2) }) - - case u @ UnaryOperator(s, recons) => - tupleify(e, Seq(s), { case Seq(s) => recons(s) }) -*/ case t: Terminal => tupleify(e, Seq(), { case Seq() => t }) } @@ -431,7 +423,6 @@ class ExprInstrumenter(funMap: Map[FunDef, FunDef], serialInst: SerialInstrument val instExprs = instrumenters map { m => m.instrumentBody(newe, selectInst(bodyId.toVariable, m.inst)) - } Let(bodyId, transformed, Tuple(TupleSelect(bodyId.toVariable, 1) +: instExprs)) diff --git a/testcases/lazy-datastructures/Unproved/HammingProblem.scala b/testcases/lazy-datastructures/Unproved/HammingProblem.scala deleted file mode 100644 index 79acc3ba6bd132a86efed9cc8c74a70a2dbe6a46..0000000000000000000000000000000000000000 --- a/testcases/lazy-datastructures/Unproved/HammingProblem.scala +++ /dev/null @@ -1,94 +0,0 @@ -import leon.lazyeval._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.lazyeval.$.eagerToLazy -import scala.math.BigInt.int2bigInt -//import leon.invariant._ - -/** - * Not the required bound - */ -object Hamming { - - sealed abstract class IList { - def size: BigInt = { - this match { - case SNil() => BigInt(0) - case SCons(x, t) => 1 + ssize(t) - } - } ensuring (_ >= 0) - } - case class SCons(x: BigInt, tail: $[IList]) extends IList - case class SNil() extends IList - def ssize(l: $[IList]): BigInt = (l*).size - - sealed abstract class List { - def size: BigInt = { - this match { - case Cons(_, xs) => 1 + xs.size - case _ => BigInt(0) - } - } ensuring (_ >= 0) - } - case class Cons(x: BigInt, tail: List) extends List - case class Nil() extends List - - /** - * The following implementation of Merge - * produces a list with out duplicates - */ - def merge(a: $[IList], b: $[IList]): IList = { - require(a.isSuspension(mult _) && b.isSuspension(mult _)) - b.value match { - case SNil() => a.value - case bl @ SCons(x, xs) => - a.value match { - case SNil() => bl - case SCons(y, ys) => - if (x == y) - SCons(x, $(merge(ys, xs))) - else if (y < x) - SCons(y, $(merge(ys, b))) - else - SCons(x, $(merge(a, xs))) - } - } - } ensuring(_ => time <= 100) - - def mult(num: BigInt, l: IList): IList ={ - l match { - case SNil() => SNil() - case SCons(x, tail) => SCons(num*x, tail) - } - } ensuring(_ => time <= 20) - - def nextElems(n: BigInt): IList = { - merge(merge($(mult(2, hamming(n-1))), $(mult(3, hamming(n-1)))), - $(mult(5, hamming(n-1)))) - } //ensuring(_ => time <= ) - - /** - * 'n' is some constant that controls the size. - * Note: the return list would have at least 'n' elements (and possibly more) - * but is still finite - */ - def hamming(n: BigInt): IList = { - if (n <= 0) - SNil() - else - SCons(1, $(nextElems(n))) - } - - def kthHamming(k: BigInt, hstream: $[IList]): BigInt = { - require(k >= 1) - hstream.value match { - case SCons(x, _) => - if(k == 1) - x - else kthHamming(k-1, hstream) - case SNil() => - BigInt(-1) - } - } //ensuring(_ => time <= 40 * k + 40) -} diff --git a/testcases/lazy-datastructures/build.sbt b/testcases/lazy-datastructures/build.sbt new file mode 100644 index 0000000000000000000000000000000000000000..ff26464c904443455179389615dcd4d5963986fb --- /dev/null +++ b/testcases/lazy-datastructures/build.sbt @@ -0,0 +1,15 @@ +name := "LazyDataStructures" + +version := "1.0" + +organization := "ch.epfl.lara" + +scalaVersion := "2.11.7" + +fork in run := true + +javaOptions in run ++= Seq("-Xmx5G", "-Xms3G", "-Xss500M") + +scalacOptions ++= Seq("-optimise") + +unmanagedSourceDirectories in Compile ++= Seq("withOrb", "eager").map(dir => baseDirectory.value / dir) diff --git a/testcases/lazy-datastructures/conc/Conqueue.scala b/testcases/lazy-datastructures/conc/Conqueue.scala index 8e16fb1bc8cbb26cb8b462cc9e7cb74ac2120b95..360aef160c64cdd60406066fd4fbafbc9d43a4fc 100644 --- a/testcases/lazy-datastructures/conc/Conqueue.scala +++ b/testcases/lazy-datastructures/conc/Conqueue.scala @@ -1,11 +1,11 @@ package conc -import leon.lazyeval._ -import leon.lang._ -import leon.math._ -import leon.annotation._ -import leon.instrumentation._ -import leon.lazyeval.$._ +import leon._ +import lazyeval._ +import lang._ +import math._ +import annotation._ +import instrumentation._ import ConcTrees._ @@ -42,7 +42,7 @@ object Conqueue { } case class Tip[T](t: Conc[T]) extends ConQ[T] - case class Spine[T](head: Conc[T], createdWithSuspension: Bool, rear: $[ConQ[T]]) extends ConQ[T] + case class Spine[T](head: Conc[T], createdWithSuspension: Bool, rear: Lazy[ConQ[T]]) extends ConQ[T] sealed abstract class Bool case class True() extends Bool @@ -51,7 +51,7 @@ object Conqueue { /** * Checks whether there is a zero before an unevaluated closure */ - def zeroPrecedesLazy[T](q: $[ConQ[T]]): Boolean = { + def zeroPrecedesLazy[T](q: Lazy[ConQ[T]]): Boolean = { if (q.isEvaluated) { q* match { case Spine(Empty(), _, rear) => @@ -66,7 +66,7 @@ object Conqueue { /** * Checks whether there is a zero before a given suffix */ - def zeroPrecedesSuf[T](q: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def zeroPrecedesSuf[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { if (q != suf) { q* match { case Spine(Empty(), _, rear) => true @@ -81,7 +81,7 @@ object Conqueue { * Everything until suf is evaluated. This * also asserts that suf should be a suffix of the list */ - def concreteUntil[T](l: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def concreteUntil[T](l: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { if (l != suf) { l.isEvaluated && (l* match { case Spine(_, cws, tail) => @@ -92,7 +92,7 @@ object Conqueue { } else true } - def isConcrete[T](l: $[ConQ[T]]): Boolean = { + def isConcrete[T](l: Lazy[ConQ[T]]): Boolean = { l.isEvaluated && (l* match { case Spine(_, _, tail) => isConcrete(tail) @@ -101,10 +101,10 @@ object Conqueue { } sealed abstract class Scheds[T] - case class Cons[T](h: $[ConQ[T]], tail: Scheds[T]) extends Scheds[T] + case class Cons[T](h: Lazy[ConQ[T]], tail: Scheds[T]) extends Scheds[T] case class Nil[T]() extends Scheds[T] - def schedulesProperty[T](q: $[ConQ[T]], schs: Scheds[T]): Boolean = { + def schedulesProperty[T](q: Lazy[ConQ[T]], schs: Scheds[T]): Boolean = { schs match { case Cons(head, tail) => head* match { @@ -120,7 +120,7 @@ object Conqueue { } } - def strongSchedsProp[T](q: $[ConQ[T]], schs: Scheds[T]) = { + def strongSchedsProp[T](q: Lazy[ConQ[T]], schs: Scheds[T]) = { q.isEvaluated && { schs match { case Cons(head, tail) => @@ -134,7 +134,7 @@ object Conqueue { /** * Note: if 'q' has a suspension then it would have a carry. */ - def pushUntilCarry[T](q: $[ConQ[T]]): $[ConQ[T]] = { + def pushUntilCarry[T](q: Lazy[ConQ[T]]): Lazy[ConQ[T]] = { q* match { case Spine(Empty(), _, rear) => // if we push a carry and get back 0 then there is a new carry pushUntilCarry(rear) @@ -145,11 +145,11 @@ object Conqueue { } } - case class Queue[T](queue: $[ConQ[T]], schedule: Scheds[T]) { + case class Queue[T](queue: Lazy[ConQ[T]], schedule: Scheds[T]) { val valid = strongSchedsProp(queue, schedule) } - def pushLeft[T](ys: Single[T], xs: $[ConQ[T]]): ConQ[T] = { + def pushLeft[T](ys: Single[T], xs: Lazy[ConQ[T]]): ConQ[T] = { require(zeroPrecedesLazy(xs)) xs.value match { case Tip(CC(_, _)) => @@ -167,7 +167,7 @@ object Conqueue { // this procedure does not change state @invstate - def pushLeftLazy[T](ys: Conc[T], xs: $[ConQ[T]]): ConQ[T] = { + def pushLeftLazy[T](ys: Conc[T], xs: Lazy[ConQ[T]]): ConQ[T] = { require(!ys.isEmpty && zeroPrecedesLazy(xs) && (xs* match { case Spine(h, _, _) => h != Empty[T]() @@ -214,7 +214,7 @@ object Conqueue { * forall suf. suf*.head != Empty() ^ zeroPredsSuf(xs, suf) ^ concUntil(xs.tail.tail, suf) => concUntil(push(rear), suf) */ @invstate - def pushLeftLazyLemma[T](ys: Conc[T], xs: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def pushLeftLazyLemma[T](ys: Conc[T], xs: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { require(!ys.isEmpty && zeroPrecedesSuf(xs, suf) && (xs* match { case Spine(h, _, _) => h != Empty[T]() @@ -266,7 +266,7 @@ object Conqueue { case _ => w.schedule } - val lq: $[ConQ[T]] = nq + val lq: Lazy[ConQ[T]] = nq (lq, nsched) } ensuring { res => // lemma instantiations @@ -285,7 +285,7 @@ object Conqueue { time <= 80 } - def Pay[T](q: $[ConQ[T]], scheds: Scheds[T]): Scheds[T] = { + def Pay[T](q: Lazy[ConQ[T]], scheds: Scheds[T]): Scheds[T] = { require(schedulesProperty(q, scheds) && q.isEvaluated) scheds match { case c @ Cons(head, rest) => @@ -341,7 +341,7 @@ object Conqueue { } // monotonicity lemmas - def schedMonotone[T](st1: Set[$[ConQ[T]]], st2: Set[$[ConQ[T]]], scheds: Scheds[T], l: $[ConQ[T]]): Boolean = { + def schedMonotone[T](st1: Set[Lazy[ConQ[T]]], st2: Set[Lazy[ConQ[T]]], scheds: Scheds[T], l: Lazy[ConQ[T]]): Boolean = { require(st1.subsetOf(st2) && (schedulesProperty(l, scheds) withState st1)) // here the input state is fixed as 'st1' //induction scheme @@ -359,18 +359,18 @@ object Conqueue { } holds @traceInduct - def concreteMonotone[T](st1: Set[$[ConQ[T]]], st2: Set[$[ConQ[T]]], l: $[ConQ[T]]): Boolean = { + def concreteMonotone[T](st1: Set[Lazy[ConQ[T]]], st2: Set[Lazy[ConQ[T]]], l: Lazy[ConQ[T]]): Boolean = { ((isConcrete(l) withState st1) && st1.subsetOf(st2)) ==> (isConcrete(l) withState st2) } holds @traceInduct - def concUntilMonotone[T](q: $[ConQ[T]], suf: $[ConQ[T]], st1: Set[$[ConQ[T]]], st2: Set[$[ConQ[T]]]): Boolean = { + def concUntilMonotone[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]], st1: Set[Lazy[ConQ[T]]], st2: Set[Lazy[ConQ[T]]]): Boolean = { ((concreteUntil(q, suf) withState st1) && st1.subsetOf(st2)) ==> (concreteUntil(q, suf) withState st2) } holds // suffix predicates and their properties (this should be generalizable) - def suffix[T](q: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def suffix[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { if (q == suf) true else { q* match { @@ -381,7 +381,7 @@ object Conqueue { } } - def properSuffix[T](l: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def properSuffix[T](l: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { l* match { case Spine(_, _, rear) => suffix(rear, suf) @@ -393,7 +393,7 @@ object Conqueue { * suf(q, suf) ==> suf(q.rear, suf.rear) */ @traceInduct - def suffixTrans[T](q: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def suffixTrans[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { suffix(q, suf) ==> ((q*, suf*) match { case (Spine(_, _, rear), Spine(_, _, sufRear)) => // 'sufRear' should be a suffix of 'rear1' @@ -405,7 +405,7 @@ object Conqueue { /** * properSuf(l, suf) ==> l != suf */ - def suffixDisequality[T](l: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def suffixDisequality[T](l: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { require(properSuffix(l, suf)) suffixTrans(l, suf) && // lemma instantiation ((l*, suf*) match { // induction scheme @@ -417,21 +417,21 @@ object Conqueue { }.holds @traceInduct - def suffixCompose[T](q: $[ConQ[T]], suf1: $[ConQ[T]], suf2: $[ConQ[T]]): Boolean = { + def suffixCompose[T](q: Lazy[ConQ[T]], suf1: Lazy[ConQ[T]], suf2: Lazy[ConQ[T]]): Boolean = { (suffix(q, suf1) && properSuffix(suf1, suf2)) ==> properSuffix(q, suf2) } holds // properties of 'concUntil' @traceInduct - def concreteUntilIsSuffix[T](l: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def concreteUntilIsSuffix[T](l: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { concreteUntil(l, suf) ==> suffix(l, suf) }.holds // properties that extend `concUntil` to larger portions of the queue @traceInduct - def concUntilExtenLemma[T](q: $[ConQ[T]], suf: $[ConQ[T]], st1: Set[$[ConQ[T]]], st2: Set[$[ConQ[T]]]): Boolean = { + def concUntilExtenLemma[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]], st1: Set[Lazy[ConQ[T]]], st2: Set[Lazy[ConQ[T]]]): Boolean = { ((concreteUntil(q, suf) withState st1) && st2 == st1 ++ Set(suf)) ==> (suf* match { case Spine(_, _, rear) => @@ -441,12 +441,12 @@ object Conqueue { } holds @traceInduct - def concUntilConcreteExten[T](q: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def concUntilConcreteExten[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { (concreteUntil(q, suf) && isConcrete(suf)) ==> isConcrete(q) } holds @traceInduct - def concUntilCompose[T](q: $[ConQ[T]], suf1: $[ConQ[T]], suf2: $[ConQ[T]]): Boolean = { + def concUntilCompose[T](q: Lazy[ConQ[T]], suf1: Lazy[ConQ[T]], suf2: Lazy[ConQ[T]]): Boolean = { (concreteUntil(q, suf1) && concreteUntil(suf1, suf2)) ==> concreteUntil(q, suf2) } holds @@ -454,18 +454,18 @@ object Conqueue { // - these are used in preconditions to derive the `zeroPrecedesLazy` property @traceInduct - def zeroPredSufConcreteUntilLemma[T](q: $[ConQ[T]], suf: $[ConQ[T]]): Boolean = { + def zeroPredSufConcreteUntilLemma[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]]): Boolean = { (zeroPrecedesSuf(q, suf) && concreteUntil(q, suf)) ==> zeroPrecedesLazy(q) } holds @traceInduct - def concreteZeroPredLemma[T](q: $[ConQ[T]]): Boolean = { + def concreteZeroPredLemma[T](q: Lazy[ConQ[T]]): Boolean = { isConcrete(q) ==> zeroPrecedesLazy(q) } holds // properties relating `suffix` an `zeroPrecedesSuf` - def suffixZeroLemma[T](q: $[ConQ[T]], suf: $[ConQ[T]], suf2: $[ConQ[T]]): Boolean = { + def suffixZeroLemma[T](q: Lazy[ConQ[T]], suf: Lazy[ConQ[T]], suf2: Lazy[ConQ[T]]): Boolean = { require(suf* match { case Spine(Empty(), _, _) => suffix(q, suf) && properSuffix(suf, suf2) diff --git a/testcases/lazy-datastructures/eager/AmortizedQueue.scala b/testcases/lazy-datastructures/eager/AmortizedQueue.scala new file mode 100644 index 0000000000000000000000000000000000000000..cdbb65727a009a4aed77514b904254a692f925b6 --- /dev/null +++ b/testcases/lazy-datastructures/eager/AmortizedQueue.scala @@ -0,0 +1,83 @@ +package eagerEval + +import leon._ +import invariant._ +import instrumentation._ + +object AmortizedQueue { + sealed abstract class List { + val size: BigInt = this match { + case Nil() => 0 + case Cons(_, xs) => 1 + xs.size + } + } + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List + + def concat(l1: List, l2: List): List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => Cons(x, concat(xs, l2)) + + }) ensuring (res => res.size == l1.size + l2.size && time <= ? * l1.size + ?) + + def reverseRec(l1: List, l2: List): List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => reverseRec(xs, Cons(x, l2)) + }) ensuring (res => l1.size + l2.size == res.size && time <= ? * l1.size + ?) + + def listRev(l: List): List = { + reverseRec(l, Nil()) + } ensuring (res => l.size == res.size && time <= ? * l.size + ?) + + def removeLast(l: List): List = { + require(l != Nil()) + l match { + case Cons(x, Nil()) => Nil() + case Cons(x, xs) => Cons(x, removeLast(xs)) + case _ => Nil() + } + } ensuring (res => res.size <= l.size && tmpl((a, b) => time <= a * l.size + b)) + + case class Queue(front: List, rear: List) { + def qsize: BigInt = front.size + rear.size + + def asList: List = concat(front, listRev(rear)) + + def isAmortized: Boolean = front.size >= rear.size + + def isEmpty: Boolean = this match { + case Queue(Nil(), Nil()) => true + case _ => false + } + + def amortizedQueue(front: List, rear: List): Queue = { + if (rear.size <= front.size) + Queue(front, rear) + else + Queue(concat(front, listRev(rear)), Nil()) + } + + def enqueue(elem: BigInt): Queue = ({ + amortizedQueue(front, Cons(elem, rear)) + }) ensuring (_ => time <= ? * qsize + ?) + + def dequeue: Queue = { + require(isAmortized && !isEmpty) + front match { + case Cons(f, fs) => amortizedQueue(fs, rear) + case _ => Queue(Nil(), Nil()) + } + } ensuring (_ => time <= ? * qsize + ?) + + def head: BigInt = { + require(isAmortized && !isEmpty) + front match { + case Cons(f, _) => f + } + } + + def reverse: Queue = { // this lets the queue perform deque operation (but they no longer have efficient constant time amortized bounds) + amortizedQueue(rear, front) + } + } +} diff --git a/testcases/lazy-datastructures/eager/BasicMergeSort.scala b/testcases/lazy-datastructures/eager/BasicMergeSort.scala new file mode 100644 index 0000000000000000000000000000000000000000..481bbbbf061b3ca6bda3a72aee3744bb40252208 --- /dev/null +++ b/testcases/lazy-datastructures/eager/BasicMergeSort.scala @@ -0,0 +1,41 @@ +package eagerEval + +import leon._ +import lang._ +import collection._ + +object MergeSort { + + def merge[T](less: (T, T) => Boolean)(xs: List[T], ys: List[T]): List[T] = { + (xs, ys) match { + case (Nil(), _) => ys + case (_, Nil()) => xs + case (Cons(x, xtail), Cons(y, ytail)) => + if (less(x, y)) + x :: merge(less)(xtail, ys) + else + y :: merge(less)(xs, ytail) + } + } ensuring { res => res.content == xs.content ++ ys.content && + res.size == xs.size + ys.size } + + def split[T](list: List[T]): (List[T], List[T]) = { + list match { + case Nil() => (Nil(), Nil()) + case Cons(x, Nil()) => (Cons(x, Nil()), Nil()) + case Cons(x1, Cons(x2, xs)) => + val (s1, s2) = split(xs) + (Cons(x1, s1), Cons(x2, s2)) + } + } + + def msort[T](less: (T, T) => Boolean)(l: List[T]): List[T] = { + l match { + case Nil() => Nil[T]() + case Cons(x, Nil()) => Cons(x, Nil()) + case _ => + val (first, second) = split(l) + merge(less)(msort(less)(first), msort(less)(second)) + } + } ensuring { res => res.content == l.content && res.size == l.size } +} diff --git a/testcases/lazy-datastructures/eager/BigNums.scala b/testcases/lazy-datastructures/eager/BigNums.scala new file mode 100644 index 0000000000000000000000000000000000000000..5d0271b0e354a578b4a43371e9b5dce087a4015a --- /dev/null +++ b/testcases/lazy-datastructures/eager/BigNums.scala @@ -0,0 +1,79 @@ +package eagerEval + +import leon._ +import annotation._ +import invariant._ +import instrumentation._ + +object BigNums { + sealed abstract class Digit + case class Zero() extends Digit { + @ignore + override def toString = "0" + } + case class One() extends Digit { + @ignore + override def toString = "1" + } + + sealed abstract class BigNum + case class Cons(head: Digit, tail: BigNum) extends BigNum + case class Nil() extends BigNum + + /** + * Time taken by the increment method + * The number of leading one's + */ + def leadingOnes(l: BigNum) : BigInt = { + l match { + case Nil() => 1 + case Cons(Zero(), tail) => 1 + case Cons(_, tail) => 1 + leadingOnes(tail) + } + } + + /** + * Total number of one's in the number + */ + def numOnes(l: BigNum) : BigInt = { + l match { + case Nil() => 0 + case Cons(Zero(), tail) => numOnes(tail) + case Cons(_, tail) => 1 + numOnes(tail) + } + } + + /** + * Increments a number + */ + def increment(l: BigNum) : BigNum = { + l match { + case Nil() => Cons(One(), l) + case Cons(Zero(), tail) => Cons(One(), tail) + case Cons(_, tail) => + Cons(Zero(), increment(tail)) + } + } ensuring (res => time <= ? * leadingOnes(l) + ? && leadingOnes(l) + numOnes(res) - numOnes(l) <= ?) + + def firstDigit(l: BigNum): Digit = { + require(l != Nil()) + l match { + case Cons(d, _) => d + } + } + + /** + * Nop is the number of operations + */ + def incrUntil(nop: BigInt, l: BigNum) : BigNum = { + if(nop == 0) l + else { + incrUntil(nop-1, increment(l)) + } + } ensuring (res => time <= ? * nop + ? * numOnes(l) + ?) + + def count(nop: BigInt) : BigNum = { + incrUntil(nop, Nil()) + } ensuring (res => time <= ? * nop + ?) + +} diff --git a/testcases/lazy-datastructures/lib/lazybenchmarkexecution_2.11-1.0.jar b/testcases/lazy-datastructures/lib/lazybenchmarkexecution_2.11-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..784f4db57ac44a6c8e78d491fe8280d5866b0663 Binary files /dev/null and b/testcases/lazy-datastructures/lib/lazybenchmarkexecution_2.11-1.0.jar differ diff --git a/testcases/lazy-datastructures/lib/macmemo.jar b/testcases/lazy-datastructures/lib/macmemo.jar new file mode 100644 index 0000000000000000000000000000000000000000..eacd3c002133ad6486d2c89eb63722ab088d2a6c Binary files /dev/null and b/testcases/lazy-datastructures/lib/macmemo.jar differ diff --git a/testcases/lazy-datastructures/memoization/FibonacciMemoized.scala b/testcases/lazy-datastructures/memoization/FibonacciMemoized.scala index 77ef3233b600aaad91caef47838d27b67ecc2bbb..2780315c17ae7844e34a6b8a1be827d371f99578 100644 --- a/testcases/lazy-datastructures/memoization/FibonacciMemoized.scala +++ b/testcases/lazy-datastructures/memoization/FibonacciMemoized.scala @@ -1,8 +1,8 @@ -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ +import leon._ +import mem._ +import lang._ +import annotation._ +import instrumentation._ //import leon.invariant._ object FibMem { diff --git a/testcases/lazy-datastructures/memoization/HammingMemoized.scala b/testcases/lazy-datastructures/memoization/HammingMemoized.scala index 9c4ae4a0b875ac1cb8152b93e20d1b2830b329db..36f606b93437055f7bae7c7908f30cba9cee88d7 100644 --- a/testcases/lazy-datastructures/memoization/HammingMemoized.scala +++ b/testcases/lazy-datastructures/memoization/HammingMemoized.scala @@ -1,8 +1,9 @@ -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ +import leon._ + +import mem._ +import lang._ +import annotation._ +import instrumentation._ /** * A memoized version of the implementation of Hamming problem shown in @@ -54,8 +55,8 @@ object Hamming { require(n == 0 || n > 0 && depsEval(n - 1)) ham(n).v } ensuring (res => { - val in = Mem.inState[Data] - val out = Mem.outState[Data] + val in = inState[Data] + val out = outState[Data] (n == 0 || depsEvalMono(n-1, in, out)) && // instantiation time <= 170 }) diff --git a/testcases/lazy-datastructures/memoization/Knapsack.scala b/testcases/lazy-datastructures/memoization/Knapsack.scala index fc67c5b03657f048e06f1fc9ea89f5921eec89e1..44eadad05a0de6edba94c8796d668748cc0aaf49 100644 --- a/testcases/lazy-datastructures/memoization/Knapsack.scala +++ b/testcases/lazy-datastructures/memoization/Knapsack.scala @@ -1,8 +1,7 @@ -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ +import leon._ +import mem._ +import annotation._ +import instrumentation._ //import leon.invariant._ object Knapscak { @@ -76,7 +75,7 @@ object Knapscak { else { Cons((i,ri), bottomup(i + 1, w, items)) } - } ensuring(items.size <= 10 ==> time <= 500 * (w - i + 1)) + } ensuring(_ => items.size <= 10 ==> time <= 500 * (w - i + 1)) /** * Computes the list of optimal solutions of all weights up to 'w' @@ -84,7 +83,7 @@ object Knapscak { def knapSackSol(w: BigInt, items: IList) = { require(w >= 0 && items.size <= 10) // the second requirement is only to keep the bounds linear for z3 to work bottomup(0, w, items) - } ensuring(time <= 500*w + 510) + } ensuring(_ => time <= 500*w + 510) /** * Lemmas of deps diff --git a/testcases/lazy-datastructures/memoization/PackratParsing.scala b/testcases/lazy-datastructures/memoization/PackratParsing.scala index f4feed46bcc41801d3ceaf4bb938fd55606500e2..b1b5f175b1162f80d05bb394cad39ce075feb4d7 100644 --- a/testcases/lazy-datastructures/memoization/PackratParsing.scala +++ b/testcases/lazy-datastructures/memoization/PackratParsing.scala @@ -1,5 +1,5 @@ -import leon.lazyeval._ -import leon.lazyeval.Mem._ +import leon._ +import leon.mem._ import leon.lang._ import leon.annotation._ import leon.instrumentation._ @@ -33,7 +33,6 @@ object PackratParsing { string(i.toInt) } ensuring(_ => time <= 1) - sealed abstract class Result { /** * Checks if the index in the result (if any) is @@ -51,47 +50,52 @@ object PackratParsing { @memoize @invstate def pAdd(i: BigInt): Result = { - require(depsEval(i) && - pMul(i).isCached && pPrim(i).isCached && - resEval(i, pMul(i))) // lemma inst - + require { + if (depsEval(i) && pMul(i).isCached && pPrim(i).isCached) + resEval(i, pMul(i)) + else false + } // lemma inst // Rule 1: Add <- Mul + Add - pMul(i) match { + val v = pMul(i) + v match { case Parsed(j) => if (j > 0 && lookup(j) == Plus()) { pAdd(j - 1) match { case Parsed(rem) => Parsed(rem) case _ => - pMul(i) // Rule2: Add <- Mul + v // Rule2: Add <- Mul } - } else pMul(i) + } else v case _ => - pMul(i) + v } - } ensuring (res => res.smallerIndex(i) && time <= 40) + } ensuring (res => res.smallerIndex(i) && time <= 26) @memoize @invstate def pMul(i: BigInt): Result = { - require(depsEval(i) && pPrim(i).isCached && - resEval(i, pPrim(i)) // lemma inst - ) + require { + if (depsEval(i) && pPrim(i).isCached) + resEval(i, pPrim(i)) // lemma inst + else false + } // Rule 1: Mul <- Prim * Mul - pPrim(i) match { + val v = pPrim(i) + v match { case Parsed(j) => if (j > 0 && lookup(j) == Plus()) { pMul(j - 1) match { case Parsed(rem) => Parsed(rem) case _ => - pPrim(i) // Rule2: Mul <- Prim + v // Rule2: Mul <- Prim } - } else pPrim(i) + } else v case _ => - pPrim(i) + v } - } ensuring (res => res.smallerIndex(i) && time <= 40) + } ensuring (res => res.smallerIndex(i) && time <= 26) @memoize @invstate @@ -106,14 +110,15 @@ object PackratParsing { } else if (char == Open() && i > 0) { pAdd(i - 1) match { // Rule 2: pPrim <- ( Add ) case Parsed(rem) => - Parsed(rem) + if (rem >= 0 && lookup(rem) == Close()) Parsed(rem - 1) + else NoParse() case _ => NoParse() } } else NoParse() - } ensuring (res => res.smallerIndex(i) && time <= 30) + } ensuring (res => res.smallerIndex(i) && time <= 28) - @inline + //@inline def depsEval(i: BigInt) = i == 0 || (i > 0 && allEval(i-1)) def allEval(i: BigInt): Boolean = { @@ -138,7 +143,6 @@ object PackratParsing { /** * Instantiates the lemma `depsLem` on the result index (if any) */ - @inline def resEval(i: BigInt, res: Result) = { (res match { case Parsed(j) => @@ -148,16 +152,38 @@ object PackratParsing { }) } - def invoke(i: BigInt): (Result, Result, Result) = { - require(i == 0 || (i > 0 && allEval(i-1))) - (pPrim(i), pMul(i), pAdd(i)) - } ensuring (res => { - val in = Mem.inState[Result] - val out = Mem.outState[Result] + def invokePrim(i: BigInt): Result = { + require(depsEval(i)) + pPrim(i) + } ensuring {res => + val in = inState[Result] + val out = outState[Result] + (if(i >0) evalMono(i-1, in, out) else true) + } + + def invokeMul(i: BigInt): Result = { + require(depsEval(i)) + invokePrim(i) match { + case _ => pMul(i) + } + } ensuring {res => + val in = inState[Result] + val out = outState[Result] + (if(i >0) evalMono(i-1, in, out) else true) + } + + def invoke(i: BigInt): Result = { + require(depsEval(i)) + invokeMul(i) match { + case _ => pAdd(i) + } + } ensuring{ res => + val in = inState[Result] + val out = outState[Result] (if(i >0) evalMono(i-1, in, out) else true) && allEval(i) && - time <= 200 - }) + time <= 189 + } /** * Parsing a string of length 'n+1'. @@ -166,11 +192,12 @@ object PackratParsing { */ def parse(n: BigInt): Result = { require(n >= 0) - if(n == 0) invoke(n)._3 + if(n == 0) invoke(n) else { - val tailres = parse(n-1) // we parse the prefixes ending at 0, 1, 2, 3, ..., n - invoke(n)._3 + parse(n-1) match { // we parse the prefixes ending at 0, 1, 2, 3, ..., n + case _ => invoke(n) + } } } ensuring(_ => allEval(n) && - time <= 250*n + 250) + time <= 198*n + 192) } diff --git a/testcases/lazy-datastructures/memoization/WeightedScheduling.scala b/testcases/lazy-datastructures/memoization/WeightedScheduling.scala index cf2fa7508ef1f3ffa9fe686a18278c3a270ef925..e640bd79e7fa7950e5b3dd01389c4bd84f734045 100644 --- a/testcases/lazy-datastructures/memoization/WeightedScheduling.scala +++ b/testcases/lazy-datastructures/memoization/WeightedScheduling.scala @@ -1,5 +1,5 @@ -import leon.lazyeval._ -import leon.lazyeval.Mem._ +import leon._ +import leon.mem._ import leon.lang._ import leon.annotation._ import leon.instrumentation._ @@ -69,7 +69,7 @@ object WeightedSched { @memoize def sched(jobIndex: BigInt): BigInt = { require(depsEval(jobIndex) && - (jobIndex == 0 || prevCompatibleJob(jobIndex) >= 0)) //evalLem(prevCompatibleJob(jobIndex), jobIndex-1))) + (jobIndex == 0 || evalLem(prevCompatibleJob(jobIndex), jobIndex-1))) val (st, fn, w) = jobInfo(jobIndex) if(jobIndex == 0) w else { @@ -85,8 +85,8 @@ object WeightedSched { require(depsEval(jobIndex)) sched(jobIndex) } ensuring (res => { - val in = Mem.inState[BigInt] - val out = Mem.outState[BigInt] + val in = inState[BigInt] + val out = outState[BigInt] (jobIndex == 0 || evalMono(jobIndex-1, in, out)) && time <= 150 }) diff --git a/testcases/lazy-datastructures/project/Build.scala b/testcases/lazy-datastructures/project/Build.scala new file mode 100644 index 0000000000000000000000000000000000000000..4d96b683b440afe56a993d3790105a0092410165 --- /dev/null +++ b/testcases/lazy-datastructures/project/Build.scala @@ -0,0 +1,21 @@ +import sbt._ +import Keys._ + +object BuildSettings { + val paradiseVersion = "2.1.0-M5" + val buildSettings = Defaults.defaultSettings ++ Seq( + resolvers += Resolver.sonatypeRepo("snapshots"), + resolvers += Resolver.sonatypeRepo("releases"), + addCompilerPlugin("org.scalamacros" % "paradise" % paradiseVersion cross CrossVersion.full) + ) +} + +object MyBuild extends Build { + import BuildSettings._ + + lazy val root = Project( + "root", + file("."), + settings = buildSettings + ) +} diff --git a/testcases/lazy-datastructures/withOrb/BottomUpMegeSort.scala b/testcases/lazy-datastructures/withOrb/BottomUpMegeSort.scala index 10a0047cf2813906ad084be22253b656d6fed512..71179a087cffb9fe6c2a465d5884953a2432725e 100644 --- a/testcases/lazy-datastructures/withOrb/BottomUpMegeSort.scala +++ b/testcases/lazy-datastructures/withOrb/BottomUpMegeSort.scala @@ -1,11 +1,12 @@ -package orb +package withOrb -import leon.lazyeval._ -import leon.lazyeval.$._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import lazyeval._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ +import stats._ /** * TODO Multiple instantiations of type parameters is not supported yet, @@ -43,10 +44,10 @@ object BottomUpMergeSort { } } ensuring (_ >= 0) } - case class SCons(x: BigInt, tail: $[IStream]) extends IStream + case class SCons(x: BigInt, tail: Lazy[IStream]) extends IStream case class SNil() extends IStream @inline - def ssize(l: $[IStream]): BigInt = (l*).size + def ssize(l: Lazy[IStream]): BigInt = (l*).size /** * A list of suspensions @@ -73,7 +74,7 @@ object BottomUpMergeSort { } } ensuring (_ >= 0) } - case class LCons(x: $[IStream], tail: LList) extends LList + case class LCons(x: Lazy[IStream], tail: LList) extends LList case class LNil() extends LList /** @@ -109,14 +110,15 @@ object BottomUpMergeSort { case _ => constructMergeTree(pairs(l)) } - } ensuring (res => res.size <= 1 && res.fullSize == l.fullSize && + } ensuring {res => + res.size <= 1 && res.fullSize == l.fullSize && (res match { case LCons(il, LNil()) => res.fullSize == ssize(il) // this is implied by the previous conditions case _ => true }) && res.valid && - time <= ? * l.size + ?) + time <= ? * l.size + ?} /** * A function that merges two sorted streams of integers. @@ -125,7 +127,7 @@ object BottomUpMergeSort { */ @invisibleBody @usePost - def merge(a: $[IStream], b: $[IStream]): IStream = { + def merge(a: Lazy[IStream], b: Lazy[IStream]): IStream = { require(((a*) != SNil() || b.isEvaluated) && // if one of the arguments is Nil then the other is evaluated ((b*) != SNil() || a.isEvaluated) && ((a*) != SNil() || (b*) != SNil())) // at least one of the arguments is not Nil @@ -142,22 +144,27 @@ object BottomUpMergeSort { } } } ensuring (res => ssize(a) + ssize(b) == res.size && - time <= ? * res.size + ?) // note: res.size >= 1 // here stack is max of a and b + //time <= ? * res.size + ?) // note: res.size >= 1 // here stack is max of a and b + time <= 67 * res.size - 47) // Orb cannot infer this due to issues with CVC4 set solving ! /** * Converts a list of integers to a list of streams of integers */ + val nilStream: IStream = SNil() + @invisibleBody def IListToLList(l: IList): LList = { l match { case INil() => LNil() case ICons(x, xs) => - LCons(SCons(x, SNil()), IListToLList(xs)) + LCons(SCons(x, nilStream), IListToLList(xs)) } - } ensuring (res => res.fullSize == l.size && - res.size == l.size && - res.valid && - time <= ? * l.size + ?) + } ensuring { res => + res.fullSize == l.size && + res.size == l.size && + res.valid && + time <= ? * l.size + ? + } /** * Takes list of integers and returns a sorted stream of integers. @@ -182,4 +189,50 @@ object BottomUpMergeSort { case SCons(x, rest) => x } } ensuring (res => time <= ? * l.size + ?) + + def kthMin(l: IStream, k: BigInt): BigInt = { + require(k >= 0) + l match { + case SCons(x, xs) => + if (k == 0) x + else + kthMin(xs.value, k - 1) + case SNil() => BigInt(0) + } + } //ensuring (_ => time <= ? * (k * ssize(l)) + ? * k + ?) + + @ignore + def main(args: Array[String]) { + //import eagerEval.MergeSort + import scala.util.Random + import scala.math.BigInt + import stats._ + import collection._ + + println("Running merge sort test...") + val length = 3000000 + val maxIndexValue = 100 + val randomList = Random.shuffle((0 until length).toList) + val l1 = randomList.foldRight(List[BigInt]()){ + case (i, acc) => BigInt(i) :: acc + } + val l2 = randomList.foldRight(INil(): IList){ + case (i, acc) => ICons(BigInt(i), acc) + } + println(s"Created inputs of size (${l1.size},${l2.size}), starting operations...") + val sort2 = timed{ mergeSort(l2) }{t => println(s"Lazy merge sort completed in ${t/1000.0} sec") } + //val sort1 = timed{ MergeSort.msort((x: BigInt, y: BigInt) => x <= y)(l1) } {t => println(s"Eager merge sort completed in ${t/1000.0} sec") } + // sample 10 elements from a space of [0-100] + val rand = Random + var totalTime1 = 0L + var totalTime2 = 0L + for(i <- 0 until 10) { + val idx = rand.nextInt(maxIndexValue) + //val e1 = timed { sort1(idx) } { totalTime1 +=_ } + val e2 = timed { kthMin(sort2, idx) }{ totalTime2 += _ } + //println(s"Element at index $idx - Eager: $e1 Lazy: $e2") + //assert(e1 == e2) + } + println(s"Time-taken to pick first 10 minimum elements - Eager: ${totalTime1/1000.0}s Lazy: ${totalTime2/1000.0}s") + } } diff --git a/testcases/lazy-datastructures/withOrb/Concat.scala b/testcases/lazy-datastructures/withOrb/Concat.scala index bd4978f6a4c6145470a3fd2983e8a4e27420a20c..b9653b5224a19298684e39bb6e3de401815cb40b 100644 --- a/testcases/lazy-datastructures/withOrb/Concat.scala +++ b/testcases/lazy-datastructures/withOrb/Concat.scala @@ -1,11 +1,13 @@ package withOrb -import leon.lazyeval._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.collection._ -import leon.invariant._ +import leon._ +import lazyeval._ +import lazyeval.Lazy._ +import lang._ +import annotation._ +import instrumentation._ +import collection._ +import invariant._ object Concat { sealed abstract class LList[T] { @@ -16,30 +18,60 @@ object Concat { } } ensuring (_ >= 0) } - case class SCons[T](x: T, tail: $[LList[T]]) extends LList[T] + case class SCons[T](x: T, tail: Lazy[LList[T]]) extends LList[T] case class SNil[T]() extends LList[T] - def ssize[T](l: $[LList[T]]): BigInt = (l*).size + def ssize[T](l: Lazy[LList[T]]): BigInt = (l*).size - def concat[T](l1: List[T], l2: List[T]) : LList[T] = { + def concat[T](l1: List[T], l2: List[T]): LList[T] = { l1 match { case Cons(x, xs) => SCons(x, $(concat(xs, l2))) - case Nil() => SNil[T]() + case Nil() => SNil[T]() } - } ensuring(_ => time <= ?) + } ensuring { _ => time <= ? } - def kthElem[T](l: $[LList[T]], k: BigInt): Option[T] = { - require(k >= 1) + def kthElem[T](l: Lazy[LList[T]], k: BigInt): Option[T] = { + require(k >= 0) l.value match { case SCons(x, xs) => - if (k == 1) Some(x) + if (k == 0) Some(x) else kthElem(xs, k - 1) case SNil() => None[T] } - } ensuring (_ => time <= ? * k) + } ensuring (_ => time <= ? * k + ?) - def concatnSelect[T](l1: List[T], l2: List[T], k: BigInt) : Option[T] = { - require(k >= 1) + def concatnSelect[T](l1: List[T], l2: List[T], k: BigInt): Option[T] = { + require(k >= 0) kthElem($(concat(l1, l2)), k) - } ensuring (_ => time <= ? * k) + } ensuring (_ => time <= ? * k + ?) + + @ignore + def main(args: Array[String]) = { + import stats._ + println("Running concat test...") + val length = 1000000 + val k = 10 + val iterCount = 10 + val l1 = (0 until length).foldRight(List[BigInt]()) { + case (i, acc) => i :: acc + } + val l2 = (0 until length).foldRight(List[BigInt]()) { + case (i, acc) => 2 * i :: acc + } + println("Created inputs, starting operations...") + def iterate[T](op: => BigInt) = { + var res = BigInt(0) + var i = iterCount + while (i > 0) { + res = op + i -= 1 + } + res + } + val elem1 = timed { iterate((l1 ++ l2)(k)) } { t => println(s"Eager Concat completed in ${t / 1000.0} sec") } + println(s"$k th element of concatenated list: $elem1") + val elem2 = timed { iterate(concatnSelect(l1, l2, k).get) } { t => println(s"Lazy ConcatnSelect completed in ${t / 1000.0} sec") } + println(s"$k th element of concatenated list: $elem2") + assert(elem1 == elem2) + } } diff --git a/testcases/lazy-datastructures/withOrb/Deque.scala b/testcases/lazy-datastructures/withOrb/Deque.scala index 6493eb8c8b2bef8b3955c31c71c8ffe4eedfb6f2..38daf01442f4b12d9b7c39f4e6abf8f3e677186a 100644 --- a/testcases/lazy-datastructures/withOrb/Deque.scala +++ b/testcases/lazy-datastructures/withOrb/Deque.scala @@ -1,13 +1,13 @@ -package orb +package withOrb -import leon.lazyeval._ -import leon.lazyeval.$._ -import leon.lang._ -import leon.annotation._ -import leon.collection._ -import leon.instrumentation._ -import leon.math._ -import leon.invariant._ +import leon._ +import lazyeval._ +import lang._ +import annotation._ +import collection._ +import instrumentation._ +import math._ +import invariant._ /** * A constant time deque based on Okasaki's implementation: Fig.8.4 Pg. 112. @@ -43,13 +43,13 @@ object RealTimeDeque { } } ensuring (_ >= 0) } - case class SCons[T](x: T, tail: $[Stream[T]]) extends Stream[T] + case class SCons[T](x: T, tail: Lazy[Stream[T]]) extends Stream[T] case class SNil[T]() extends Stream[T] @inline - def ssize[T](l: $[Stream[T]]): BigInt = (l*).size + def ssize[T](l: Lazy[Stream[T]]): BigInt = (l*).size - def isConcrete[T](l: $[Stream[T]]): Boolean = { + def isConcrete[T](l: Lazy[Stream[T]]): Boolean = { l.isEvaluated && (l* match { case SCons(_, tail) => isConcrete(tail) @@ -58,12 +58,25 @@ object RealTimeDeque { } @invstate - def revAppend[T](l1: $[Stream[T]], l2: $[Stream[T]]): $[Stream[T]] = { + def takeLazy[T](n: BigInt, l: Lazy[Stream[T]]): Stream[T] = { + require(isConcrete(l) && n >= 1 && ssize(l) >= n) + l.value match { + case SCons(x, tail) => + if (n == 1) + SCons[T](x, SNil[T]()) + else + SCons[T](x, $(takeLazy(n - 1, tail))) + } + } ensuring(res => res.size == n && res.isCons && + time <= ?) + + @invstate + def revAppend[T](l1: Lazy[Stream[T]], l2: Lazy[Stream[T]]): Lazy[Stream[T]] = { require(isConcrete(l1) && isConcrete(l2)) l1.value match { case SNil() => l2 case SCons(x, tail) => - val nt: $[Stream[T]] = SCons[T](x, l2) + val nt: Lazy[Stream[T]] = SCons[T](x, l2) revAppend(tail, nt) } } ensuring(res => ssize(res) == ssize(l1) + ssize(l2) && @@ -72,7 +85,7 @@ object RealTimeDeque { time <= ? * ssize(l1) + ?) @invstate - def drop[T](n: BigInt, l: $[Stream[T]]): $[Stream[T]] = { + def drop[T](n: BigInt, l: Lazy[Stream[T]]): Lazy[Stream[T]] = { require(n >= 0 && isConcrete(l) && ssize(l) >= n) if (n == 0) { l @@ -87,9 +100,9 @@ object RealTimeDeque { time <= ? * n + ?) @invstate - def take[T](n: BigInt, l: $[Stream[T]]): $[Stream[T]] = { + def take[T](n: BigInt, l: Lazy[Stream[T]]): Lazy[Stream[T]] = { require(n >= 0 && isConcrete(l) && ssize(l) >= n) - val r: $[Stream[T]] = + val r: Lazy[Stream[T]] = if (n == 0) { SNil[T]() } else { @@ -105,20 +118,7 @@ object RealTimeDeque { time <= ? * n + ?) @invstate - def takeLazy[T](n: BigInt, l: $[Stream[T]]): Stream[T] = { - require(isConcrete(l) && n >= 1 && ssize(l) >= n) - l.value match { - case SCons(x, tail) => - if (n == 1) - SCons[T](x, SNil[T]()) - else - SCons[T](x, $(takeLazy(n - 1, tail))) - } - } ensuring(res => res.size == n && res.isCons && - time <= ?) - - @invstate - def rotateRev[T](r: $[Stream[T]], f: $[Stream[T]], a: $[Stream[T]]): Stream[T] = { + def rotateRev[T](r: Lazy[Stream[T]], f: Lazy[Stream[T]], a: Lazy[Stream[T]]): Stream[T] = { require(isConcrete(r) && isConcrete(f) && isConcrete(a) && { val lenf = ssize(f) @@ -135,7 +135,7 @@ object RealTimeDeque { time <= ?) @invstate - def rotateDrop[T](r: $[Stream[T]], i: BigInt, f: $[Stream[T]]): Stream[T] = { + def rotateDrop[T](r: Lazy[Stream[T]], i: BigInt, f: Lazy[Stream[T]]): Stream[T] = { require(isConcrete(r) && isConcrete(f) && i >= 0 && { val lenf = ssize(f) val lenr = ssize(r) @@ -144,7 +144,7 @@ object RealTimeDeque { }) val rval = r.value if(i < 2 || rval == SNil[T]()) { // A bug in Okasaki implementation: we must check for: 'rval = SNil()' - val a: $[Stream[T]] = SNil[T]() + val a: Lazy[Stream[T]] = SNil[T]() rotateRev(r, drop(i, f), a) } else { rval match { @@ -155,7 +155,7 @@ object RealTimeDeque { } ensuring(res => res.size == (r*).size + (f*).size - i && res.isCons && time <= ?) - def firstUneval[T](l: $[Stream[T]]): $[Stream[T]] = { + def firstUneval[T](l: Lazy[Stream[T]]): Lazy[Stream[T]] = { if (l.isEvaluated) { l* match { case SCons(_, tail) => @@ -172,8 +172,8 @@ object RealTimeDeque { case _ => true })) - case class Queue[T](f: $[Stream[T]], lenf: BigInt, sf: $[Stream[T]], - r: $[Stream[T]], lenr: BigInt, sr: $[Stream[T]]) { + case class Queue[T](f: Lazy[Stream[T]], lenf: BigInt, sf: Lazy[Stream[T]], + r: Lazy[Stream[T]], lenr: BigInt, sr: Lazy[Stream[T]]) { def isEmpty = lenf + lenr == 0 def valid = { (firstUneval(f) == firstUneval(sf)) && @@ -192,8 +192,8 @@ object RealTimeDeque { * the balance invariant, and restores the balance. */ @invisibleBody - def createQueue[T](f: $[Stream[T]], lenf: BigInt, sf: $[Stream[T]], - r: $[Stream[T]], lenr: BigInt, sr: $[Stream[T]]): Queue[T] = { + def createQueue[T](f: Lazy[Stream[T]], lenf: BigInt, sf: Lazy[Stream[T]], + r: Lazy[Stream[T]], lenr: BigInt, sr: Lazy[Stream[T]]): Queue[T] = { require(firstUneval(f) == firstUneval(sf) && firstUneval(r) == firstUneval(sr) && (lenf == ssize(f) && lenr == ssize(r)) && @@ -220,15 +220,14 @@ object RealTimeDeque { } ensuring(res => res.valid && time <= ?) - @invisibleBody - def funeEqual[T](s1: $[Stream[T]], s2: $[Stream[T]]) = firstUneval(s1) == firstUneval(s2) - + def funeEqual[T](s1: Lazy[Stream[T]], s2: Lazy[Stream[T]]) = firstUneval(s1) == firstUneval(s2) + /** * Forces the schedules, and ensures that `firstUneval` equality is preserved */ @invisibleBody - def force[T](tar: $[Stream[T]], htar: $[Stream[T]], other: $[Stream[T]], hother: $[Stream[T]]): $[Stream[T]] = { + def force[T](tar: Lazy[Stream[T]], htar: Lazy[Stream[T]], other: Lazy[Stream[T]], hother: Lazy[Stream[T]]): Lazy[Stream[T]] = { require(funeEqual(tar, htar) && funeEqual(other, hother)) tar.value match { case SCons(_, tail) => tail @@ -236,8 +235,8 @@ object RealTimeDeque { } } ensuring (res => { //lemma instantiations - val in = $.inState[Stream[T]] - val out = $.outState[Stream[T]] + val in = inState[Stream[T]] + val out = outState[Stream[T]] funeMonotone(tar, htar, in, out) && funeMonotone(other, hother, in, out) && { //properties @@ -252,12 +251,12 @@ object RealTimeDeque { * Forces the schedules in the queue twice and ensures the `firstUneval` property. */ @invisibleBody - def forceTwice[T](q: Queue[T]): ($[Stream[T]], $[Stream[T]]) = { + def forceTwice[T](q: Queue[T]): (Lazy[Stream[T]], Lazy[Stream[T]]) = { require(q.valid) val nsf = force(force(q.sf, q.f, q.r, q.sr), q.f, q.r, q.sr) // forces q.sf twice val nsr = force(force(q.sr, q.r, q.f, nsf), q.r, q.f, nsf) // forces q.sr twice (nsf, nsr) - } ensuring(time <= ?) + } ensuring(_ => time <= ?) // the following properties are ensured, but need not be stated /*ensuring (res => { val nsf = res._1 @@ -270,10 +269,18 @@ object RealTimeDeque { })*/ def empty[T] = { - val nil: $[Stream[T]] = SNil[T]() + val nil: Lazy[Stream[T]] = SNil[T]() Queue(nil, 0, nil, nil, 0, nil) } + def head[T](q: Queue[T]): T = { + require(!q.isEmpty && q.valid) + (q.f.value, q.r.value) match { + case (SCons(x, _), _) => x + case (_, SCons(x, _)) => x + } + } + /** * Adding an element to the front of the list */ @@ -317,14 +324,19 @@ object RealTimeDeque { def reverse[T](q: Queue[T]): Queue[T] = { require(q.valid) Queue(q.r, q.lenr, q.sr, q.f, q.lenf, q.sf) - } ensuring(q.valid && time <= ?) + } ensuring(_ => q.valid && time <= ?) + + def snoc[T](x: T, q: Queue[T]): Queue[T] = { + require(q.valid) + reverse(cons(x, reverse(q))) + } // Properties of `firstUneval`. We use `fune` as a shorthand for `firstUneval` /** * st1.subsetOf(st2) ==> fune(l, st2) == fune(fune(l, st1), st2) */ @traceInduct - def funeCompose[T](l1: $[Stream[T]], st1: Set[$[Stream[T]]], st2: Set[$[Stream[T]]]): Boolean = { + def funeCompose[T](l1: Lazy[Stream[T]], st1: Set[Lazy[Stream[T]]], st2: Set[Lazy[Stream[T]]]): Boolean = { require(st1.subsetOf(st2)) // property (firstUneval(l1) withState st2) == (firstUneval(firstUneval(l1) withState st1) withState st2) @@ -336,7 +348,7 @@ object RealTimeDeque { * This is a kind of frame axiom for `fune` but is slightly different in that * it doesn't require (st2 \ st1) to be disjoint from la and lb. */ - def funeMonotone[T](l1: $[Stream[T]], l2: $[Stream[T]], st1: Set[$[Stream[T]]], st2: Set[$[Stream[T]]]): Boolean = { + def funeMonotone[T](l1: Lazy[Stream[T]], l2: Lazy[Stream[T]], st1: Set[Lazy[Stream[T]]], st2: Set[Lazy[Stream[T]]]): Boolean = { require((firstUneval(l1) withState st1) == (firstUneval(l2) withState st1) && st1.subsetOf(st2)) funeCompose(l1, st1, st2) && // lemma instantiations @@ -351,4 +363,49 @@ object RealTimeDeque { } else true) && (firstUneval(l1) withState st2) == (firstUneval(l2) withState st2) // property } holds + + @ignore + def main(args: Array[String]) { + //import eagerEval.AmortizedQueue + import scala.util.Random + import scala.math.BigInt + import stats._ + import collection._ + + println("Running Deque test...") + val ops = 2000000 + val rand = Random + // initialize to a queue with one element (required to satisfy preconditions of dequeue and front) + var rtd = empty[BigInt] + //var amq = AmortizedQueue.Queue(AmortizedQueue.Nil(), AmortizedQueue.Nil()) + var totalTime1 = 0L + var totalTime2 = 0L + println(s"Testing amortized emphemeral behavior on $ops operations...") + for (i <- 0 until ops) { + if (!rtd.isEmpty) { + val h1 = head(rtd) + //val h2 = amq.head + //assert(h1 == h2, s"Eager head: $h2 Lazy head: $h1") + } + rand.nextInt(3) match { + case x if x == 0 => //add to rear + //println("Enqueue..") + rtd = timed { snoc(BigInt(i), rtd) } { totalTime1 += _ } + //amq = timed { amq.enqueue(BigInt(i)) } { totalTime2 += _ } + case x if x == 1 => // remove from front + if (!rtd.isEmpty) { + //if(i%100000 == 0) + //println("Dequeue..") + rtd = timed { tail(rtd) } { totalTime1 += _ } + //amq = timed { amq.dequeue } { totalTime2 += _ } + } + case x if x == 2 => // reverse + //if(i%100000 == 0) + //println("reverse..") + rtd = timed { reverse(rtd) } { totalTime1 += _ } + //amq = timed { amq.reverse } { totalTime2 += _ } + } + } + println(s"Ephemeral Amortized Time - Eager: ${totalTime2/1000.0}s Lazy: ${totalTime1/1000.0}s") + } } diff --git a/testcases/lazy-datastructures/withOrb/Esop15Benchmarks.scala b/testcases/lazy-datastructures/withOrb/Esop15Benchmarks.scala index ee381e608d9301725586dffe5c20e391920583bb..45d520dcf67579ddccb94c3f2d95278a16896e93 100644 --- a/testcases/lazy-datastructures/withOrb/Esop15Benchmarks.scala +++ b/testcases/lazy-datastructures/withOrb/Esop15Benchmarks.scala @@ -1,12 +1,12 @@ -package lazybenchmarks +package withorb -import leon.lazyeval._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.lazyeval.$._ -import leon.collection._ -import leon.invariant._ +import leon._ +import lazyeval._ +import lang._ +import annotation._ +import instrumentation._ +import collection._ +import invariant._ /** * This file is the collection of programs in the ESOP 2015 paper. @@ -15,14 +15,14 @@ import leon.invariant._ */ object Esop15Benchmarks { sealed abstract class IStream - case class SCons(x: BigInt, tail: $[IStream]) extends IStream + case class SCons(x: BigInt, tail: Lazy[IStream]) extends IStream case class SNil() extends IStream sealed abstract class StreamPairs - case class PCons(pair: (BigInt, BigInt), tail: $[StreamPairs]) extends StreamPairs + case class PCons(pair: (BigInt, BigInt), tail: Lazy[StreamPairs]) extends StreamPairs case class PNil() extends StreamPairs - def zipWith(xs: $[IStream], ys: $[IStream]): StreamPairs = { + def zipWith(xs: Lazy[IStream], ys: Lazy[IStream]): StreamPairs = { (xs.value, ys.value) match { case (SCons(x, xtail), SCons(y, ytail)) => PCons((x, y), $(zipWith(xtail, ytail))) @@ -44,7 +44,7 @@ object Esop15Benchmarks { SCons(0, SCons(1, $(nextFib(0, 1, n)))) } - def nthFib(n: BigInt, fs: $[IStream]): BigInt = { + def nthFib(n: BigInt, fs: Lazy[IStream]): BigInt = { require(n >= 0) fs.value match { case SCons(x, tail) => diff --git a/testcases/lazy-datastructures/withOrb/FibonacciMemoized.scala b/testcases/lazy-datastructures/withOrb/FibonacciMemoized.scala index 89ce222e49424eb2f609a15952801f6562a447be..db9db04212e5ee2dcd98ac37fd11b95ba4d3c9b1 100644 --- a/testcases/lazy-datastructures/withOrb/FibonacciMemoized.scala +++ b/testcases/lazy-datastructures/withOrb/FibonacciMemoized.scala @@ -1,11 +1,12 @@ -package orb +package withOrb -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import mem._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ +import Mem._ object FibMem { sealed abstract class IList @@ -15,11 +16,16 @@ object FibMem { @memoize def fibRec(n: BigInt): BigInt = { require(n >= 0) - if(n <= 2) - BigInt(1) - else - fibRec(n-1) + fibRec(n-2) // postcondition implies that the second call would be cached - } ensuring(_ => - (n <= 2 || (fibRec(n-1).isCached && - fibRec(n-2).isCached)) && time <= ? * n + ?) + if (n <= 2) + BigInt(1) + else + fibRec(n - 1) + fibRec(n - 2) // postcondition implies that the second call would be cached + } ensuring (_ => + (n <= 2 || (fibRec(n - 1).isCached && + fibRec(n - 2).isCached)) && time <= ? * n + ?) + + @ignore + def main(args: Array[String]) { + println("32nd fibonnacci number: " + fibRec(50)) + } } diff --git a/testcases/lazy-datastructures/withOrb/HammingMemoized.scala b/testcases/lazy-datastructures/withOrb/HammingMemoized.scala index 4b214ed9045b7875d01c1d4b8b046e2aa11cb772..65192934bb0f87128e9e6a8c8e64d436cd9ecd62 100644 --- a/testcases/lazy-datastructures/withOrb/HammingMemoized.scala +++ b/testcases/lazy-datastructures/withOrb/HammingMemoized.scala @@ -1,11 +1,11 @@ -package orb +package withOrb -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import mem._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ /** * A memoized version of the implementation of Hamming problem shown in @@ -13,8 +13,17 @@ import leon.invariant._ */ object Hamming { sealed abstract class IList - case class Cons(x: BigInt, tail: IList) extends IList - case class Nil() extends IList + case class Cons(x: BigInt, tail: IList) extends IList { + @ignore + override def toString: String = { + if(tail == Nil()) x.toString + else x.toString + "," + tail.toString + } + } + case class Nil() extends IList { + @ignore + override def toString = "" + } case class Data(v: BigInt, i2: BigInt, i3: BigInt, i5: BigInt) @@ -25,9 +34,9 @@ object Hamming { if(n == BigInt(0)) Data(1, 0, 0, 0) else { val Data(x, i2, i3, i5) = ham(n-1) - val a = ham(i2).i2 * 2 - val b = ham(i3).i3 * 3 - val c = ham(i5).i5 * 5 + val a = ham(i2).v * 2 + val b = ham(i3).v * 3 + val c = ham(i5).v * 5 val (v, ni, nj, nk) = threeWayMerge(a, b, c, i2, i3, i5) Data(v, ni, nj, nk) } @@ -57,8 +66,8 @@ object Hamming { require(n == 0 || n > 0 && depsEval(n - 1)) ham(n).v } ensuring (res => { - val in = Mem.inState[Data] - val out = Mem.outState[Data] + val in = inState[Data] + val out =outState[Data] (n == 0 || depsEvalMono(n-1, in, out)) && // instantiation time <= ? }) @@ -86,4 +95,11 @@ object Hamming { else if(b < c && b < a) (b, i2 , i3 + 1, i5 ) else/*if(c < a && c < b)*/(c, i2 , i3 , i5 + 1) } + + @ignore + def main(args: Array[String]) { + import collection._ + val hlist = hammingList(100) // without memoization this will take too long + println("Hamming numbers: "+hlist) + } } diff --git a/testcases/lazy-datastructures/withOrb/Knapsack.scala b/testcases/lazy-datastructures/withOrb/Knapsack.scala index e1645b545160e455e7878e23e4ce1292ef018b9a..8eb64d369dbe2ef198508103d4790ae51c37ef3a 100644 --- a/testcases/lazy-datastructures/withOrb/Knapsack.scala +++ b/testcases/lazy-datastructures/withOrb/Knapsack.scala @@ -1,11 +1,11 @@ -package orb +package wihtOrb -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import mem._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ object Knapscak { sealed abstract class IList { @@ -16,8 +16,17 @@ object Knapscak { } } ensuring(_ >= 0) } - case class Cons(x: (BigInt, BigInt), tail: IList) extends IList // a list of pairs of weights and values - case class Nil() extends IList + case class Cons(x: (BigInt, BigInt), tail: IList) extends IList { // a list of pairs of weights and values + @ignore + override def toString: String = { + if(tail == Nil()) x.toString + else x.toString + "," + tail.toString + } + } + case class Nil() extends IList { + @ignore + override def toString = "" + } def deps(i: BigInt, items: IList): Boolean = { require(i >= 0) @@ -78,7 +87,7 @@ object Knapscak { else { Cons((i,ri), bottomup(i + 1, w, items)) } - } ensuring(items.size <= 10 ==> time <= ? * (w - i + 1)) + } ensuring(_ => items.size <= 10 ==> time <= ? * (w - i + 1)) /** * Computes the list of optimal solutions of all weights up to 'w' @@ -86,7 +95,7 @@ object Knapscak { def knapSackSol(w: BigInt, items: IList) = { require(w >= 0 && items.size <= 10) // the second requirement is only to keep the bounds linear for z3 to work bottomup(0, w, items) - } ensuring(time <= ? * w + ?) + } ensuring(_ => time <= ? * w + ?) /** * Lemmas of deps @@ -104,4 +113,20 @@ object Knapscak { require(x >= 0 && y >= 0) (x <= y && deps(y, items)) ==> deps(x, items) } holds + + @ignore + def main(args: Array[String]) { + import scala.util.Random + // pick some random weights and values + val weightsnValues1 = (1 to 10).foldRight(Nil(): IList){ + case (i, acc) => Cons((i, i), acc) + } + val reslist1 = knapSackSol(100, weightsnValues1) // without memoization this will take too long + println("Optimal solutions: "+reslist1.toString) + val weightsnValues2 = ((1 to 10) zip (10 to 1 by -1)).foldRight(Nil(): IList){ + case ((i, j), acc) => Cons((i, j), acc) + } + val reslist2 = knapSackSol(100, weightsnValues2) + println("Optimal solutions for set 2: "+reslist2.toString) + } } diff --git a/testcases/lazy-datastructures/withOrb/LazyNumericalRep.scala b/testcases/lazy-datastructures/withOrb/LazyNumericalRep.scala index 47a42bfa1d6d891a843db9767ac94562bc343c0e..b3c5a205fd392de97bc653a5750698197b825239 100644 --- a/testcases/lazy-datastructures/withOrb/LazyNumericalRep.scala +++ b/testcases/lazy-datastructures/withOrb/LazyNumericalRep.scala @@ -1,16 +1,22 @@ -package orb +package withOrb -import leon.lazyeval._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.lazyeval.$._ -import leon.invariant._ +import leon._ +import lazyeval._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ object DigitObject { sealed abstract class Digit - case class Zero() extends Digit - case class One() extends Digit + case class Zero() extends Digit { + @ignore + override def toString = "0" + } + case class One() extends Digit { + @ignore + override def toString = "1" + } } import DigitObject._ @@ -19,13 +25,13 @@ object LazyNumericalRep { sealed abstract class NumStream { val isSpine: Boolean = this match { case Spine(_, _, _) => true - case _ => false + case _ => false } val isTip = !isSpine } case class Tip() extends NumStream - case class Spine(head: Digit, createdWithSuspension: Bool, rear: $[NumStream]) extends NumStream + case class Spine(head: Digit, createdWithSuspension: Bool, rear: Lazy[NumStream]) extends NumStream sealed abstract class Bool case class True() extends Bool @@ -34,7 +40,7 @@ object LazyNumericalRep { /** * Checks whether there is a zero before an unevaluated closure */ - def zeroPrecedeLazy[T](q: $[NumStream]): Boolean = { + def zeroPrecedeLazy[T](q: Lazy[NumStream]): Boolean = { if (q.isEvaluated) { q* match { case Spine(Zero(), _, rear) => @@ -49,7 +55,7 @@ object LazyNumericalRep { /** * Checks whether there is a zero before a given suffix */ - def zeroPrecedeSuf[T](q: $[NumStream], suf: $[NumStream]): Boolean = { + def zeroPrecedeSuf[T](q: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { if (q != suf) { q* match { case Spine(Zero(), _, rear) => true @@ -64,7 +70,7 @@ object LazyNumericalRep { * Everything until suf is evaluated. This * also asserts that suf should be a suffix of the list */ - def concreteUntil[T](l: $[NumStream], suf: $[NumStream]): Boolean = { + def concreteUntil[T](l: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { if (l != suf) { l.isEvaluated && (l* match { case Spine(_, cws, tail) => @@ -75,7 +81,7 @@ object LazyNumericalRep { } else true } - def isConcrete[T](l: $[NumStream]): Boolean = { + def isConcrete[T](l: Lazy[NumStream]): Boolean = { l.isEvaluated && (l* match { case Spine(_, _, tail) => isConcrete(tail) @@ -84,10 +90,10 @@ object LazyNumericalRep { } sealed abstract class Scheds - case class Cons(h: $[NumStream], tail: Scheds) extends Scheds + case class Cons(h: Lazy[NumStream], tail: Scheds) extends Scheds case class Nil() extends Scheds - def schedulesProperty[T](q: $[NumStream], schs: Scheds): Boolean = { + def schedulesProperty[T](q: Lazy[NumStream], schs: Scheds): Boolean = { schs match { case Cons(head, tail) => head* match { @@ -104,7 +110,7 @@ object LazyNumericalRep { } @invisibleBody - def strongSchedsProp[T](q: $[NumStream], schs: Scheds) = { + def strongSchedsProp[T](q: Lazy[NumStream], schs: Scheds) = { q.isEvaluated && { schs match { case Cons(head, tail) => @@ -119,7 +125,7 @@ object LazyNumericalRep { * Note: if 'q' has a suspension then it would have a carry. */ @invisibleBody - def pushUntilCarry[T](q: $[NumStream]): $[NumStream] = { + def pushUntilCarry[T](q: Lazy[NumStream]): Lazy[NumStream] = { q* match { case Spine(Zero(), _, rear) => // if we push a carry and get back 0 then there is a new carry pushUntilCarry(rear) @@ -130,12 +136,16 @@ object LazyNumericalRep { } } - case class Number(digs: $[NumStream], schedule: Scheds) { - val valid = strongSchedsProp(digs, schedule) + case class Number(digs: Lazy[NumStream], schedule: Scheds) { + def isEmpty = digs.value.isTip + + def valid = strongSchedsProp(digs, schedule) } + def emptyNum = Number(Tip(), Nil()) + @invisibleBody - def inc(xs: $[NumStream]): NumStream = { + def inc(xs: Lazy[NumStream]): NumStream = { require(zeroPrecedeLazy(xs)) xs.value match { case Tip() => @@ -149,11 +159,11 @@ object LazyNumericalRep { @invisibleBody @invstate - def incLazy(xs: $[NumStream]): NumStream = { + def incLazy(xs: Lazy[NumStream]): NumStream = { require(zeroPrecedeLazy(xs) && (xs* match { case Spine(h, _, _) => h != Zero() // xs doesn't start with a zero - case _ => false + case _ => false })) xs.value match { case Spine(head, _, rear) => // here, rear is guaranteed to be evaluated by 'zeroPrecedeLazy' invariant @@ -191,11 +201,11 @@ object LazyNumericalRep { */ @invisibleBody @invstate - def incLazyLemma[T](xs: $[NumStream], suf: $[NumStream]): Boolean = { + def incLazyLemma[T](xs: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { require(zeroPrecedeSuf(xs, suf) && (xs* match { case Spine(h, _, _) => h != Zero() - case _ => false + case _ => false }) && (suf* match { case Spine(Zero(), _, _) => @@ -243,7 +253,7 @@ object LazyNumericalRep { case _ => w.schedule } - val lq: $[NumStream] = nq + val lq: Lazy[NumStream] = nq (lq, nsched) } ensuring { res => // lemma instantiations @@ -263,7 +273,7 @@ object LazyNumericalRep { } @invisibleBody - def Pay[T](q: $[NumStream], scheds: Scheds): Scheds = { + def Pay[T](q: Lazy[NumStream], scheds: Scheds): Scheds = { require(schedulesProperty(q, scheds) && q.isEvaluated) scheds match { case c @ Cons(head, rest) => @@ -331,8 +341,15 @@ object LazyNumericalRep { } ensuring { res => res.valid && time <= ? } + def firstDigit(w: Number): Digit = { + require(!w.isEmpty) + w.digs.value match { + case Spine(d, _, _) => d + } + } + // monotonicity lemmas - def schedMonotone[T](st1: Set[$[NumStream]], st2: Set[$[NumStream]], scheds: Scheds, l: $[NumStream]): Boolean = { + def schedMonotone[T](st1: Set[Lazy[NumStream]], st2: Set[Lazy[NumStream]], scheds: Scheds, l: Lazy[NumStream]): Boolean = { require(st1.subsetOf(st2) && (schedulesProperty(l, scheds) withState st1)) // here the input state is fixed as 'st1' //induction scheme @@ -350,18 +367,18 @@ object LazyNumericalRep { } holds @traceInduct - def concreteMonotone[T](st1: Set[$[NumStream]], st2: Set[$[NumStream]], l: $[NumStream]): Boolean = { + def concreteMonotone[T](st1: Set[Lazy[NumStream]], st2: Set[Lazy[NumStream]], l: Lazy[NumStream]): Boolean = { ((isConcrete(l) withState st1) && st1.subsetOf(st2)) ==> (isConcrete(l) withState st2) } holds @traceInduct - def concUntilMonotone[T](q: $[NumStream], suf: $[NumStream], st1: Set[$[NumStream]], st2: Set[$[NumStream]]): Boolean = { + def concUntilMonotone[T](q: Lazy[NumStream], suf: Lazy[NumStream], st1: Set[Lazy[NumStream]], st2: Set[Lazy[NumStream]]): Boolean = { ((concreteUntil(q, suf) withState st1) && st1.subsetOf(st2)) ==> (concreteUntil(q, suf) withState st2) } holds // suffix predicates and their properties (this should be generalizable) - def suffix[T](q: $[NumStream], suf: $[NumStream]): Boolean = { + def suffix[T](q: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { if (q == suf) true else { q* match { @@ -372,7 +389,7 @@ object LazyNumericalRep { } } - def properSuffix[T](l: $[NumStream], suf: $[NumStream]): Boolean = { + def properSuffix[T](l: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { l* match { case Spine(_, _, rear) => suffix(rear, suf) @@ -384,7 +401,7 @@ object LazyNumericalRep { * suf(q, suf) ==> suf(q.rear, suf.rear) */ @traceInduct - def suffixTrans[T](q: $[NumStream], suf: $[NumStream]): Boolean = { + def suffixTrans[T](q: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { suffix(q, suf) ==> ((q*, suf*) match { case (Spine(_, _, rear), Spine(_, _, sufRear)) => // 'sufRear' should be a suffix of 'rear1' @@ -396,7 +413,7 @@ object LazyNumericalRep { /** * properSuf(l, suf) ==> l != suf */ - def suffixDisequality[T](l: $[NumStream], suf: $[NumStream]): Boolean = { + def suffixDisequality[T](l: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { require(properSuffix(l, suf)) suffixTrans(l, suf) && // lemma instantiation ((l*, suf*) match { // induction scheme @@ -408,21 +425,21 @@ object LazyNumericalRep { }.holds @traceInduct - def suffixCompose[T](q: $[NumStream], suf1: $[NumStream], suf2: $[NumStream]): Boolean = { + def suffixCompose[T](q: Lazy[NumStream], suf1: Lazy[NumStream], suf2: Lazy[NumStream]): Boolean = { (suffix(q, suf1) && properSuffix(suf1, suf2)) ==> properSuffix(q, suf2) } holds // properties of 'concUntil' @traceInduct - def concreteUntilIsSuffix[T](l: $[NumStream], suf: $[NumStream]): Boolean = { + def concreteUntilIsSuffix[T](l: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { concreteUntil(l, suf) ==> suffix(l, suf) }.holds // properties that extend `concUntil` to larger portions of the queue @traceInduct - def concUntilExtenLemma[T](q: $[NumStream], suf: $[NumStream], st1: Set[$[NumStream]], st2: Set[$[NumStream]]): Boolean = { + def concUntilExtenLemma[T](q: Lazy[NumStream], suf: Lazy[NumStream], st1: Set[Lazy[NumStream]], st2: Set[Lazy[NumStream]]): Boolean = { ((concreteUntil(q, suf) withState st1) && st2 == st1 ++ Set(suf)) ==> (suf* match { case Spine(_, _, rear) => @@ -432,12 +449,12 @@ object LazyNumericalRep { } holds @traceInduct - def concUntilConcreteExten[T](q: $[NumStream], suf: $[NumStream]): Boolean = { + def concUntilConcreteExten[T](q: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { (concreteUntil(q, suf) && isConcrete(suf)) ==> isConcrete(q) } holds @traceInduct - def concUntilCompose[T](q: $[NumStream], suf1: $[NumStream], suf2: $[NumStream]): Boolean = { + def concUntilCompose[T](q: Lazy[NumStream], suf1: Lazy[NumStream], suf2: Lazy[NumStream]): Boolean = { (concreteUntil(q, suf1) && concreteUntil(suf1, suf2)) ==> concreteUntil(q, suf2) } holds @@ -446,19 +463,19 @@ object LazyNumericalRep { @invisibleBody @traceInduct - def zeroPredSufConcreteUntilLemma[T](q: $[NumStream], suf: $[NumStream]): Boolean = { + def zeroPredSufConcreteUntilLemma[T](q: Lazy[NumStream], suf: Lazy[NumStream]): Boolean = { (zeroPrecedeSuf(q, suf) && concreteUntil(q, suf)) ==> zeroPrecedeLazy(q) } holds @invisibleBody @traceInduct - def concreteZeroPredLemma[T](q: $[NumStream]): Boolean = { + def concreteZeroPredLemma[T](q: Lazy[NumStream]): Boolean = { isConcrete(q) ==> zeroPrecedeLazy(q) } holds // properties relating `suffix` an `zeroPrecedeSuf` - def suffixZeroLemma[T](q: $[NumStream], suf: $[NumStream], suf2: $[NumStream]): Boolean = { + def suffixZeroLemma[T](q: Lazy[NumStream], suf: Lazy[NumStream], suf2: Lazy[NumStream]): Boolean = { require(suf* match { case Spine(Zero(), _, _) => suffix(q, suf) && properSuffix(suf, suf2) @@ -476,4 +493,43 @@ object LazyNumericalRep { } else true) && zeroPrecedeSuf(q, suf2) // property }.holds + + @ignore + def main(args: Array[String]) { + //import eagerEval.BigNums + import scala.util.Random + import scala.math.BigInt + import stats._ + import collection._ + + println("Running Numerical Representation test...") + val ops = 1000000 + // initialize to a queue with one element (required to satisfy preconditions of dequeue and front) + //var bignum: BigNums.BigNum = BigNums.Nil() + var lazynum = emptyNum + var totalTime1 = 0L + var totalTime2 = 0L + println(s"Testing amortized emphemeral behavior on $ops operations...") + for (i <- 0 until ops) { + //println("Inc..") + //bignum = timed { BigNums.increment(bignum) } { totalTime1 += _ } + lazynum = timed { incAndPay(lazynum) } { totalTime2 += _ } + //val d1 = BigNums.firstDigit(bignum) + val d2 = firstDigit(lazynum) + //assert(d1.toString == d2.toString, s"Eager head: $d1 Lazy head: $d2") + } + println(s"Ephemeral Amortized Time - Eager: ${totalTime1 / 1000.0}s Lazy: ${totalTime2 / 1000.0}s") // this should be linear in length for both cases + // now, test worst-case behavior (in persitent mode if necessary) + val length = (1 << 22) - 1 // a number of the form: 2^{n-1} + //bignum = BigNums.Nil() + lazynum = emptyNum + for (i <- 0 until length) { + //bignum = BigNums.increment(bignum) + lazynum = incAndPay(lazynum) + } + //println(s"Number of leading ones of bignum: ${BigNums.leadingOnes(bignum)}") + //dequeue 1 element from both queues + //timed { BigNums.increment(bignum) } { t => println(s"Time for one eager increment in the worst case: ${t / 1000.0}s") } + timed { incAndPay(lazynum) } { t => println(s"Time for one lazy increment in the worst case: ${t / 1000.0}s") } + } } diff --git a/testcases/lazy-datastructures/withOrb/PackratParsing.scala b/testcases/lazy-datastructures/withOrb/PackratParsing.scala index b9377da8ff6b0f3cb987dc32880b5216a90e9574..4cf2b81fea9a1a574acef30b0535a4596eceb46d 100644 --- a/testcases/lazy-datastructures/withOrb/PackratParsing.scala +++ b/testcases/lazy-datastructures/withOrb/PackratParsing.scala @@ -1,11 +1,11 @@ package orb -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import mem._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ /** * The packrat parser that uses the Expressions grammar presented in Bran Ford ICFP'02 paper. @@ -22,7 +22,9 @@ object PackratParsing { case class Digit() extends Terminal /** - * A mutable array of tokens returned by the lexer + * A mutable array of tokens returned by the lexer. + * The string of tokens is reversed i.e, + * string(legnth-1) represents the first char and string(0) represents the last char. */ @ignore var string = Array[Terminal]() @@ -33,7 +35,7 @@ object PackratParsing { @extern def lookup(i: BigInt): Terminal = { string(i.toInt) - } ensuring(_ => time <= 1) + } ensuring (_ => time <= 1) sealed abstract class Result { /** @@ -43,7 +45,7 @@ object PackratParsing { @inline def smallerIndex(i: BigInt) = this match { case Parsed(m) => m < i - case _ => true + case _ => true } } case class Parsed(rest: BigInt) extends Result @@ -53,48 +55,53 @@ object PackratParsing { @memoize @invstate def pAdd(i: BigInt): Result = { - require(depsEval(i) && - pMul(i).isCached && pPrim(i).isCached && - resEval(i, pMul(i))) // lemma inst - + require { + if (depsEval(i) && pMul(i).isCached && pPrim(i).isCached) + resEval(i, pMul(i)) // lemma inst + else false + } // Rule 1: Add <- Mul + Add - pMul(i) match { + val mulRes = pMul(i) + mulRes match { case Parsed(j) => if (j > 0 && lookup(j) == Plus()) { pAdd(j - 1) match { case Parsed(rem) => Parsed(rem) case _ => - pMul(i) // Rule2: Add <- Mul + mulRes // Rule2: Add <- Mul } - } else pMul(i) + } else mulRes case _ => - pMul(i) + mulRes } - } ensuring (res => res.smallerIndex(i) && time <= ?) + } ensuring (res => res.smallerIndex(i) && time <= ?) // time <= 26 @invisibleBody @memoize @invstate def pMul(i: BigInt): Result = { - require(depsEval(i) && pPrim(i).isCached && - resEval(i, pPrim(i)) // lemma inst - ) + require{ + if (depsEval(i) && pPrim(i).isCached) + resEval(i, pPrim(i)) // lemma inst + else false + } // Rule 1: Mul <- Prim * Mul - pPrim(i) match { + val primRes = pPrim(i) + primRes match { case Parsed(j) => - if (j > 0 && lookup(j) == Plus()) { + if (j > 0 && lookup(j) == Times()) { pMul(j - 1) match { case Parsed(rem) => Parsed(rem) case _ => - pPrim(i) // Rule2: Mul <- Prim + primRes // Rule2: Mul <- Prim } - } else pPrim(i) + } else primRes case _ => - pPrim(i) + primRes } - } ensuring (res => res.smallerIndex(i) && time <= ?) + } ensuring (res => res.smallerIndex(i) && time <= ?) // time <= 26 @invisibleBody @memoize @@ -106,23 +113,24 @@ object PackratParsing { if (i > 0) Parsed(i - 1) // Rule1: Prim <- Digit else - Parsed(-1) // here, we can use -1 to convery that the suffix is empty + Parsed(-1) // here, we can use -1 to convey that the suffix is empty } else if (char == Open() && i > 0) { pAdd(i - 1) match { // Rule 2: pPrim <- ( Add ) case Parsed(rem) => - Parsed(rem) + if (rem >= 0 && lookup(rem) == Close()) Parsed(rem - 1) + else NoParse() case _ => NoParse() } } else NoParse() - } ensuring (res => res.smallerIndex(i) && time <= ?) + } ensuring (res => res.smallerIndex(i) && time <= ?) // time <= 28 //@inline - def depsEval(i: BigInt) = i == 0 || (i > 0 && allEval(i-1)) + def depsEval(i: BigInt) = i == 0 || (i > 0 && allEval(i - 1)) def allEval(i: BigInt): Boolean = { require(i >= 0) - (pPrim(i).isCached && pMul(i).isCached && pAdd(i).isCached) &&( + (pPrim(i).isCached && pMul(i).isCached && pAdd(i).isCached) && ( if (i == 0) true else allEval(i - 1)) } @@ -152,17 +160,51 @@ object PackratParsing { }) } - @invisibleBody + /*@invisibleBody def invoke(i: BigInt): (Result, Result, Result) = { - require(i == 0 || (i > 0 && allEval(i-1))) + require(i == 0 || (i > 0 && allEval(i - 1))) (pPrim(i), pMul(i), pAdd(i)) } ensuring (res => { - val in = Mem.inState[Result] - val out = Mem.outState[Result] - (if(i >0) evalMono(i-1, in, out) else true) && - allEval(i) && - time <= ? - }) + val in = inState[Result] + val out = outState[Result] + (if (i > 0) evalMono(i - 1, in, out) else true) && + allEval(i) && + time <= ? + })*/ + + def invokePrim(i: BigInt): Result = { + require(depsEval(i)) + pPrim(i) + } ensuring { res => + val in = inState[Result] + val out = outState[Result] + (if (i > 0) evalMono(i - 1, in, out) else true) + } + + def invokeMul(i: BigInt): Result = { + require(depsEval(i)) + invokePrim(i) match { + case _ => pMul(i) + } + } ensuring { res => + val in = inState[Result] + val out = outState[Result] + (if (i > 0) evalMono(i - 1, in, out) else true) + } + + @invisibleBody + def invoke(i: BigInt): Result = { + require(depsEval(i)) + invokeMul(i) match { + case _ => pAdd(i) + } + } ensuring { res => + val in = inState[Result] + val out = outState[Result] + (if (i > 0) evalMono(i - 1, in, out) else true) && + allEval(i) && + time <= ? // 189 + } /** * Parsing a string of length 'n+1'. @@ -172,11 +214,34 @@ object PackratParsing { @invisibleBody def parse(n: BigInt): Result = { require(n >= 0) - if(n == 0) invoke(n)._3 + if (n == 0) invoke(n) else { - val tailres = parse(n-1) // we parse the prefixes ending at 0, 1, 2, 3, ..., n - invoke(n)._3 + parse(n - 1) match { // we parse the prefixes ending at 0, 1, 2, 3, ..., n + case _ => + invoke(n) + } } - } ensuring(_ => allEval(n) && - time <= ? * n + ?) + } ensuring (_ => allEval(n) && + time <= ? * n + ?) // 198 * n + 192 + + @ignore + def main(args: Array[String]) { + // note: we can run only one test in each run as the cache needs to be cleared between the tests, + // which is not currently supported by the api's + test1() + //test2() + } + + @ignore + def test1() { + // list of tokens to parse. The list is reversed i.e, the first char is at the last index, the last char is at the first index. + string = Array(Plus(), Digit(), Times(), Close(), Digit(), Plus(), Digit(), Open()) // d * ( d + d ) + + println("Parsing Expression 1: " + parse(string.length - 1)) + } + + @ignore + def test2() { + string = Array(Times(), Digit(), Open()) // ( d * + println("Parsing Expression 2: " + parse(string.length - 1)) + } } diff --git a/testcases/lazy-datastructures/withOrb/RealTimeQueue.scala b/testcases/lazy-datastructures/withOrb/RealTimeQueue.scala index f0a78b4f2330e70be9285407c1265ccb00b4abb2..3eda466a8b6c3885d3c9ca34a3ebd15a58af3b83 100644 --- a/testcases/lazy-datastructures/withOrb/RealTimeQueue.scala +++ b/testcases/lazy-datastructures/withOrb/RealTimeQueue.scala @@ -1,12 +1,12 @@ -package orb +package withOrb -import leon.lazyeval._ -import leon.lazyeval.$._ -import leon.lang._ -import leon.annotation._ -import leon.collection._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import lazyeval._ +import lang._ +import annotation._ +import collection._ +import instrumentation._ +import invariant._ object RealTimeQueue { @@ -32,10 +32,10 @@ object RealTimeQueue { } } ensuring (_ >= 0) } - case class SCons[T](x: T, tail: $[Stream[T]]) extends Stream[T] + case class SCons[T](x: T, tail: Lazy[Stream[T]]) extends Stream[T] case class SNil[T]() extends Stream[T] - def isConcrete[T](l: $[Stream[T]]): Boolean = { + def isConcrete[T](l: Lazy[Stream[T]]): Boolean = { l.isEvaluated && (l* match { case SCons(_, tail) => isConcrete(tail) @@ -45,7 +45,7 @@ object RealTimeQueue { @invisibleBody @invstate - def rotate[T](f: $[Stream[T]], r: List[T], a: $[Stream[T]]): Stream[T] = { // doesn't change state + def rotate[T](f: Lazy[Stream[T]], r: List[T], a: Lazy[Stream[T]]): Stream[T] = { // doesn't change state require(r.size == (f*).size + 1 && isConcrete(f)) (f.value, r) match { case (SNil(), Cons(y, _)) => //in this case 'y' is the only element in 'r' @@ -56,12 +56,12 @@ object RealTimeQueue { SCons[T](x, rot) } } ensuring (res => res.size == (f*).size + r.size + (a*).size && res.isCons && - time <= ?) + time <= ?) /** * Returns the first element of the stream that is not evaluated. */ - def firstUnevaluated[T](l: $[Stream[T]]): $[Stream[T]] = { + def firstUnevaluated[T](l: Lazy[Stream[T]]): Lazy[Stream[T]] = { if (l.isEvaluated) { l* match { case SCons(_, tail) => @@ -77,7 +77,7 @@ object RealTimeQueue { case _ => true })) - case class Queue[T](f: $[Stream[T]], r: List[T], s: $[Stream[T]]) { + case class Queue[T](f: Lazy[Stream[T]], r: List[T], s: Lazy[Stream[T]]) { def isEmpty = (f*).isEmpty def valid = { (firstUnevaluated(f) == firstUnevaluated(s)) && @@ -86,7 +86,7 @@ object RealTimeQueue { } @inline - def createQ[T](f: $[Stream[T]], r: List[T], s: $[Stream[T]]) = { + def createQ[T](f: Lazy[Stream[T]], r: List[T], s: Lazy[Stream[T]]) = { s.value match { case SCons(_, tail) => Queue(f, r, tail) case SNil() => @@ -96,6 +96,18 @@ object RealTimeQueue { } } + def empty[T] = { + val a: Stream[T] = SNil() + Queue(a, Nil(), a) + } + + def head[T](q: Queue[T]): T = { + require(!q.isEmpty && q.valid) + q.f.value match { + case SCons(x, _) => x + } + } //ensuring (res => res.valid && time <= ?) + def enqueue[T](x: T, q: Queue[T]): Queue[T] = { require(q.valid) createQ(q.f, Cons(x, q.r), q.s) @@ -108,4 +120,57 @@ object RealTimeQueue { createQ(nf, q.r, q.s) } } ensuring (res => res.valid && time <= ?) + + @ignore + def main(args: Array[String]) { + //import eagerEval.AmortizedQueue + import scala.util.Random + import scala.math.BigInt + import stats._ + import collection._ + + println("Running RTQ test...") + val ops = 10000000 + val rand = Random + // initialize to a queue with one element (required to satisfy preconditions of dequeue and front) + var rtq = empty[BigInt] + //var amq = AmortizedQueue.Queue(AmortizedQueue.Nil(), AmortizedQueue.Nil()) + var totalTime1 = 0L + var totalTime2 = 0L + println(s"Testing amortized emphemeral behavior on $ops operations...") + for (i <- 0 until ops) { + if (!rtq.isEmpty) { + val h1 = head(rtq) + //val h2 = amq.head + //assert(h1 == h2, s"Eager head: $h2 Lazy head: $h1") + } + rand.nextInt(2) match { + case x if x == 0 => //enqueue + // /if(i%100000 == 0) println("Enqueue..") + rtq = timed { enqueue(BigInt(i), rtq) } { totalTime1 += _ } + //amq = timed { amq.enqueue(BigInt(i)) } { totalTime2 += _ } + case x if x == 1 => //dequeue + if (!rtq.isEmpty) { + //if(i%100000 == 0) println("Dequeue..") + rtq = timed { dequeue(rtq) } { totalTime1 += _ } + //amq = timed { amq.dequeue } { totalTime2 += _ } + } + } + } + println(s"Ephemeral Amortized Time - Eager: ${totalTime2 / 1000.0}s Lazy: ${totalTime1 / 1000.0}s") // this should be linear in length for both cases + // now, test worst-case behavior (in persitent mode if necessary) + val length = (1 << 22) - 2 // a number of the form: 2^{n-2} + // reset the queues + rtq = empty[BigInt] + //amq = AmortizedQueue.Queue(AmortizedQueue.Nil(), AmortizedQueue.Nil()) + // enqueue length elements + for (i <- 0 until length) { + rtq = enqueue(BigInt(0), rtq) + //amq = amq.enqueue(BigInt(0)) + } + //println(s"Amortized queue size: ${amq.front.size}, ${amq.rear.size}") + //dequeue 1 element from both queues + //timed { amq.dequeue } { t => println(s"Time to dequeue one element from Amortized Queue in the worst case: ${t / 1000.0}s") } + timed { dequeue(rtq) } { t => println(s"Time to dequeue one element from RTQ in the worst case: ${t / 1000.0}s") } + } } diff --git a/testcases/lazy-datastructures/withOrb/SortingnConcat-orb.scala b/testcases/lazy-datastructures/withOrb/SortingnConcat.scala similarity index 72% rename from testcases/lazy-datastructures/withOrb/SortingnConcat-orb.scala rename to testcases/lazy-datastructures/withOrb/SortingnConcat.scala index 76f6bae6bba6e3d7f24a5741cbbd9c638642e29d..0f4c1151b72c4559be6a11c29aa7d854d881a28c 100644 --- a/testcases/lazy-datastructures/withOrb/SortingnConcat-orb.scala +++ b/testcases/lazy-datastructures/withOrb/SortingnConcat.scala @@ -1,10 +1,11 @@ package withOrb -import leon.lazyeval._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import lazyeval._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ object SortingnConcat { @@ -17,9 +18,9 @@ object SortingnConcat { } } ensuring (_ >= 0) } - case class SCons(x: BigInt, tail: $[LList]) extends LList + case class SCons(x: BigInt, tail: Lazy[LList]) extends LList case class SNil() extends LList - def ssize(l: $[LList]): BigInt = (l*).size + def ssize(l: Lazy[LList]): BigInt = (l*).size sealed abstract class List { def size: BigInt = this match { @@ -60,18 +61,7 @@ object SortingnConcat { } } ensuring(res => time <= ?) - def secondMin(l: $[LList]) : BigInt = { - l.value match { - case SCons(x, xs) => - xs.value match { - case SCons(y, ys) => y - case SNil() => x - } - case SNil() => BigInt(0) - } - } ensuring (_ => time <= ? * ssize(l) + ?) - -/* def kthMin(l: $[LList], k: BigInt): BigInt = { + def kthMin(l: Lazy[LList], k: BigInt): BigInt = { require(k >= 1) l.value match { case SCons(x, xs) => @@ -80,5 +70,5 @@ object SortingnConcat { kthMin(xs, k - 1) case SNil() => BigInt(0) } - } ensuring (_ => time <= ? * k * ssize(l) + ? * k + ?)*/ + } ensuring (_ => time <= ? * (k * ssize(l)) + ? * k + ?) } diff --git a/testcases/lazy-datastructures/withOrb/WeightedScheduling.scala b/testcases/lazy-datastructures/withOrb/WeightedScheduling.scala index 74281ac66511eca27c394a62e4a7e5d88e3a3964..fc254279b27b8e7c0596deb1a4a3c276341a9333 100644 --- a/testcases/lazy-datastructures/withOrb/WeightedScheduling.scala +++ b/testcases/lazy-datastructures/withOrb/WeightedScheduling.scala @@ -1,20 +1,20 @@ package orb -import leon.lazyeval._ -import leon.lazyeval.Mem._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -import leon.invariant._ +import leon._ +import mem._ +import lang._ +import annotation._ +import instrumentation._ +import invariant._ object WeightedSched { sealed abstract class IList { def size: BigInt = { this match { case Cons(_, tail) => 1 + tail.size - case Nil() => BigInt(0) + case Nil() => BigInt(0) } - } ensuring(_ >= 0) + } ensuring (_ >= 0) } case class Cons(x: BigInt, tail: IList) extends IList case class Nil() extends IList @@ -23,12 +23,14 @@ object WeightedSched { * array of jobs * (a) each job has a start time, finish time, and weight * (b) Jobs are sorted in ascending order of finish times + * The first job should be (0,0,0) so that it acts as sentinel of every other job */ @ignore var jobs = Array[(BigInt, BigInt, BigInt)]() /** * A precomputed mapping from each job i to the previous job j it is compatible with. + * The value of the first index could be anything. */ @ignore var p = Array[Int]() @@ -36,17 +38,17 @@ object WeightedSched { @extern def jobInfo(i: BigInt) = { jobs(i.toInt) - } ensuring(_ => time <= 1) + } ensuring (_ => time <= 1) @extern def prevCompatibleJob(i: BigInt) = { BigInt(p(i.toInt)) - } ensuring(res => res >=0 && res < i && time <= 1) + } ensuring (res => res >= 0 && res < i && time <= 1) @inline def max(x: BigInt, y: BigInt) = if (x >= y) x else y - def depsEval(i: BigInt) = i == 0 || (i > 0 && allEval(i-1)) + def depsEval(i: BigInt) = i == 0 || (i > 0 && allEval(i - 1)) def allEval(i: BigInt): Boolean = { require(i >= 0) @@ -62,7 +64,7 @@ object WeightedSched { } holds @traceInduct - def evalLem(x: BigInt, y: BigInt) = { + def evalLem(x: BigInt, y: BigInt) = { require(x >= 0 && y >= 0) (x <= y && allEval(y)) ==> allEval(x) } holds @@ -72,9 +74,9 @@ object WeightedSched { @memoize def sched(jobIndex: BigInt): BigInt = { require(depsEval(jobIndex) && - (jobIndex == 0 || evalLem(prevCompatibleJob(jobIndex), jobIndex-1))) + (jobIndex == 0 || evalLem(prevCompatibleJob(jobIndex), jobIndex - 1))) val (st, fn, w) = jobInfo(jobIndex) - if(jobIndex == 0) w + if (jobIndex == 0) w else { // we may either include the head job or not: // if we include the head job, we have to skip every job that overlaps with it @@ -82,28 +84,50 @@ object WeightedSched { val prevCompatVal = sched(prevCompatibleJob(jobIndex)) max(w + prevCompatVal, tailValue) } - } ensuring(_ => time <= ?) + } ensuring (_ => time <= ?) @invisibleBody def invoke(jobIndex: BigInt) = { require(depsEval(jobIndex)) sched(jobIndex) } ensuring (res => { - val in = Mem.inState[BigInt] - val out = Mem.outState[BigInt] - (jobIndex == 0 || evalMono(jobIndex-1, in, out)) && + val in = inState[BigInt] + val out = outState[BigInt] + (jobIndex == 0 || evalMono(jobIndex - 1, in, out)) && time <= ? }) @invisibleBody def schedBU(jobi: BigInt): IList = { require(jobi >= 0) - if(jobi == 0) { - Cons(invoke(jobi), Nil()) + if (jobi == 0) { + Cons(invoke(jobi), Nil()) } else { - val tailRes = schedBU(jobi-1) + val tailRes = schedBU(jobi - 1) Cons(invoke(jobi), tailRes) } - } ensuring(_ => allEval(jobi) && - time <= ? * (jobi + 1)) + } ensuring (_ => allEval(jobi) && + time <= ? * (jobi + 1)) + + @ignore + def main(args: Array[String]) { + // note: we can run only one test in each run as the cache needs to be cleared between the tests, + // which is not currently supported by the api's (note: the methods access a mutable field) + test1() + //test2() + } + + @ignore + def test1() { + jobs = Array((0, 0, 0), (1, 2, 5), (3, 4, 2), (3, 8, 5), (6, 7, 10), (8, 11, 11), (10, 13, 20)) + p = Array(0 /* anything */ , 0, 1, 1, 2, 4, 4) + println("Schedule for 5 jobs of set 1: " + schedBU(6)) + } + + @ignore + def test2() { + jobs = Array((0, 0, 0), (1, 3, 5), (3, 4, 2), (3, 8, 5), (6, 7, 10), (8, 11, 11), (10, 13, 20)) + p = Array(0 /* anything */ , 0, 0, 0, 2, 4, 4) + println("Schedule for 5 jobs of set 2: " + schedBU(6)) + } } diff --git a/testcases/lazy-datastructures/BottomUpMegeSort.scala b/testcases/lazy-datastructures/withconst/BottomUpMegeSort.scala similarity index 92% rename from testcases/lazy-datastructures/BottomUpMegeSort.scala rename to testcases/lazy-datastructures/withconst/BottomUpMegeSort.scala index 255da6e09da79b1c05db01e37becdaae70f9a754..1f4672599a487d19f80b0ebae1f5382ebf53f30c 100644 --- a/testcases/lazy-datastructures/BottomUpMegeSort.scala +++ b/testcases/lazy-datastructures/withconst/BottomUpMegeSort.scala @@ -1,11 +1,11 @@ package lazybenchmarks -import leon.lazyeval._ -import leon.lazyeval.$._ -import leon.lang._ -import leon.annotation._ -import leon.instrumentation._ -//import leon.invariant._ +import leon._ +import lazyeval._ +import lang._ +import annotation._ +import instrumentation._ +//import invariant._ /** * TODO Multiple instantiations of type parameters is not supported yet, @@ -43,9 +43,9 @@ object BottomUpMergeSort { } } ensuring (_ >= 0) } - case class SCons(x: BigInt, tail: $[IStream]) extends IStream + case class SCons(x: BigInt, tail: Lazy[IStream]) extends IStream case class SNil() extends IStream - def ssize(l: $[IStream]): BigInt = (l*).size + def ssize(l: Lazy[IStream]): BigInt = (l*).size /** * A list of suspensions @@ -72,7 +72,7 @@ object BottomUpMergeSort { } } ensuring (_ >= 0) } - case class LCons(x: $[IStream], tail: LList) extends LList + case class LCons(x: Lazy[IStream], tail: LList) extends LList case class LNil() extends LList /** @@ -120,7 +120,8 @@ object BottomUpMergeSort { * Note: the sorted stream of integers may by recursively constructed using merge. * Takes time linear in the size of the streams (non-trivial to prove due to cascading of lazy calls) */ - def merge(a: $[IStream], b: $[IStream]): IStream = { + @usePost + def merge(a: Lazy[IStream], b: Lazy[IStream]): IStream = { require(((a*) != SNil() || b.isEvaluated) && // if one of the arguments is Nil then the other is evaluated ((b*) != SNil() || a.isEvaluated) && ((a*) != SNil() || (b*) != SNil())) // at least one of the arguments is not Nil diff --git a/testcases/lazy-datastructures/Deque.scala b/testcases/lazy-datastructures/withconst/Deque.scala similarity index 100% rename from testcases/lazy-datastructures/Deque.scala rename to testcases/lazy-datastructures/withconst/Deque.scala diff --git a/testcases/lazy-datastructures/Esop15Benchmarks.scala b/testcases/lazy-datastructures/withconst/Esop15Benchmarks.scala similarity index 100% rename from testcases/lazy-datastructures/Esop15Benchmarks.scala rename to testcases/lazy-datastructures/withconst/Esop15Benchmarks.scala diff --git a/testcases/lazy-datastructures/LazyMegeSort.scala b/testcases/lazy-datastructures/withconst/LazyMegeSort.scala similarity index 100% rename from testcases/lazy-datastructures/LazyMegeSort.scala rename to testcases/lazy-datastructures/withconst/LazyMegeSort.scala diff --git a/testcases/lazy-datastructures/LazyNumericalRep.scala b/testcases/lazy-datastructures/withconst/LazyNumericalRep.scala similarity index 100% rename from testcases/lazy-datastructures/LazyNumericalRep.scala rename to testcases/lazy-datastructures/withconst/LazyNumericalRep.scala diff --git a/testcases/lazy-datastructures/RealTimeQueue.scala b/testcases/lazy-datastructures/withconst/RealTimeQueue.scala similarity index 100% rename from testcases/lazy-datastructures/RealTimeQueue.scala rename to testcases/lazy-datastructures/withconst/RealTimeQueue.scala diff --git a/testcases/lazy-datastructures/SortingnConcat.scala b/testcases/lazy-datastructures/withconst/SortingnConcat.scala similarity index 100% rename from testcases/lazy-datastructures/SortingnConcat.scala rename to testcases/lazy-datastructures/withconst/SortingnConcat.scala diff --git a/testcases/orb-testcases/amortized/BigNums.scala b/testcases/orb-testcases/amortized/BigNums.scala index 883d1f45ab8752668070699c840edd7a3de160b9..e4860ff5af743e01eb6530018bf93bde5ca53932 100644 --- a/testcases/orb-testcases/amortized/BigNums.scala +++ b/testcases/orb-testcases/amortized/BigNums.scala @@ -1,37 +1,66 @@ -import leon.invariant._ -import leon.instrumentation._ +package orb + +import leon._ +import annotation._ +import invariant._ +import instrumentation._ object BigNums { + sealed abstract class Digit + case class Zero() extends Digit { + @ignore + override def toString = "0" + } + case class One() extends Digit { + @ignore + override def toString = "1" + } + sealed abstract class BigNum - case class Cons(head: BigInt, tail: BigNum) extends BigNum + case class Cons(head: Digit, tail: BigNum) extends BigNum case class Nil() extends BigNum - def incrTime(l: BigNum) : BigInt = { + /** + * Time taken by the increment method + * The number of leading one's + */ + def leadingOnes(l: BigNum) : BigInt = { l match { case Nil() => 1 - case Cons(x, tail) => - if(x == 0) 1 - else 1 + incrTime(tail) + case Cons(Zero(), tail) => 1 + case Cons(_, tail) => 1 + leadingOnes(tail) } } - def potentialIncr(l: BigNum) : BigInt = { + /** + * Total number of one's in the number + */ + def numOnes(l: BigNum) : BigInt = { l match { case Nil() => 0 - case Cons(x, tail) => - if(x == 0) potentialIncr(tail) - else 1 + potentialIncr(tail) + case Cons(Zero(), tail) => numOnes(tail) + case Cons(_, tail) => 1 + numOnes(tail) } } - + + /** + * Increments a number + */ def increment(l: BigNum) : BigNum = { l match { - case Nil() => Cons(1,l) - case Cons(x, tail) => - if(x == 0) Cons(1, tail) - else Cons(0, increment(tail)) + case Nil() => Cons(One(), l) + case Cons(Zero(), tail) => Cons(One(), tail) + case Cons(_, tail) => + Cons(Zero(), increment(tail)) } - } ensuring (res => time <= ? * incrTime(l) + ? && incrTime(l) + potentialIncr(res) - potentialIncr(l) <= ?) + } ensuring (res => time <= ? * leadingOnes(l) + ? && leadingOnes(l) + numOnes(res) - numOnes(l) <= ?) + + def firstDigit(l: BigNum): Digit = { + require(l != Nil()) + l match { + case Cons(d, _) => d + } + } /** * Nop is the number of operations @@ -41,7 +70,7 @@ object BigNums { else { incrUntil(nop-1, increment(l)) } - } ensuring (res => time <= ? * nop + ? * potentialIncr(l) + ?) + } ensuring (res => time <= ? * nop + ? * numOnes(l) + ?) def count(nop: BigInt) : BigNum = { incrUntil(nop, Nil()) diff --git a/testcases/orb-testcases/timing/AmortizedQueue.scala b/testcases/orb-testcases/timing/AmortizedQueue.scala index 23b5c35726d4e40f152e5d80878a96952266cbfb..8aece6a669c4fe6dd51d764efb40529731666a66 100644 --- a/testcases/orb-testcases/timing/AmortizedQueue.scala +++ b/testcases/orb-testcases/timing/AmortizedQueue.scala @@ -1,88 +1,83 @@ -import leon.invariant._ -import leon.instrumentation._ +package orb -object AmortizedQueue { - sealed abstract class List - case class Cons(head : BigInt, tail : List) extends List - case class Nil() extends List - - case class Queue(front : List, rear : List) +import leon._ +import invariant._ +import instrumentation._ - def size(list : List) : BigInt = (list match { - case Nil() => 0 - case Cons(_, xs) => 1 + size(xs) - }) - - def sizeList(list: List): BigInt = { - list match { - case Nil() => BigInt(0) - case Cons(_, xs) => 1 + sizeList(xs) +object AmortizedQueue { + sealed abstract class List { + val size: BigInt = this match { + case Nil() => 0 + case Cons(_, xs) => 1 + xs.size } - }ensuring((res : BigInt) => res >= 0 && tmpl((a, b) => time <= a * size(list) + b)) - - def qsize(q : Queue) : BigInt = size(q.front) + size(q.rear) - - def asList(q : Queue) : List = concat(q.front, reverse(q.rear)) - - def concat(l1 : List, l2 : List) : List = (l1 match { - case Nil() => l2 - case Cons(x,xs) => Cons(x, concat(xs, l2)) + } + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List - }) ensuring (res => size(res) == size(l1) + size(l2) && tmpl((a,b,c) => time <= a*size(l1) + b)) + def concat(l1: List, l2: List): List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => Cons(x, concat(xs, l2)) - def isAmortized(q : Queue) : Boolean = sizeList(q.front) >= sizeList(q.rear) - - def isEmpty(queue : Queue) : Boolean = queue match { - case Queue(Nil(), Nil()) => true - case _ => false - } + }) ensuring (res => res.size == l1.size + l2.size && time <= ? * l1.size + ?) def reverseRec(l1: List, l2: List): List = (l1 match { - case Nil() => l2 + case Nil() => l2 case Cons(x, xs) => reverseRec(xs, Cons(x, l2)) + }) ensuring (res => l1.size + l2.size == res.size && time <= ? * l1.size + ?) - }) ensuring (res => size(l1) + size(l2) == size(res) && tmpl((a,b) => time <= a*size(l1) + b)) - - def reverse(l: List): List = { + def listRev(l: List): List = { reverseRec(l, Nil()) - } ensuring (res => size(l) == size(res) && tmpl((a,b) => time <= a*size(l) + b)) + } ensuring (res => l.size == res.size && time <= ? * l.size + ?) - def amortizedQueue(front : List, rear : List) : Queue = { - if (sizeList(rear) <= sizeList(front)) - Queue(front, rear) - else - Queue(concat(front, reverse(rear)), Nil()) - } + def removeLast(l: List): List = { + require(l != Nil()) + l match { + case Cons(x, Nil()) => Nil() + case Cons(x, xs) => Cons(x, removeLast(xs)) + case _ => Nil() + } + } ensuring (res => res.size <= l.size && tmpl((a, b) => time <= a * l.size + b)) - def enqueue(q : Queue, elem : BigInt) : Queue = ({ + case class Queue(front: List, rear: List) { + def qsize: BigInt = front.size + rear.size - amortizedQueue(q.front, Cons(elem, q.rear)) + def asList: List = concat(front, listRev(rear)) - }) ensuring(res => true && tmpl((a,b) => time <= a*qsize(q) + b)) + def isAmortized: Boolean = front.size >= rear.size - def dequeue(q : Queue) : Queue = { - require(isAmortized(q) && !isEmpty(q)) - q match { - case Queue(Cons(f, fs), rear) => amortizedQueue(fs, rear) - case _ => Queue(Nil(),Nil()) + def isEmpty: Boolean = this match { + case Queue(Nil(), Nil()) => true + case _ => false } - } ensuring(res => true && tmpl((a,b) => time <= a*qsize(q) + b)) - def removeLast(l : List) : List = { - require(l != Nil()) - l match { - case Cons(x,Nil()) => Nil() - case Cons(x,xs) => Cons(x, removeLast(xs)) - case _ => Nil() + def amortizedQueue(front: List, rear: List): Queue = { + if (rear.size <= front.size) + Queue(front, rear) + else + Queue(concat(front, listRev(rear)), Nil()) } - } ensuring(res => size(res) <= size(l) && tmpl((a,b) => time <= a*size(l) + b)) - def pop(q : Queue) : Queue = { - require(isAmortized(q) && !isEmpty(q)) - q match { - case Queue(front, Cons(r,rs)) => Queue(front, rs) - case Queue(front, rear) => Queue(removeLast(front), rear) - case _ => Queue(Nil(),Nil()) + def enqueue(elem: BigInt): Queue = ({ + amortizedQueue(front, Cons(elem, rear)) + }) ensuring (_ => time <= ? * qsize + ?) + + def dequeue: Queue = { + require(isAmortized && !isEmpty) + front match { + case Cons(f, fs) => amortizedQueue(fs, rear) + case _ => Queue(Nil(), Nil()) + } + } ensuring (_ => time <= ? * qsize + ?) + + def head: BigInt = { + require(isAmortized && !isEmpty) + front match { + case Cons(f, _) => f + } } - } ensuring(res => true && tmpl((a,b) => time <= a*size(q.front) + b)) + + def reverse: Queue = { // this lets the queue perform deque operation (but they no longer have efficient constant time amortized bounds) + amortizedQueue(rear, front) + } + } } diff --git a/testcases/verification/list-algorithms/BasicMergeSort.scala b/testcases/verification/list-algorithms/BasicMergeSort.scala index 55e632ff8f68307ba24cff691bde6b45fa206842..67fadf7dd7e7569107c4a6802b849f7844b66bd1 100644 --- a/testcases/verification/list-algorithms/BasicMergeSort.scala +++ b/testcases/verification/list-algorithms/BasicMergeSort.scala @@ -1,12 +1,16 @@ -import leon.lang._ -import leon.collection._ +package listalgorithms + +import leon._ +import lang._ +import collection._ + object MergeSort { def merge[T](less: (T, T) => Boolean)(xs: List[T], ys: List[T]): List[T] = { (xs, ys) match { case (Nil(), _) => ys case (_, Nil()) => xs - case (Cons(x,xtail), Cons(y,ytail)) => + case (Cons(x, xtail), Cons(y, ytail)) => if (less(x, y)) x :: merge(less)(xtail, ys) else @@ -15,12 +19,23 @@ object MergeSort { } ensuring { res => res.content == xs.content ++ ys.content && res.size == xs.size + ys.size } + def split[T](list: List[T]): (List[T], List[T]) = { + list match { + case Nil() => (Nil(), Nil()) + case Cons(x, Nil()) => (Cons(x, Nil()), Nil()) + case Cons(x1, Cons(x2, xs)) => + val (s1, s2) = split(xs) + (Cons(x1, s1), Cons(x2, s2)) + } + } + def msort[T](less: (T, T) => Boolean)(l: List[T]): List[T] = { - if (l.size <= 1) l - else { - val c = l.length/2 - val (first, second) = l.splitAtIndex(c) // evenSplit - merge(less)(msort(less)(first), msort(less)(second)) + l match { + case Nil() => Nil[T]() + case Cons(x, Nil()) => Cons(x, Nil()) + case _ => + val (first, second) = split(l) + merge(less)(msort(less)(first), msort(less)(second)) } } ensuring { res => res.content == l.content && res.size == l.size } } diff --git a/testcases/verification/list-algorithms/MergeSort.scala b/testcases/verification/list-algorithms/MergeSort.scala index 5fc80d117c9eae3bddec01377683eac54f0c37cd..4b494b62d40b24f5ad660fbe17ea215054509168 100644 --- a/testcases/verification/list-algorithms/MergeSort.scala +++ b/testcases/verification/list-algorithms/MergeSort.scala @@ -1,5 +1,6 @@ -import leon.lang._ -import leon.annotation._ +import leon._ +import lang._ +import annotation._ object MergeSort { sealed abstract class List