def isPrime(n:Int): Boolean = {
require(n > 0)
var r = true
if (n <= 1) r = false
else {
var i:Int = 2
(while (i < (n / 2)) {
decreases(n-i)
if (n % i == 0) r = false
i += 1
}).invariant(2 <= i && i <= n)
}
r
}.ensuring((res: Boolean) => res == (n > 1 && (2 until n).forall(i => n % i != 0)))
class IsPrimeTest extends FlatSpec with Matchers {
"isPrime" should "fulfill the postcond" in {
assert(!isPrime(1))
assert(isPrime(5))
}
}
assert(!isPrime(1))
assert(!isPrime(1))
assert(!isPrime(5))
Counter example generation
[Warning ] - Result for 'body assertion' VC for isPrime @25:12:
[Warning ] n <= 0
[Warning ] SubList.scala:25:12: => INVALID
assert(n != n, "BB_1")
^^^^^^
[Warning ] Found counter-example:
[Warning ] n: Int -> 1
isPrime(1)
Ensuring Clause
def isPrime(n:Int): Boolean = {
require(n > 0)
var r = true
if (n <= 1) r = false
else {
var i:Int = 2
(while (i < (n / 2)) {
decreases(n-i)
if (n % i == 0) r = false
i += 1
}).invariant(2 <= i && i <= n)
}
r
}.ensuring((res: Boolean) => res == (n > 1 && (2 until n).forall(i => n % i != 0)))
val n = 2
assert(((res: Boolean) =>
res == (n > 1 && (2 until n).forall(i => n % i != 0)))
(isPrime(n)))
assert(false)
Program
def size(list: List): BigInt = list match{
case Cons(_,xs) => 1+size(xs)
case Nil() => 0
}
VC
size(list) <= 0
Program
def size(list: List): BigInt = list match{
case Cons(_,xs) => 1+size(xs)
case Nil() => 0
}
VC
size(list) <= 0
def isPrime(n: Int): Boolean = {
require(n > 0)
val r: Boolean = true
val t: (Unit, Boolean) = if (n <= 1) {
val r: Boolean = false
((), r)
} else {
val i: Int = 2
val t: (Unit, Int, Boolean) = if (i < n / 2) {
isPrimeWhile(n, i, r)
} else {
((), i, r)
}
val res: Unit = t._1
val r: Boolean = t._3
val i: Int = t._2
(res, r)
}
val res: Unit = t._1
val r: Boolean = t._2
val tmp: Unit = res
r
}
@derived(isPrime)
def isPrimeWhile(n: Int, i: Int, r: Boolean):
(Unit, Int, Boolean) = {
require(((n > 0)): @dropConjunct )
val r: Boolean = true
require((!(n <= 1)): @dropConjunct )
val i: Int = 2
{
require({
val t: Boolean = if (2 <= i) {
val t: Boolean = if (i <= n) {
i < n / 2
} else {
false
}
val res: Boolean = t
res
} else {
false
}
val res: Boolean = t
res
})
decreases(n - i)
val r: Boolean = r
val i: Int = i
val t: (Unit, Boolean) = if (n % i == 0) {
val r: Boolean = false
((), r)
} else {
((), r)
}
val res: Unit = t._1
val r: Boolean = t._2
val tmp: Unit = res
val i: Int = i + 1
val tmp: Unit = ()
val tmp: Unit = ()
if (i < n / 2) {
isPrimeWhile(n, i, r)
} else {
((), i, r)
}
} ensuring {
(_res: (Unit, Int, Boolean)) => {
val t: Boolean = if (2 <= _res._2) {
val t: Boolean = if (_res._2 <= n) {
!(_res._2 < n / 2)
} else {
false
}
val res: Boolean = t
res
} else {
false
}
val res: Boolean = t
res
}
}
}
/**
* Transform trees by inserting Reachability probes. s is the input tree, t is the transformed tree
*/
class ReachabilityProbeInjector(override val s: ast.Trees, override val t: ast.Trees)
(using val symbols: s.Symbols)
extends transformers.ConcreteTreeTransformer(s, t) {
override def transform(e: s.Expr): t.Expr = e match {
// TODO add at beginning of method
case s.MatchExpr(scrutine, cases) => t.MatchExpr(transform(scrutine), cases.map(casee => casee match {
case s.MatchCase(pattern, optGuard, rhs) => t.MatchCase(pattern, transform(optGuard), t.ReachabilityProbe(transform(rhs)))
}))
case s.IfExpr(cond, _then, _else) => t.IfExpr(transform(cond), t.ReachabilityProbe(transform(_then)), t.ReachabilityProbe(transform(_else)))
case _ => super.transform(e) // go through the other expressions and transform recursively
}
}
def isPrime(n: Int): Boolean = {
require(n > 0)
reachabilityProbe(n)
val r: Boolean = true
val targetBound: Boolean = n <= 1
val t: (Unit, Boolean) = if (targetBound) {
reachabilityProbe(n)
val r: Boolean = false
((), r)
} else {
val i: Int = 2
val t: (Unit, Int, Boolean) = if (i < n / 2) {
isPrimeWhile(n, i, r)
} else {
((), i, r)
}
val res: Unit = t._1
val r: Boolean = t._3
val i: Int = t._2
(res, r)
}
val res: Unit = t._1
val r: Boolean = t._2
val tmp: Unit = res
reachabilityProbe(n)
r
}
@derived(isPrime)
def isPrimeWhile(n: Int, i: Int, r: Boolean):
(Unit, Int, Boolean) = {
require(@dropConjunct @DropVCs (n > 0) &&&
@dropConjunct @DropVCs (n ≠ n))
val targetBound: Boolean = @DropVCs (n <= 1)
require(@dropConjunct @DropVCs ¬targetBound)
{
require((2 <= i && i <= n) &&& (i < n / 2))
decreases(n - i)
val r: Boolean = r
val i: Int = i
reachabilityProbe(n)
val targetBound: Boolean = n % i == 0
val t: (Unit, Boolean) = if (targetBound) {
reachabilityProbe(n)
val r: Boolean = false
((), r)
} else {
((), r)
}
val res: Unit = t._1
val r: Boolean = t._2
val tmp: Unit = res
reachabilityProbe(n)
val i: Int = i + 1
val tmp: Unit = ()
if (i < n / 2) {
isPrimeWhile(n, i, r)
} else {
((), i, r)
}
} ensuring {
(_res: (Unit, Int, Boolean)) => {
val r: Boolean = _res._3
val i: Int = _res._2
(2 <= i && i <= n) &&& ¬(i < n / 2)
}
}
}
Modify stainless to handle probes:
[EH2007] C. Engel and R. Hähnle, “Generating Unit Tests from Formal Proofs,” in Tests and Proofs, Berlin, Heidelberg, 2007, pp. 169–188. doi: 10.1007/978-3-540-73770-4_10. [Voi2019] Voirol, Nicolas Charles Yves. Verified functional programming. THESIS. EPFL, 2019.