I wrote a method GetPredecessor(x, s)
which takes a real number x
and a sorted sequence of reals s
.
The input x
is not necessarily a member of the sequence, but it is greater than or equal to the smallest member of the sequence, and less than or equal to the largest member of the sequence.
The output is supposed to be the largest member of the sequence that is less than the input (if possible).
Let s = [1.0 2.0 3.0 4.0]
.
3.0 < x <= 4.0
(e.g. x = 3.2
, x = 3.8
, x = 4.0
), the output shall be 3.0
(not 1.0
or 2.0
).x = 1.0
there is no element in the sequence less than x
, so the output shall be 1.0
.I have been able to specify all preconditions and the following postconditions:
which is why the following assertions hold
var s := [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
var r := 0.0;
r := GetPredecessor(1.0, s);
assert r == 1.0; // OK
r := GetPredecessor(3.2, s);
assert r <= 3.0; // OK
I have been unable to write the following postcondition:
which is why this assertion does not hold:
r := GetPredecessor(3.2, s);
assert r == 3.0; // FAILS
What do I need to do to specify the behaviour explained above, and how do I need to change the method body for the postcondition to hold on all return paths in the method?
Any help is greatly appreciated.
predicate Sorted(s: seq<real>)
{
forall i,j :: 0 <= i < j < |s| ==> s[i] < s[j]
}
predicate Distinct(s: seq<real>)
{
forall i,j :: 0 <= i < |s| && 0 <= j < |s| && i != j ==> s[i] != s[j]
}
function method Min(s: seq<real>) : (m: real)
requires |s| > 0 && Sorted(s)
ensures m in s
ensures forall i :: 0 <= i < |s| ==> m <= s[i]
ensures exists i :: 0 <= i < |s| && m == s[i]
{
s[0]
}
function method Max(s: seq<real>) : (m: real)
requires |s| > 0 && Sorted(s)
ensures m in s
ensures forall i :: 0 <= i < |s| ==> m >= s[i]
ensures exists i :: 0 <= i < |s| && m == s[i]
{
s[|s| - 1]
}
// returns the largest element in the sequence s that is smaller than input x; or Min(s), if x is Min(s)
method GetPredecessor(x: real, s: seq<real>) returns (r: real)
requires |s| > 0 && Sorted(s) && Distinct(s)
requires Min(s) <= x <= Max(s)
ensures r in s
ensures x == Min(s) ==> r == Min(s)
ensures x != Min(s) ==> r < x
// TODO: add postcondition -> not just any "predecessor" is returned, rather the largest of them
{
if x == Min(s)
{
return x;
}
var i := 0;
r := s[i];
while i < |s|
invariant 0 <= i <= |s|
invariant r in s
invariant r < x
decreases |s| - i
{
if r < s[i] < x
{
r := s[i];
}
i := i + 1;
}
}
I think you need this predicate:
predicate LargestSmaller(x: real, s: seq<real>, returned: real)
{
&& returned in s
&& forall i | 0 <= i < |s| ::
s[i] > returned ==> s[i] > x
}
Here is how you'd use it in your method definition:
method GetPredecessor(x: real, s: seq<real>) returns (r: real)
...
ensures x != Min(s) ==> LargestSmaller(x, s, r)
Because you have a forall, you need to explicitly trigger it it if you want to prove your point:
r := GetPredecessor(3.2, s);
assert r == 1.0 || r == 2.0 || r == 3.0;
var _, _ := s[1], s[2]; // Automatically instantiate the predicate to prove that r != 1.0 and r != 2.0, otherwise you'd get a contradiction.
assert r == 3.0; // SUCCEEDS