Verifying Account Transfer in Dafny - dafny

I'm trying to verify a simple account transfer in Dafny, and this is what I came up with:
function sum(items: seq<int>): int
decreases |items| {
if items == []
then 0
else items[0] + sum(items[1..])
}
method transfer(accounts: array<int>, balance: int, from: int, to: int)
requires from >= 0 && from < accounts.Length;
requires to >= 0 && to < accounts.Length;
requires accounts.Length > 0;
requires sum(accounts[..]) == balance;
ensures sum(accounts[..]) == balance;
modifies accounts;
{
accounts[from] := accounts[from] - 1;
accounts[to] := accounts[to] + 1;
return;
}
I'm just transferring one token at a time here (though actually I want to transfer an arbitrary amount). Eitherway, it doesn't verify. I'm wondering what else I need to do?

Here is one way to do it.
The problem is that Dafny does not know anything about the sum function, so we have to teach it a few facts.
function sum(items: seq<int>): int
decreases |items| {
if items == []
then 0
else items[0] + sum(items[1..])
}
lemma sum_append(xs: seq<int>, ys: seq<int>)
ensures sum(xs + ys) == sum(xs) + sum(ys)
{
if xs == [] {
assert [] + ys == ys;
} else {
assert (xs + ys)[1..] == xs[1..] + ys;
}
}
lemma sum_focus(xs: seq<int>, i: int)
requires 0 <= i < |xs|
ensures sum(xs) == sum(xs[..i]) + xs[i] + sum(xs[i+1..])
{
calc {
sum(xs);
{ assert xs == xs[..i] + [xs[i]] + xs[i+1..]; }
sum(xs[..i] + [xs[i]] + xs[i+1..]);
{ forall xs, ys { sum_append(xs, ys); } }
sum(xs[..i]) + xs[i] + sum(xs[i+1..]);
}
}
method transfer(accounts: array<int>, balance: int, from: int, to: int)
requires from >= 0 && from < accounts.Length;
requires to >= 0 && to < accounts.Length;
requires accounts.Length > 0;
requires sum(accounts[..]) == balance;
ensures sum(accounts[..]) == balance;
modifies accounts;
{
sum_focus(accounts[..], from);
accounts[from] := accounts[from] - 1;
sum_focus(accounts[..], from);
sum_focus(accounts[..], to);
accounts[to] := accounts[to] + 1;
sum_focus(accounts[..], to);
}
The idea is that the sum_focus lemma allows us to break up the sum of an array into the sum of elements before i, plus the element at i, plus the sum of elements after i. This "focusing" step allows Dafny to reason about the assignment statements that write to the array elements accounts[from] and accounts[to].
Alternative version of sum_focus:
lemma sum_focus(xs: seq<int>, i: int)
requires 0 <= i < |xs|
ensures sum(xs) == sum(xs[..i]) + xs[i] + sum(xs[i+1..])
{
calc {
sum(xs);
{ assert xs == xs[..i] + [xs[i]] + xs[i+1..]; }
sum(xs[..i] + [xs[i]] + xs[i+1..]);
{
sum_append(xs[..i] + [xs[i]], xs[i+1..]);
sum_append(xs[..i], [xs[i]]);
}
sum(xs[..i]) + xs[i] + sum(xs[i+1..]);
}
}

Ok, based on your suggestion I came up with this:
function sum(items: seq<int>): int
decreases |items| {
if items == []
then 0
else items[0] + sum(items[1..])
}
lemma diff(xs: seq<int>, ys: seq<int>, acc: int, amt: int)
requires |xs| == |ys| && 0 <= acc < |xs|;
requires (forall k:int :: (0 <= k < |xs|) ==> (k == acc || xs[k] == ys[k]));
requires xs[acc] == ys[acc] + amt;
ensures sum(xs) == sum(ys) + amt;
{
if acc == 0 {
assert xs[1..] == ys[1..];
} else {
diff(xs[1..],ys[1..],acc-1,amt);
}
}
method transfer(accounts: array<int>, balance: int, from: int, to: int)
requires from >= 0 && from < accounts.Length;
requires to >= 0 && to < accounts.Length;
requires accounts.Length > 0;
requires sum(accounts[..]) == balance;
ensures sum(accounts[..]) == balance;
modifies accounts;
{
var 'accounts : seq<int> := accounts[..];
accounts[from] := accounts[from] - 1;
var ''accounts : seq<int> := accounts[..];
diff(accounts[..],'accounts,from,-1);
accounts[to] := accounts[to] + 1;
diff(accounts[..],''accounts,to,1);
return;
}
That seems to go through. Its a bit clunky though --- I prefer your way.

Related

Dafny cannot prove that 'a^2<=b^2*c^2' implies 'a<=b*c' for reals greater than 0

I want to test the following property in Dafny: a^2<=b^2*c^2 implies a<=b*c. It automatically checks this for (positive) integer numbers:
lemma powersVSsquares_integers(a:int, b:int, c:int)
requires a>=0 && b>=0 && c>= 0
ensures (a*a <= b*b*c*c) ==> (a<=b*c)
{}
Now, I want to test this for real numbers, so I try:
lemma powersVSsquares_reals(a:real, b:real, c:real)
requires a>=0.0 && b>=0.0 && c>=0.0
ensures (a*a <= b*b*c*c) ==> (a<=b*c)
{}
But Dafny cannot prove this. Thus, I start with the case where a>=1.0 & b>=1.0 & c>=1.0:
lemma powersVSsquares_reals1(a:real, b:real, c:real)
requires (a>=1.0 && b>=1.0 && c>=1.0)
ensures (a*a <= b*b*c*c) ==> (a<=b*c)
{
assert a <= (b*b*c*c)/a;
}
But not even assert a <= (b*b*c*c)/a; can be proven. So I have no idea on how to deal with this prove.
Can anyone provide lemma powersVSsquares_reals proven in Dafny? Can this be done without defining a symbolic square root function?
I think you will need some axioms of the reals that are not readily available in Dafny.
lemma increase(a: real, b: real, x: real)
requires x > 0.0
requires a > b
ensures a * x > b * x
{
}
lemma zero(a: real)
requires a*a == 0.0
ensures a == 0.0
{
if a != 0.0 {
if a > 0.0 {
increase(a, 0.0, a);
assert false;
} else if a < 0.0 {
increase(-a, 0.0, -a);
assert false;
}
}
}
Then you can use these axioms to do a regular proof. Here I choose the style of a proof by contradiction
lemma powersVSsquares_reals(a:real, b:real, c:real)
requires a>=0.0 && b>=0.0 && c>=0.0
ensures (a*a <= (b*b)*(c*c)) ==> (a<=b*c)
{
if a*a <= (b*b)*(c*c) {
if a == 0.0 {
return;
}
if b == 0.0 || c == 0.0 {
zero(a);
return;
}
assert a*a <= (b*c)*(b*c);
if a > (b*c) {
assert b * c > 0.0;
increase(a, b*c, b*c);
assert a * (b*c) > (b * c) * (b*c);
assert a > 0.0;
increase(a, b*c, a);
assert a * a > (b * c) * a;
assert a * a > (b * c) * (b*c);
assert false;
}
assert a<=b*c;
}
}

Proving a covariance inequality in Dafny, use contradiction?

I am trying to prove the following property in Dafny: covariance(x,y) <= variance(x) * variance(y), where x and y are real.
First of all, I would like to know if I am interpreting covariance and variance correctly. Thus, I make the following question: is it the same proving covariance(x,y) <= variance(x) * variance(y) and proving covariance(x,y) <= covariance(x,x) * covariance(y,y)? I make this question because the variance usually appears to the power of 2 (i.e., sigma^2(x)), which confuses me.
However, I understand from https://en.wikipedia.org/wiki/Covariance that this equivalence holds. Concretely, the following is stated: The variance is a special case of the covariance in which the two variables are identical.
I also take one of the definitions of covariance from that link. Concretely: cov(x,y)=(1/n)*summation(i from 1 to n){(x_i-mean(x))*(y_i-mean(y))}.
Thus, assuming that I must prove covariance(x,y) <= covariance(x,x) * covariance(y,y) and that covariance is defined like above, I test the following lemma in Dafny:
lemma covariance_equality (x: seq<real>, y:seq<real>)
requires |x| > 1 && |y| > 1; //my conditions
requires |x| == |y|; //my conditions
requires forall elemX :: elemX in x ==> 0.0 < elemX; //my conditions
requires forall elemY :: elemY in y ==> 0.0 < elemY; //my conditions
ensures covariance_fun (x,y) <= covariance_fun(x,x) * covariance_fun (y,y);
{}
Which cannot be proven automatically. The point is that I do not know how to prove it manually. I made this (not useful) approach of a contraction proof, but I guess this is not the way:
lemma covariance_equality (x: seq<real>, y:seq<real>)
requires |x| > 1 && |y| > 1; //my conditions
requires |x| == |y|; //my conditions
requires forall elemX :: elemX in x ==> 0.0 < elemX; //my conditions
requires forall elemY :: elemY in y ==> 0.0 < elemY; //my conditions
ensures covariance_fun (x,y) <= covariance_fun(x,x) * covariance_fun (y,y);
{
if !(covariance_fun(x,y) < covariance_fun(x,c) * covariance_fun (y,y)) {
//...
assert !(forall elemX' :: elemX' in x ==> 0.0<elemX') && (forall elemY' :: elemY' in y ==> 0.0<elemY'); //If we assume this, the proof is verified
assert false;
}
}
My main question is: how can I prove this lemma?
So that you have all the information, I offer the functions by which covariance calculation is made up. First, the covariance function is as follows:
function method covariance_fun(x:seq<real>, y:seq<real>):real
requires |x| > 1 && |y| > 1;
requires |x| == |y|;
decreases |x|,|y|;
{
summation(x,mean_fun(x),y,mean_fun(y)) / ((|x|-1) as real)
}
As you can see, it performs summation and then divides by the length. Also, note that the summation function receives mean of x and mean of y. Summation is as follows:
function method summation(x:seq<real>,x_mean:real,y:seq<real>,y_mean:real):real
requires |x| == |y|;
decreases |x|,|y|;
{
if x == [] then 0.0
else ((x[0] - x_mean)*(y[0] - y_mean))
+ summation(x[1..],x_mean,y[1..],y_mean)
}
As you can see, it recursively performs (x-mean(x))*(y-mean(y)) of each position.
As for helper functions, mean_fun calculates the mean and is as follows:
function method mean_fun(s:seq<real>):real
requires |s| >= 1;
decreases |s|;
{
sum_seq(s) / (|s| as real)
}
Note that is uses sum_seq function which, given a sequence, calculates the sum of all its positions. It is defined recursively:
function method sum_seq(s:seq<real>):real
decreases s;
{
if s == [] then 0.0 else s[0] + sum_seq(s[1..])
}
Now, with all these ingredientes, can anyone tell me (1) if I already did something wrong and/or (2) how to prove the lemma?
PS: I have been looking and this lemma seems to be the Cauchy–Schwarz inequality, but still do not know how to solve it.
Elaborating on the answer
I will (1) define a convariance function cov in terms of product and then (2) use this definition in a new lemma that should hold.
Also, note that I add the restriction that 'x' and 'y' are non-empty, but you can modify this
//calculates the mean of a sequence
function method mean_fun(s:seq<real>):real
requires |s| >= 1;
decreases |s|;
{
sum_seq(s) / (|s| as real)
}
//from x it constructs a=[x[0]-x_mean, x[1]-x_mean...]
function construct_list (x:seq<real>, m:real) : (a:seq<real>)
requires |x| >= 1
ensures |x|==|a|
{
if |x| == 1 then [x[0]-m]
else [x[0]-m] + construct_list(x[1..],m)
}
//covariance is calculated in terms of product
function cov(x: seq<real>, y: seq<real>) : real
requires |x| == |y|
requires |x| >= 1
ensures cov(x,y) == product(construct_list(x, mean_fun(x)),construct_list(y, mean_fun(y))) / (|x| as real) //if we do '(|x| as real)-1', then we can have a division by zero
//i.e., ensures cov(x,y) == product(a,b) / (|x| as real)
{
//if |a| == 1 then 0.0 //we do base case in '|a|==1' instead of '|a|==1', because we have precondition '|a| >= 1'
//else
var x_mean := mean_fun(x);
var y_mean := mean_fun(y);
var a := construct_list(x, x_mean);
var b := construct_list(y, y_mean);
product(a,b) / (|a| as real)
}
//Now, test that Cauchy holds for Cov
lemma cauchyInequality_usingCov (x: seq<real>, y: seq<real>)
requires |x| == |y|
requires |x| >= 1
requires forall i :: 0 <= i < |x| ==> x[i] >= 0.0 && y[i] >= 0.0
ensures square(cov(x,y)) <= cov(x,x)*cov(y,y)
{
CauchyInequality(x,y);
}
\\IT DOES NOT HOLD!
Proving HelperLemma (not provable?)
Trying to prove HelperLemma I find that Dafny is able to prove that LHS of a*a*x + b*b*y >= 2.0*a*b*z is positive.
Then, I started to prove different cases (I know this is not the best procedure):
(1) If one of a,b or z is negative (while the rest positive) or when the three are negative; then RHS is negative
And when RHS is negative, LHS is greater or equal (since LHS is positive).
(2) If a or b or z are 0, then RHS is 0, thus LHS is greater or equal.
(3) If a>=1, b>=1 and z>=1, I tried to prove this using sqrt_ineq().
BUT! We can find a COUNTER EXAMPLE.
With a=1, b=1, z=1, RHS is 2; now, choose x to be 0 and y to be 0 (i.e., LHS=0). Since 0<2, this is a counter example.
Am I right? If not, how can I prove this lemma?
lemma HelperLemma(a: real, x: real, b: real, y: real, z: real)
requires x >= 0.0 && y >= 0.0 //&& z >= 0.0
requires square(z) <= x * y
ensures a*a*x + b*b*y >= 2.0*a*b*z
{
assert square(z) == z*z;
assert a*a*x + b*b*y >= 0.0; //a*a*x + b*b*y is always positive
if ((a<0.0 && b>=0.0 && z>=0.0) || (a>=0.0 && b<0.0 && z>=0.0) || (a>=0.0 && b>=0.0 && z<0.0) || (a<0.0 && b<0.0 && z<0.0)) {
assert 2.0*a*b*z <=0.0; //2.0*a*b*z <= 0.0 is negative or 0 when product is negative
assert a*a*x + b*b*y >= 2.0*a*b*z; // When 2.0*a*b*z <= 0.0 is negative or 0, a*a*x + b*b*y is necessarily greater
}
if (a==0.0 || b==0.0 || z==0.0){
assert a*a*x + b*b*y >= 2.0*a*b*z;
}
else if (a>=1.0 && b>=1.0 && z>= 1.0){
assert a*a >= a;
assert b*b >= b;
assert a*a+b*b >= a*b;
assert square(z) <= x * y;
//sqrt_ineq(square(z),x*y);
//assert square(z) == z*z;
//sqrt_square(z);
//assert sqrt(z) <= sqrt(x*y);
//assert (x==0.0 || y == 0.0) ==> (x*y==0.0) ==> (square(z) <= 0.0) ==> z<=0.0;
assert a*a*x + b*b*y >= 2.0*a*b*z;
//INDEED: COUNTER EXAMPLE: a=1, b=1, z=1. Thus: RHS is 2; now, choose x to be 0 and y to be 0 (i.e., LHS=0). Since 0<2, this is a counter example.
}
}
Edited after clarification/fixing of post condition.
Here is attempt to do it in Dafny using calculation and induction.
Few comments on verification
Since task at hand is verification, I assumed sqrt function and some of its properties. HelperLemma is provable provided we assume some properties of sqrt function. There are two assumption in verification that is easy to prove.
function square(r: real) : real
ensures square(r) >= 0.0
{
r * r
}
function sqrt(r: real) : real
function {:fuel 2} product(a: seq<real>, b: seq<real>) : real
requires |a| == |b|
{
if |a| == 0 then 0.0
else a[0] * b[0] + product(a[1..], b[1..])
}
lemma sqrt_ineq(a: real, b: real)
requires a >= 0.0 && b >= 0.0
requires a <= b
ensures sqrt(a) <= sqrt(b)
lemma sqrt_square(a: real)
ensures sqrt(square(a)) == a
lemma CauchyBaseInequality(a1: real, a2: real, b1: real, b2: real)
ensures square(a1*b1 + a2*b2) <= (a1*a1 + a2*a2) * (b1*b1 + b2*b2)
{
calc <= {
square(a1*b1 + a2*b2) - (a1*a1 + a2*a2) * (b1*b1 + b2*b2);
a1*b1*a1*b1 + a2*b2*a2*b2 + 2.0*a1*b1*a2*b2 - a1*a1*b1*b1 - a1*a1*b2*b2 - a2*a2*b1*b1 - a2*a2*b2*b2;
2.0*a1*b1*a2*b2 - a1*a1*b2*b2 - a2*a2*b1*b1;
- square(a1*b2 - a2*b1);
0.0;
}
}
lemma HelperLemma(a: real, x: real, b: real, y: real, z: real)
requires x >= 0.0 && y >= 0.0
requires square(z) <= x * y
ensures a*a*x + b*b*y >= 2.0*a*b*z
lemma CauchyInequality(a: seq<real>, b: seq<real>)
requires |a| == |b|
ensures square(product(a, b)) <= product(a, a) * product(b, b)
{
if |a| <= 2 {
if |a| == 2 {
CauchyBaseInequality(a[0], a[1], b[0], b[1]);
}
}
else {
var x, xs := a[0], a[1..];
var y, ys := b[0], b[1..];
calc >= {
product(a, a) * product(b, b) - square((product(a, b)));
(x*x + product(xs, xs))*(y*y + product(ys, ys)) - square(x*y + product(xs, ys));
x*x*y*y + y*y*product(xs, xs) + x*x*product(ys, ys) + product(xs, xs)*product(ys, ys)
- x*x*y*y - square(product(xs, ys)) - 2.0*x*y*product(xs, ys);
y*y*product(xs, xs) + x*x*product(ys, ys) + product(xs, xs) * product(ys, ys)
- square(product(xs, ys)) - 2.0*x*y*product(xs, ys);
{ CauchyInequality(xs, ys); }
y*y*product(xs, xs) + x*x*product(ys, ys) - 2.0*x*y*product(xs, ys);
{
CauchyInequality(xs, ys);
assume product(xs, xs) >= 0.0;
assume product(ys, ys) >= 0.0;
HelperLemma(y, product(xs, xs), x, product(ys, ys), product(xs, ys));
}
0.0;
}
}
}
lemma cauchyInequality_usingCov (x: seq<real>, y: seq<real>)
requires |x| == |y|
requires |x| >= 1
requires forall i :: 0 <= i < |x| ==> x[i] >= 0.0 && y[i] >= 0.0
ensures square(cov(x,y)) <= cov(x,x)*cov(y,y)
{
var x_mean := mean_fun(x);
var y_mean := mean_fun(y);
var a := construct_list(x, x_mean);
var b := construct_list(y, y_mean);
assert cov(x, y) == product(a, b) / (|x| as real);
assert cov(x, x) == product(a, a) / (|x| as real);
assert cov(y, y) == product(b, b) / (|x| as real);
CauchyInequality(a, b);
}
Verification of Helper Lemma
function square(r: real): real
ensures square(r) >= 0.0
{
r * r
}
function abs(a: real) : real
ensures abs(a) >= 0.0
{
if a < 0.0 then -a else a
}
function sqrt(r: real): real
requires r >= 0.0
ensures square(sqrt(r)) == r && sqrt(r) >= 0.0
lemma sqrtIneq(a: real, b: real)
requires a >= 0.0 && b >= 0.0
requires a <= b
ensures sqrt(a) <= sqrt(b)
lemma sqrtLemma(a: real)
ensures sqrt(square(a)) == abs(a)
lemma sqrtMultiply(a: real, b: real)
requires a >= 0.0 && b >= 0.0
ensures sqrt(a*b) == sqrt(a) * sqrt(b);
lemma differenceLemma(a: real, b: real)
requires a >= abs(b)
ensures a - b >= 0.0 && a + b >= 0.0
{
}
lemma HelperLemma(a: real, x: real, b: real, y: real, z: real)
requires x >= 0.0 && y >= 0.0
requires square(z) <= x * y
ensures a*a*x + b*b*y >= 2.0*a*b*z
{
var sx := sqrt(x);
var sy := sqrt(y);
assert sx * sx == x;
assert sy * sy == y;
if (a >= 0.0 && b >= 0.0) || (a < 0.0 && b < 0.0) {
calc >= {
a*a*x + b*b*y - 2.0*a*b*z;
a*a*sx*sx + b*b*sy*sy - 2.0*a*b*sx*sy + 2.0*a*b*sx*sy - 2.0*a*b*z;
square(a*sx - b*sy) + 2.0*a*b*sx*sy - 2.0*a*b*z;
2.0*a*b*sx*sy - 2.0*a*b*z;
2.0*a*b*(sx*sy - z);
{
sqrtIneq(square(z), x * y);
assert sqrt(x * y) >= sqrt(square(z));
sqrtMultiply(x, y);
assert sx * sy >= sqrt(square(z));
sqrtLemma(z);
assert sx * sy >= abs(z);
differenceLemma(sx*sy, z);
assert sx*sy >= z;
}
0.0;
}
}
else {
calc >= {
a*a*x + b*b*y - 2.0*a*b*z;
a*a*sx*sx + b*b*sy*sy + 2.0*a*b*sx*sy - 2.0*a*b*sx*sy - 2.0*a*b*z;
square(a*sx + b*sy) - 2.0*a*b*sx*sy - 2.0*a*b*z;
- 2.0*a*b*sx*sy - 2.0*a*b*z;
- 2.0*a*b*(sx*sy + z);
{
sqrtIneq(square(z), x * y);
assert sqrt(x * y) >= sqrt(square(z));
sqrtMultiply(x, y);
assert sx * sy >= sqrt(square(z));
sqrtLemma(z);
assert sx * sy >= abs(z);
differenceLemma(sx*sy, z);
assert sx*sy >= -z;
}
0.0;
}
}
}

Dafny linear search

When working on a basic linear search I encountered an error with my Valid() predicate. It seems to only work when I uncomment the additional ensures statements on the constructor and the data method. That is, when I am very explicit about the contents.
I'm also having trouble with the postcondition of my search when the item isn't found.
Any suggestions on how to resolve these?
class Search{
ghost var Contents: set<int>;
var a : array<int>;
predicate Valid()
reads this, a;
{
a != null &&
a.Length > 0 &&
Contents == set x | 0 <= x < a.Length :: a[x]
}
constructor ()
ensures a != null;
ensures a.Length == 4;
//ensures a[0] == 0;
ensures Valid();
{
a := new int[4](_ => 0);
Contents := {0};
new;
}
method data()
modifies this, a;
requires Valid();
requires a != null;
requires a.Length == 4;
ensures a != null;
ensures a.Length == 4;
// ensures a[0] == 0;
// ensures a[1] == 1;
// ensures a[2] == 2;
// ensures a[3] == 3;
ensures Valid();
{
a[0] := 0;
a[1] := 1;
a[2] := 2;
a[3] := 3;
Contents := {0, 1, 2, 3};
}
method search(e: int) returns (r: bool)
modifies this, a;
requires Valid();
ensures Valid();
ensures r == (e in Contents)
ensures r == exists i: int :: 0 <= i < a.Length && a[i] == e
{
var length := a.Length - 1;
while (length >= 0)
decreases length;
{
var removed := a[length];
if (e == removed)
{
return true;
}
length := length - 1;
}
return false;
}
}
method Main()
{
var s := new Search();
s.data();
}
There are several orthogonal issues going on here.
First, you have noticed that Dafny is reluctant to reason about the part of Valid that describes Contents. This is a common problem when reasoning about sets in Dafny. Essentially, the only way Dafny will ever "notice" that something is a member of the set set x | 0 <= x < a.Length :: a[x] is if it already has the expression a[x] lying around somewhere. Your solution of including extra postconditions works because it mentions a lot of expressions of the form a[x]. Another solution is to include those facts as assertions instead of postconditions:
// in data()
assert a[0] == 0;
assert a[1] == 1;
assert a[2] == 2;
assert a[3] == 3;
Second, Dafny cannot show your search procedure satisfies its postcondition. You need a loop invariant to keep track of the progress of the search. See the guide for more information about how to design loop invariants.
Third, Dafny reports a problem with your Main about modifies clauses. You can fix this by adding a postcondition fresh(a) to the constructor. The problem here is that the data method claims to modify a, but Dafny can't tell if a is visible from the caller.

Dafny generic type array error

In trying to verify a generic FIFO queue backed by an array I ran into a confusing error. The queue was found in this paper, authored by the creator of Dafny.
The error in question is:
unless an initializer is provided for the array elements, a new array of 'Data' must have empty size
which relates to both lines allocating an array via new Data[whatever] in the constructor and the enqueue method.
Dafny version: Dafny 2.0.0.00922 technical preview 0
Full code for reference.
class {:autocontracts} SimpleQueue<Data>
{
ghost var Contents: seq<Data>;
var a: array<Data>;
var m: int, n: int;
predicate Valid() {
a != null && a.Length != 0 && 0 <= m <= n <= a.Length && Contents == a[m..n]
}
constructor ()
ensures Contents == [];
{
a := new Data[10];
m := 0;
n := 0;
Contents := [];
}
method Enqueue(d: Data)
ensures Contents == old(Contents) + [d];
{
if n == a.Length {
var b := a;
if m == 0 {
b := new Data[2 * a.Length];
}
forall (i | 0 <= i < n - m) {
b[i] := a[m + i];
}
a, m, n := b, 0, n - m;
}
a[n], n, Contents := d, n + 1, Contents + [d];
}
method Dequeue() returns (d: Data)
requires Contents != [];
ensures d == old(Contents)[0] && Contents == old(Contents)[1..];
{
assert a[m] == a[m..n][0];
d, m, Contents := a[m], m + 1, Contents[1..];
}
}
method Main()
{
var q := new SimpleQueue();
q.Enqueue(5); q.Enqueue(12);
var x := q.Dequeue();
assert x == 5;
}
Since the time of writing that paper, Dafny's type system has been generalized to support types that are not "default initializable". This has led to some backwards incompatibilities.
The easiest fix is to change
class SimpleQueue<Data>
to
class SimpleQueue<Data(0)>
which means that the type variable Data can only be instantiated with default-initializable types.
Another fix is to change the constructor to accept a default value for type Data as an argument. Then you can allocate an array using an initializer function, as in
new Data[10] (_ => d)

Dafny syntax error in function

I am struggling with dafny syntax.
searchAndReplace receives three arrays of chars. Let's imagine line is [s][n][a][k][e]; pat is [n][a] and dst is [h][i]. I want to search for all the occurrences of pat in line and replace it with dst resulting in [s][h][i][k][e]
Method findwill return the indice of the first letter in line that is equal to pat.
Method deletewill remove pat from line at the variable at returned at find, and move all the others elements after at+p to the left in order to fill the null spaces.
Method insertwill make space in order to dst be added to lineat atby moving all the characters between atand at + p ppositions to the right.
I created an auxiliar function which will compare patand dst in order to verify that they aren't equal(if they were it would be replacing infinitely time dstin line in case patexisted in line)
For now i'm receiving the error "then expected" on the following section of code inside function checkIfEqual:
if(pat.Length != dst.Length) {
return false;
}
The full code:
method searchAndReplace(line:array<char>, l:int,
pat:array<char>, p:int,
dst:array<char>, n:int)returns(nl:int)
requires line != null && pat!=null && dst!=null;
requires !checkIfEqual(pat, dst);
requires 0<=l<line.Length;
requires 0<=p<pat.Length;
requires 0<=n<dst.Length;
modifies line;
{
var at:int := 0;
var p:int := n;
while(at != -1 )
invariant -1<=at<=l;
{
at := find(line, l, dst, n);
delete(line, l, at, p);
insert(line, l, pat, p, at);
}
var length:int := line.Length;
return length;
}
function checkIfEqual(pat:array<char>, dst:array<char>):bool
requires pat!=null && dst!=null;
reads pat;
reads dst;
{
var i:int := 0;
if(pat.Length != dst.Length) {
return false;
}
while(i<dst.Length) {
if(pat[i] != dst[i]){
return false;
}
i := i + 1;
}
return true;
}
method insert(line:array<char>, l:int, nl:array<char>, p:int, at:int)
requires line != null && nl != null;
requires 0 <= l+p <= line.Length && 0 <= p <= nl.Length ;
requires 0 <= at <= l;
modifies line;
ensures forall i :: (0<=i<p) ==> line[at+i] == nl[i]; // error
{
var i:int := 0;
var positionAt:int := at;
while(i<l && positionAt < l)
invariant 0<=i<l+1;
invariant at<=positionAt<=l;
{
line[positionAt+p] := line[positionAt];
line[positionAt] := ' ';
positionAt := positionAt + 1;
i := i + 1;
}
positionAt := at;
i := 0;
while(i<p && positionAt < l)
invariant 0<=i<=p;
invariant at<=positionAt<=l;
{
line[positionAt] := nl[i];
positionAt := positionAt + 1;
i := i + 1;
}
}
method find(line:array<char>, l:int, pat:array<char>, p:int) returns (pos:int)
requires line!=null && pat!=null
requires 0 <= l < line.Length
requires 0 <= p < pat.Length
ensures 0 <= pos < l || pos == -1
{
var iline:int := 0;
var ipat:int := 0;
pos := -1;
while(iline<l && ipat<pat.Length)
invariant 0<=iline<=l
invariant 0<=ipat<=pat.Length
invariant -1 <= pos < iline
{
if(line[iline]==pat[ipat] && (line[iline]!=' ' && pat[ipat]!=' ')){
if(pos==-1){
pos := iline;
}
ipat:= ipat + 1;
} else {
if(ipat>0){
if(line[iline] == pat[ipat-1]){
pos := pos + 1;
}
}
ipat:=0;
pos := -1;
}
if(ipat==p) {
return;
}
iline := iline + 1;
}
return;
}
method delete(line:array<char>, l:nat, at:nat, p:nat)
requires line!=null
requires l <= line.Length
requires at+p <= l
modifies line
ensures line[..at] == old(line[..at])
ensures line[at..l-p] == old(line[at+p..l])
{
var i:nat := 0;
while(i < l-(at+p))
invariant i <= l-(at+p)
invariant at+p+i >= at+i
invariant line[..at] == old(line[..at])
invariant line[at..at+i] == old(line[at+p..at+p+i])
invariant line[at+i..l] == old(line[at+i..l]) // future is untouched
{
line[at+i] := line[at+p+i];
i := i+1;
}
}
functions in Dafny are pure, inductively defined and use a different syntax to the imperative methods. You cannot use imperative language features inside a function. In this case you are not allowed to use:
The conditional statement if cond { s1* } else { s2* }
The loop statement while cond { s1* }
Instead the body of the function must be an expression:
predicate checkIfEqual(pat:array<char>, dst:array<char>)
requires pat!=null && dst!=null;
reads pat;
reads dst;
{
pat.Length == dst.Length
&& forall i:nat :: i < pat.Length ==> pat[i] == dst[i]
}
Although it is not needed here, Dafny does have a conditional expression (ite):
predicate checkIfEqual(pat:array<char>, dst:array<char>)
requires pat!=null && dst!=null;
reads pat;
reads dst;
{
if pat.Length != dst.Length then false
else forall i:nat :: i < pat.Length ==> pat[i] == dst[i]
}
Note that:
You cannot put a loop in the body of a function, but you may use recursion
predicate is a shorthand for a function that returns bool

Resources