The following array reversing code is "proven correct" with Dafny but it clearly isn't correct. What am I doing wrong?
A counter example is the array:
var a = new int[4] {1,3,5,7};
with the expected result {7,5,3,1}
and the actual result {1,3,3,1}
:
// A method reversing an array
predicate is_sint32(x : int) { -0x8000_0000 <= x < 0x8000_0000 }
newtype {:nativeType "int"} sint32 = x | -0x8000_0000 <= x < 0x8000_0000
method reverse(a: array<sint32>)
requires is_sint32(a.Length)
modifies a;
ensures forall i :: 0 <= i < a.Length ==> a[i] == old(a)[a.Length - 1 - i];
{
var i := 0 as sint32;
var aLength := a.Length as sint32;
while i < aLength
invariant 0 <= i && i <= aLength
invariant forall k :: 0 <= k < i ==> a[k] == old(a)[aLength - 1 - k];
{
a[aLength - 1 - i] := a[i];
i := i + 1;
}
}
I found a solution inspired by the great answer from James Wilcox. Adding here for future reference:
method reverse(a: array<int>)
modifies a;
ensures forall i :: 0 < i < a.Length ==> a[i] == old(a[a.Length - 1 - i]);
{
var i := 0;
while i < a.Length / 2
invariant 0 <= i <= a.Length / 2
invariant forall k :: 0 < k < i ==> a[k] == old(a[a.Length - 1 - k])
invariant forall k :: i <= k < a.Length - i ==> a[k] == old(a[k])
invariant forall k :: 0 <= k < i ==> a[a.Length - 1 - k] == old(a[k])
{
var v0 := a[i];
var v1 := a[a.Length - 1 - i];
a[i] := v1;
a[a.Length - 1 - i] := v0;
i := i + 1;
}
}
And here is the solution using C# native types for optimum performance when generating C# code:
predicate is_sint32(x : int) { -0x8000_0000 <= x < 0x8000_0000 }
newtype {:nativeType "int"} sint32 = x : int | -0x8000_0000 <= x < 0x8000_0000
method reverse(a: array<sint32>)
requires is_sint32(a.Length)
modifies a;
ensures forall i :: 0 < i < a.Length ==> a[i] == old(a[a.Length - 1 - i]);
{
var aLength := a.Length as sint32;
var aLengthDiv2 := aLength / 2;
var i := 0 as sint32;
while i < aLengthDiv2
invariant 0 <= i <= aLengthDiv2
invariant forall k :: 0 < k < i ==> a[k] == old(a[aLength - 1 - k])
invariant forall k :: i <= k < aLength - i ==> a[k] == old(a[k])
invariant forall k :: 0 <= k < i ==> a[aLength - 1 - k] == old(a[k])
{
var v0 := a[i];
var v1 := a[aLength - 1 - i];
a[i] := v1;
a[aLength - 1 - i] := v0;
i := i + 1;
}
}