I'm attempting to annotate the merge sort algorithm with ACSL specifications and verify its correctness using Frama-C's WP plugin, but I'm running into some issues. Below is my code:
/*@
predicate Sorted(int *t, integer lo, integer hi) =
\forall integer i, j; lo <= i <= j < hi ==> t[i] <= t[j];
*/
/*@
requires \valid(A + (0..a - 1));
requires \valid(B + (0..b - 1));
requires \valid(res + (0..a + b - 1));
requires a > 0;
requires b > 0;
requires Sorted(A, 0, a);
requires Sorted(B, 0, b);
assigns res[0..a + b - 1];
ensures Sorted(res, 0, a + b);
*/
void merge(int A[], int B[], int res[], int a, int b) {
int i = 0;
int j = 0;
int k = 0;
/*@
loop invariant 0 <= i <= a;
loop invariant 0 <= j <= b;
loop invariant k == i + j;
loop invariant 0 <= k <= a + b;
loop invariant res_sorted_1: Sorted(res, 0, k);
loop invariant less_than_A: \forall integer p; 0 <= p < k && i < a ==> res[p] <= A[i];
loop invariant less_than_B: \forall integer p; 0 <= p < k && j < a ==> res[p] <= B[j];
loop assigns i, j, k;
loop assigns res[0..a + b - 1];
*/
while (i < a && j < b) {
if (A[i] <= B[j]) {
res[k] = A[i];
k = k + 1;
i = i + 1;
}
else {
res[k] = B[j];
k = k + 1;
j = j + 1;
}
}
/*@
loop invariant 0 <= i <= a;
loop invariant k == i + j;
loop invariant 0 <= k <= a + b;
loop invariant res_sorted_2: Sorted(res, 0, k);
loop invariant less_than_A_2: \forall integer p; 0 <= p < k && i < a ==> res[p] <= A[i];
loop assigns res[0..a + b - 1];
loop assigns i, k;
*/
while (i < a) {
res[k] = A[i];
k = k + 1;
i = i + 1;
}
/*@
loop invariant 0 <= j <= b;
loop invariant k == i + j;
loop invariant 0 <= k <= a + b;
loop invariant res_sorted_3: Sorted(res, 0, k);
loop invariant less_than_B_2:\forall integer p; 0 <= p < k && j < a ==> res[p] <= B[j];
loop assigns res[0..a + b - 1];
loop assigns j, k;
*/
while (j < b) {
res[k] = B[j];
k = k + 1;
j = j + 1;
}
}
When I run frama-c -wp -wp-prover Alt-Ergo,Z3 -wp-print -wp-timeout 20 _merge.c
I am getting some timeout result. I've excerpted part of the output messages:
[wp] Proved goals: 40 / 47
Qed: 25 (0.61ms-24ms-66ms)
Alt-Ergo : 14 (10ms-28ms-112ms) (670) (interrupted: 7) (cached: 21)
Z3 4.8.6: 12 (10ms-32ms-60ms) (453310) (interrupted: 7) (cached: 18)
------------------------------------------------------------
Goal Preservation of Invariant 'less_than_A' (file Benchmark/acsl-algorithms_verified/fail/_merge.c, line 27):
Let x = a + b.
Let a_1 = shift_sint32(res_0, 0).
Let a_2 = havoc(Mint_undef_0, Mint_1, a_1, x).
Let x_1 = k_3 - j_1.
Let a_3 = a_2[shift_sint32(A, x_1)].
Let a_4 = a_2[shift_sint32(B, j_1)].
Let x_2 = a + j_1.
Let x_3 = k - j.
Assume {
Type: is_sint32(a) /\ is_sint32(b) /\ is_sint32(i_1) /\ is_sint32(j) /\
is_sint32(j_1) /\ is_sint32(k) /\ is_sint32(k_1) /\ is_sint32(k_2) /\
is_sint32(k_3) /\ is_sint32(x_3) /\ is_sint32(x_1) /\ is_sint32(a_4) /\
is_sint32(a_3).
(* Heap *)
Type: (region(A.base) <= 0) /\ (region(B.base) <= 0) /\
(region(res_0.base) <= 0) /\ linked(Malloc_0).
(* Goal *)
When: (0 <= i) /\ (i < k) /\ (k < (a + j)).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(A, 0), a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(B, 0), b).
(* Pre-condition *)
Have: valid_rw(Malloc_0, a_1, x).
(* Pre-condition *)
Have: 0 < a.
(* Pre-condition *)
Have: 0 < b.
(* Pre-condition *)
Have: P_Sorted(Mint_1, A, 0, a).
(* Pre-condition *)
Have: P_Sorted(Mint_1, B, 0, b).
(* Invariant *)
Have: 0 <= x.
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_1, res_0, 0, 0).
(* Invariant *)
Have: (j_1 <= k_3) /\ (k_3 <= x_2).
(* Invariant *)
Have: (j_1 <= b) /\ (0 <= j_1).
(* Invariant *)
Have: (0 <= k_3) /\ (k_3 <= x).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(a_2, res_0, 0, k_3).
(* Invariant 'less_than_A' *)
Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < k_3) ->
(a_2[shift_sint32(res_0, i_2)] <= a_3))).
(* Invariant 'less_than_B' *)
Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < k_3) -> ((j_1 < a) ->
(a_2[shift_sint32(res_0, i_2)] <= a_4)))).
(* Then *)
Have: k_3 < x_2.
(* Then *)
Have: j_1 < b.
If a_3 <= a_4
Then {
Have: (k_3 = k_1) /\ ((i_1 + j) = k_1) /\ ((i_1 + j_1) = k_1).
Have: a_2[shift_sint32(res_0, k_1) <- a_2[shift_sint32(A, i_1)]] =
Mint_0.
Have: (1 + k_1) = k.
}
Else {
Have: (k_3 = k_2) /\ ((1 + j_1) = j) /\ ((1 + k_2) = k).
Have: a_2[shift_sint32(res_0, k_2) <- a_4] = Mint_0.
}
(* Invariant *)
Have: j <= k.
(* Invariant *)
Have: (j <= b) /\ (0 <= j).
(* Invariant *)
Have: (0 <= k) /\ (k <= x).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_0, res_0, 0, k).
}
Prove: Mint_0[shift_sint32(res_0, i)] <= Mint_0[shift_sint32(A, x_3)].
Prover Z3 4.8.6 returns Timeout (Qed:67ms) (20s) (cached)
Prover Alt-Ergo returns Timeout (Qed:67ms) (20s) (cached)
------------------------------------------------------------
Goal Preservation of Invariant 'less_than_B' (file Benchmark/acsl-algorithms_verified/fail/_merge.c, line 28):
Let x = a + b.
Let a_1 = shift_sint32(res_0, 0).
Let a_2 = havoc(Mint_undef_0, Mint_1, a_1, x).
Let x_1 = k_3 - j.
Let a_3 = a_2[shift_sint32(A, x_1)].
Let a_4 = a_2[shift_sint32(B, j)].
Let x_2 = a + j.
Let x_3 = k - i_1.
Assume {
Type: is_sint32(a) /\ is_sint32(b) /\ is_sint32(i_1) /\ is_sint32(i_2) /\
is_sint32(j) /\ is_sint32(k) /\ is_sint32(k_1) /\ is_sint32(k_2) /\
is_sint32(k_3) /\ is_sint32(x_3) /\ is_sint32(x_1) /\ is_sint32(a_4) /\
is_sint32(a_3).
(* Heap *)
Type: (region(A.base) <= 0) /\ (region(B.base) <= 0) /\
(region(res_0.base) <= 0) /\ linked(Malloc_0).
(* Goal *)
When: (0 <= i) /\ (i < k) /\ (k < (a + i_1)).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(A, 0), a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(B, 0), b).
(* Pre-condition *)
Have: valid_rw(Malloc_0, a_1, x).
(* Pre-condition *)
Have: 0 < a.
(* Pre-condition *)
Have: 0 < b.
(* Pre-condition *)
Have: P_Sorted(Mint_1, A, 0, a).
(* Pre-condition *)
Have: P_Sorted(Mint_1, B, 0, b).
(* Invariant *)
Have: 0 <= x.
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_1, res_0, 0, 0).
(* Invariant *)
Have: (j <= k_3) /\ (k_3 <= x_2).
(* Invariant *)
Have: (j <= b) /\ (0 <= j).
(* Invariant *)
Have: (0 <= k_3) /\ (k_3 <= x).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(a_2, res_0, 0, k_3).
(* Invariant 'less_than_A' *)
Have: forall i_3 : Z. ((0 <= i_3) -> ((i_3 < k_3) ->
(a_2[shift_sint32(res_0, i_3)] <= a_3))).
(* Invariant 'less_than_B' *)
Have: forall i_3 : Z. ((0 <= i_3) -> ((i_3 < k_3) -> ((j < a) ->
(a_2[shift_sint32(res_0, i_3)] <= a_4)))).
(* Then *)
Have: k_3 < x_2.
(* Then *)
Have: j < b.
If a_3 <= a_4
Then {
Have: ((1 + k_1) = k) /\ ((1 + k_3) = k) /\ ((i_1 + j) = k) /\
((1 + i_2 + j) = k).
Have: a_2[shift_sint32(res_0, k - 1) <- a_2[shift_sint32(A, k - 1 - j)]] =
Mint_0.
}
Else {
Have: (k_3 = k_2) /\ ((i_1 + j) = k_2).
Have: a_2[shift_sint32(res_0, k_2) <- a_4] = Mint_0.
Have: (1 + k_2) = k.
}
(* Invariant *)
Have: (i_1 <= a) /\ (0 <= i_1).
(* Invariant *)
Have: (i_1 <= k) /\ (k <= (b + i_1)).
(* Invariant *)
Have: (0 <= k) /\ (k <= x).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_0, res_0, 0, k).
(* Invariant 'less_than_A' *)
Have: forall i_3 : Z. ((i_1 < a) -> ((0 <= i_3) -> ((i_3 < k) ->
(Mint_0[shift_sint32(res_0, i_3)] <= Mint_0[shift_sint32(A, i_1)])))).
}
Prove: Mint_0[shift_sint32(res_0, i)] <= Mint_0[shift_sint32(B, x_3)].
Prover Z3 4.8.6 returns Timeout (Qed:69ms) (20s) (cached)
Prover Alt-Ergo returns Timeout (Qed:69ms) (20s) (cached)
------------------------------------------------------------
Goal Preservation of Invariant 'res_sorted_1' (file Benchmark/acsl-algorithms_verified/fail/_merge.c, line 26):
Let x = i + j.
Let x_1 = a + b.
Let a_1 = shift_sint32(res_0, 0).
Let a_2 = havoc(Mint_undef_0, Mint_1, a_1, x_1).
Let x_2 = k_2 - j_1.
Let a_3 = a_2[shift_sint32(A, x_2)].
Let a_4 = a_2[shift_sint32(B, j_1)].
Let x_3 = a + j_1.
Assume {
Type: is_sint32(a) /\ is_sint32(b) /\ is_sint32(i) /\ is_sint32(i_1) /\
is_sint32(j) /\ is_sint32(j_1) /\ is_sint32(k) /\ is_sint32(k_1) /\
is_sint32(k_2) /\ is_sint32(x) /\ is_sint32(x_2) /\ is_sint32(a_4) /\
is_sint32(a_3).
(* Heap *)
Type: (region(A.base) <= 0) /\ (region(B.base) <= 0) /\
(region(res_0.base) <= 0) /\ linked(Malloc_0).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(A, 0), a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(B, 0), b).
(* Pre-condition *)
Have: valid_rw(Malloc_0, a_1, x_1).
(* Pre-condition *)
Have: 0 < a.
(* Pre-condition *)
Have: 0 < b.
(* Pre-condition *)
Have: P_Sorted(Mint_1, A, 0, a).
(* Pre-condition *)
Have: P_Sorted(Mint_1, B, 0, b).
(* Invariant *)
Have: 0 <= x_1.
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_1, res_0, 0, 0).
(* Invariant *)
Have: (j_1 <= k_2) /\ (k_2 <= x_3).
(* Invariant *)
Have: (j_1 <= b) /\ (0 <= j_1).
(* Invariant *)
Have: (0 <= k_2) /\ (k_2 <= x_1).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(a_2, res_0, 0, k_2).
(* Invariant 'less_than_A' *)
Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < k_2) ->
(a_2[shift_sint32(res_0, i_2)] <= a_3))).
(* Invariant 'less_than_B' *)
Have: forall i_2 : Z. ((0 <= i_2) -> ((i_2 < k_2) -> ((j_1 < a) ->
(a_2[shift_sint32(res_0, i_2)] <= a_4)))).
(* Then *)
Have: k_2 < x_3.
(* Then *)
Have: j_1 < b.
If a_3 <= a_4
Then {
Have: (k_2 = k) /\ ((i_1 + j) = k) /\ ((i_1 + j_1) = k).
Have: a_2[shift_sint32(res_0, k) <- a_2[shift_sint32(A, i_1)]] = Mint_0.
Have: (1 + i_1) = i.
}
Else {
Have: (k_2 = k_1) /\ ((i + j_1) = k_1).
Have: a_2[shift_sint32(res_0, k_1) <- a_4] = Mint_0.
Have: (1 + j_1) = j.
}
(* Invariant *)
Have: (i <= a) /\ (0 <= i).
(* Invariant *)
Have: (j <= b) /\ (0 <= j).
(* Invariant *)
Have: (x <= x_1) /\ (0 <= x).
}
Prove: P_Sorted(Mint_0, res_0, 0, x).
Prover Z3 4.8.6 returns Timeout (Qed:69ms) (20s) (cached)
Prover Alt-Ergo returns Timeout (Qed:69ms) (20s) (cached)
------------------------------------------------------------
Goal Preservation of Invariant 'less_than_A_2' (file Benchmark/acsl-algorithms_verified/fail/_merge.c, line 50):
Let a_1 = shift_sint32(res_0, 0).
Let x = a + b.
Let a_2 = havoc(Mint_undef_0, Mint_0, a_1, x).
Let x_1 = -j.
Let x_2 = k - j.
Let a_3 = a_2[shift_sint32(A, x_2)].
Let a_4 = a_2[shift_sint32(res_0, k) <- a_3].
Let x_3 = 1 + k.
Let x_4 = a + j.
Let a_5 = havoc(Mint_undef_1, Mint_0, a_1, x).
Let x_5 = k_1 - j.
Let x_6 = 1 + k - j.
Assume {
Type: is_sint32(a) /\ is_sint32(b) /\ is_sint32(j) /\ is_sint32(k) /\
is_sint32(k_1) /\ is_sint32(x_3) /\ is_sint32(x_2) /\ is_sint32(x_5) /\
is_sint32(x_6).
(* Heap *)
Type: (region(A.base) <= 0) /\ (region(B.base) <= 0) /\
(region(res_0.base) <= 0) /\ linked(Malloc_0).
(* Goal *)
When: (0 <= i) /\ (i <= k) /\ ((2 + k) <= x_4).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(A, 0), a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(B, 0), b).
(* Pre-condition *)
Have: valid_rw(Malloc_0, a_1, x).
(* Pre-condition *)
Have: 0 < a.
(* Pre-condition *)
Have: 0 < b.
(* Pre-condition *)
Have: P_Sorted(Mint_0, A, 0, a).
(* Pre-condition *)
Have: P_Sorted(Mint_0, B, 0, b).
(* Invariant *)
Have: 0 <= x.
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_0, res_0, 0, 0).
(* Invariant *)
Have: (j <= k_1) /\ (k_1 <= x_4).
(* Invariant *)
Have: (j <= b) /\ (0 <= j).
(* Invariant *)
Have: (0 <= k_1) /\ (k_1 <= x).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(a_5, res_0, 0, k_1).
(* Invariant 'less_than_A' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k_1) -> ((k_1 < x_4) ->
(a_5[shift_sint32(res_0, i_1)] <= a_5[shift_sint32(A, x_5)])))).
(* Invariant 'less_than_B' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k_1) -> ((j < a) ->
(a_5[shift_sint32(res_0, i_1)] <= a_5[shift_sint32(B, j)])))).
If k_1 < x_4
Then { (* Else *) Have: b <= j. }
(* Invariant *)
Have: (j <= k) /\ (k <= x_4).
(* Invariant *)
Have: (0 <= k) /\ (k <= x).
(* Invariant 'res_sorted_2' *)
Have: P_Sorted(a_2, res_0, 0, k).
(* Invariant 'less_than_A_2' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k) ->
(a_2[shift_sint32(res_0, i_1)] <= a_3))).
(* Then *)
Have: k < x_4.
(* Invariant *)
Have: j <= x_3.
(* Invariant *)
Have: (k < x) /\ ((-1) <= k).
(* Invariant 'res_sorted_2' *)
Have: P_Sorted(a_4, res_0, 0, x_3).
}
Prove: a_4[shift_sint32(res_0, i)] <= a_4[shift_sint32(A, x_6)].
Prover Z3 4.8.6 returns Timeout (Qed:54ms) (20s) (cached)
Prover Alt-Ergo returns Timeout (Qed:54ms) (20s) (cached)
------------------------------------------------------------
Goal Preservation of Invariant 'less_than_B_2' (file Benchmark/acsl-algorithms_verified/fail/_merge.c, line 65):
Let a_1 = shift_sint32(res_0, 0).
Let x = a + b.
Let a_2 = havoc(Mint_undef_0, Mint_0, a_1, x).
Let x_1 = -a.
Let a_3 = a_2[shift_sint32(B, k - a)].
Let a_4 = a_2[shift_sint32(res_0, k) <- a_3].
Let x_2 = 1 + k.
Let x_3 = a + j.
Let a_5 = havoc(Mint_undef_2, Mint_0, a_1, x).
Let a_6 = shift_sint32(B, j).
Let a_7 = havoc(Mint_undef_1, Mint_0, a_1, x).
Let x_4 = -j.
Let x_5 = k_2 - j.
Let x_6 = -k_1.
Assume {
Type: is_sint32(a) /\ is_sint32(b) /\ is_sint32(j) /\ is_sint32(k) /\
is_sint32(k_1) /\ is_sint32(k_2) /\ is_sint32(x_2) /\
is_sint32(k_1 - j) /\ is_sint32(x_5) /\ is_sint32(j + k - k_1) /\
is_sint32(1 + j + k - k_1).
(* Heap *)
Type: (region(A.base) <= 0) /\ (region(B.base) <= 0) /\
(region(res_0.base) <= 0) /\ linked(Malloc_0).
(* Goal *)
When: (0 <= i) /\ (i <= k) /\ ((2 + j + k) <= (a + k_1)).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(A, 0), a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(B, 0), b).
(* Pre-condition *)
Have: valid_rw(Malloc_0, a_1, x).
(* Pre-condition *)
Have: 0 < a.
(* Pre-condition *)
Have: 0 < b.
(* Pre-condition *)
Have: P_Sorted(Mint_0, A, 0, a).
(* Pre-condition *)
Have: P_Sorted(Mint_0, B, 0, b).
(* Invariant *)
Have: 0 <= x.
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_0, res_0, 0, 0).
(* Invariant *)
Have: (j <= k_2) /\ (k_2 <= x_3).
(* Invariant *)
Have: (j <= b) /\ (0 <= j).
(* Invariant *)
Have: (0 <= k_2) /\ (k_2 <= x).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(a_7, res_0, 0, k_2).
(* Invariant 'less_than_A' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k_2) -> ((k_2 < x_3) ->
(a_7[shift_sint32(res_0, i_1)] <= a_7[shift_sint32(A, x_5)])))).
(* Invariant 'less_than_B' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k_2) -> ((j < a) ->
(a_7[shift_sint32(res_0, i_1)] <= a_7[a_6])))).
If k_2 < x_3
Then { (* Else *) Have: b <= j. }
(* Invariant *)
Have: (j <= k_1) /\ (k_1 <= x_3).
(* Invariant *)
Have: (0 <= k_1) /\ (k_1 <= x).
(* Invariant 'res_sorted_2' *)
Have: P_Sorted(a_5, res_0, 0, k_1).
(* Else *)
Have: x_3 <= k_1.
(* Invariant 'less_than_B_2' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((j < a) -> ((i_1 < x_3) ->
(a_5[shift_sint32(res_0, i_1)] <= a_5[a_6])))).
(* Invariant *)
Have: (a <= k) /\ (k <= x).
(* Invariant *)
Have: 0 <= k.
(* Invariant 'res_sorted_3' *)
Have: P_Sorted(a_2, res_0, 0, k).
(* Invariant 'less_than_B_2' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k) -> ((k < (2 * a)) ->
(a_2[shift_sint32(res_0, i_1)] <= a_3)))).
(* Then *)
Have: k < x.
(* Invariant *)
Have: a <= x_2.
(* Invariant *)
Have: (-1) <= k.
(* Invariant 'res_sorted_3' *)
Have: P_Sorted(a_4, res_0, 0, x_2).
}
Prove: a_4[shift_sint32(res_0, i)] <= a_4[shift_sint32(B, 1 + k - a)].
Prover Z3 4.8.6 returns Timeout (Qed:58ms) (20s) (cached)
Prover Alt-Ergo returns Timeout (Qed:58ms) (20s) (cached)
------------------------------------------------------------
Goal Establishment of Invariant 'less_than_B_2' (file Benchmark/acsl-algorithms_verified/fail/_merge.c, line 65):
Let x = a + j.
Let a_1 = shift_sint32(res_0, 0).
Let x_1 = a + b.
Let a_2 = havoc(Mint_undef_0, Mint_0, a_1, x_1).
Let a_3 = havoc(Mint_undef_1, Mint_0, a_1, x_1).
Let a_4 = shift_sint32(B, j).
Let x_2 = -j.
Let x_3 = k_1 - j.
Assume {
Type: is_sint32(a) /\ is_sint32(b) /\ is_sint32(j) /\ is_sint32(k) /\
is_sint32(k_1) /\ is_sint32(k - j) /\ is_sint32(x_3).
(* Heap *)
Type: (region(A.base) <= 0) /\ (region(B.base) <= 0) /\
(region(res_0.base) <= 0) /\ linked(Malloc_0).
(* Goal *)
When: (0 <= i) /\ (i < k) /\ (j < a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(A, 0), a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(B, 0), b).
(* Pre-condition *)
Have: valid_rw(Malloc_0, a_1, x_1).
(* Pre-condition *)
Have: 0 < a.
(* Pre-condition *)
Have: 0 < b.
(* Pre-condition *)
Have: P_Sorted(Mint_0, A, 0, a).
(* Pre-condition *)
Have: P_Sorted(Mint_0, B, 0, b).
(* Invariant *)
Have: 0 <= x_1.
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_0, res_0, 0, 0).
(* Invariant *)
Have: (j <= k_1) /\ (k_1 <= x).
(* Invariant *)
Have: (j <= b) /\ (0 <= j).
(* Invariant *)
Have: (0 <= k_1) /\ (k_1 <= x_1).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(a_3, res_0, 0, k_1).
(* Invariant 'less_than_A' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k_1) -> ((k_1 < x) ->
(a_3[shift_sint32(res_0, i_1)] <= a_3[shift_sint32(A, x_3)])))).
(* Invariant 'less_than_B' *)
Have: forall i_1 : Z. ((0 <= i_1) -> ((i_1 < k_1) ->
(a_3[shift_sint32(res_0, i_1)] <= a_3[a_4]))).
If k_1 < x
Then { (* Else *) Have: b <= j. }
(* Invariant *)
Have: (j <= k) /\ (k <= x).
(* Invariant *)
Have: (0 <= k) /\ (k <= x_1).
(* Invariant 'res_sorted_2' *)
Have: P_Sorted(a_2, res_0, 0, k).
(* Else *)
Have: x <= k.
}
Prove: a_2[shift_sint32(res_0, i)] <= a_2[a_4].
Prover Z3 4.8.6 returns Timeout (Qed:37ms) (20s) (cached)
Prover Alt-Ergo returns Timeout (Qed:37ms) (20s) (cached)
------------------------------------------------------------
Goal Preservation of Invariant 'res_sorted_3' (file Benchmark/acsl-algorithms_verified/fail/_merge.c, line 64):
Let x = a + j.
Let a_1 = shift_sint32(res_0, 0).
Let x_1 = a + b.
Let a_2 = havoc(Mint_undef_0, Mint_0, a_1, x_1).
Let a_3 = a_2[shift_sint32(B, j)].
Let x_2 = a + j_1.
Let a_4 = havoc(Mint_undef_2, Mint_0, a_1, x_1).
Let a_5 = shift_sint32(B, j_1).
Let a_6 = havoc(Mint_undef_1, Mint_0, a_1, x_1).
Let x_3 = -j_1.
Let x_4 = k_1 - j_1.
Assume {
Type: is_sint32(a) /\ is_sint32(b) /\ is_sint32(j) /\ is_sint32(j_1) /\
is_sint32(k) /\ is_sint32(k_1) /\ is_sint32(1 + j) /\
is_sint32(k - j_1) /\ is_sint32(x_4) /\ is_sint32(j + k - j_1) /\
is_sint32(1 + j + k - j_1).
(* Heap *)
Type: (region(A.base) <= 0) /\ (region(B.base) <= 0) /\
(region(res_0.base) <= 0) /\ linked(Malloc_0).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(A, 0), a).
(* Pre-condition *)
Have: valid_rw(Malloc_0, shift_sint32(B, 0), b).
(* Pre-condition *)
Have: valid_rw(Malloc_0, a_1, x_1).
(* Pre-condition *)
Have: 0 < a.
(* Pre-condition *)
Have: 0 < b.
(* Pre-condition *)
Have: P_Sorted(Mint_0, A, 0, a).
(* Pre-condition *)
Have: P_Sorted(Mint_0, B, 0, b).
(* Invariant *)
Have: 0 <= x_1.
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(Mint_0, res_0, 0, 0).
(* Invariant *)
Have: (j_1 <= k_1) /\ (k_1 <= x_2).
(* Invariant *)
Have: (j_1 <= b) /\ (0 <= j_1).
(* Invariant *)
Have: (0 <= k_1) /\ (k_1 <= x_1).
(* Invariant 'res_sorted_1' *)
Have: P_Sorted(a_6, res_0, 0, k_1).
(* Invariant 'less_than_A' *)
Have: forall i : Z. ((0 <= i) -> ((i < k_1) -> ((k_1 < x_2) ->
(a_6[shift_sint32(res_0, i)] <= a_6[shift_sint32(A, x_4)])))).
(* Invariant 'less_than_B' *)
Have: forall i : Z. ((0 <= i) -> ((i < k_1) -> ((j_1 < a) ->
(a_6[shift_sint32(res_0, i)] <= a_6[a_5])))).
If k_1 < x_2
Then { (* Else *) Have: b <= j_1. }
(* Invariant *)
Have: (j_1 <= k) /\ (k <= x_2).
(* Invariant *)
Have: (0 <= k) /\ (k <= x_1).
(* Invariant 'res_sorted_2' *)
Have: P_Sorted(a_4, res_0, 0, k).
(* Else *)
Have: x_2 <= k.
(* Invariant 'less_than_B_2' *)
Have: forall i : Z. ((0 <= i) -> ((j_1 < a) -> ((i < x_2) ->
(a_4[shift_sint32(res_0, i)] <= a_4[a_5])))).
(* Invariant *)
Have: (j <= b) /\ (0 <= j).
(* Invariant *)
Have: 0 <= x.
(* Invariant 'res_sorted_3' *)
Have: P_Sorted(a_2, res_0, 0, x).
(* Invariant 'less_than_B_2' *)
Have: forall i : Z. ((0 <= i) -> ((j < a) -> ((i < x) ->
(a_2[shift_sint32(res_0, i)] <= a_3)))).
(* Then *)
Have: j < b.
(* Invariant *)
Have: (-1) <= j.
(* Invariant *)
Have: (-1) <= x.
}
Prove: P_Sorted(a_2[shift_sint32(res_0, x) <- a_3], res_0, 0, 1 + a + j).
Prover Z3 4.8.6 returns Timeout (Qed:53ms) (20s) (cached)
Prover Alt-Ergo returns Timeout (Qed:53ms) (20s) (cached)
------------------------------------------------------------
I'd like to understand how to modify existing invariants or add new specifications to make Frama-C successfully verify the implementation. Below is a verified Dafny implementation of the merge algorithm that I'm referencing:
predicate IsPermutation<T(==)>(input: seq<T>, output: seq<T>)
{
multiset(input) == multiset(output)
}
predicate Sorted(a: seq<int>)
{
forall j, k :: 0 <= j <= k < |a| ==> a[j] <= a[k]
}
predicate merged(a1: seq<int>, a2: seq<int>, b: seq<int>)
{
multiset(a1) + multiset(a2) == multiset(b)
}
method merge(a1: seq<int>, a2: seq<int>, start: int, end: int, b: array<int>)
modifies b
requires Sorted(a1)
requires Sorted(a2)
requires end - start == |a1| + |a2|
requires 0 <= start <= end <= b.Length
ensures Sorted(b[start..end])
ensures merged(a1, a2, b[start..end])
ensures forall p:: 0 <= p < start ==> old(b[p]) == b[p]
ensures forall p:: end <= p < b.Length ==> old(b[p]) == b[p]
{
assert forall xs : seq<int> :: xs[0..|xs|] == xs;
assert forall xs : seq<int>, a,b : int :: 0 <= a < b < |xs| ==> xs[a..b+1] == xs[a..b] + [xs[b]];
var a1Pos := 0;
var a2Pos := 0;
var k := start;
while k < end
invariant (start <= k && k <= end)
invariant Sorted(b[start..k])
invariant (|a1| - a1Pos) + (|a2| - a2Pos) == end - k
invariant 0 <= a1Pos <= |a1|
invariant 0 <= a2Pos <= |a2|
invariant forall i :: start <= i < k && a1Pos < |a1| ==> b[i] <= a1[a1Pos]
invariant forall i :: start <= i < k && a2Pos < |a2| ==> b[i] <= a2[a2Pos]
invariant merged(a1[0..a1Pos], a2[0..a2Pos], b[start..k])
invariant forall p:: 0 <= p < start ==> old(b[p]) == b[p]
invariant forall p:: end <= p < b.Length ==> old(b[p]) == b[p]
{
if a1Pos < |a1| && a2Pos < |a2| && a1[a1Pos] <= a2[a2Pos] {
b[k] := a1[a1Pos];
a1Pos := a1Pos + 1;
} else if a1Pos < |a1| && a2Pos < |a2| && a1[a1Pos] > a2[a2Pos] {
b[k] := a2[a2Pos];
a2Pos := a2Pos + 1;
} else if a1Pos < |a1| {
assert(a2Pos >= |a2|);
b[k] := a1[a1Pos];
a1Pos := a1Pos + 1;
} else {
assert(a1Pos >= |a1|);
b[k] := a2[a2Pos];
a2Pos := a2Pos + 1;
}
k := k + 1;
}
}
My Frama-C version is 25.0 (Manganese)
.
The current contract is wrong, in particular, if res
and a
or b
overlap, it is not possible to fulfill the specification with this code. So first, one needs to specify that original and result arrays are separated memory locations.
requires \separated(&A[0..(a-1)], &B[0..(b-1)], &res[0..(a+b-1)]);
Then, the invariant less_than_B
has a typo:
loop invariant less_than_B: \forall integer p; 0 <= p < k && j < a ==> res[p] <= B[j];
~~~~~~^
loop invariant less_than_B: \forall integer p; 0 <= p < k && j < b ==> res[p] <= B[j];
Same typo in less_than_B_2
.
Finally, one needs to state what is not modified by the second loop and state more precise bounds for k
so that WP can easily handle the last case:
//@ ghost End_l: ;
/*@ ...
loop invariant \at(k, End_l) <= k <= a + b;
...
loop invariant unchanged_beg: \forall integer p; 0 <= p < \at(k, End_l) ==> res[p] == \at(res[p], End_l);
loop invariant unchanged_end: \forall integer p; k <= p < a+b ==> res[p] == \at(res[p], End_l);
Which allows to finish the proof.
/*@
predicate Sorted(int *t, integer lo, integer hi) =
\forall integer i, j; lo <= i <= j < hi ==> t[i] <= t[j];
*/
/*@
requires \valid(A + (0..a- 1));
requires \valid(B + (0..b - 1));
requires \valid(res + (0..a + b - 1));
requires \separated(&A[0..(a-1)], &B[0..(b-1)], &res[0..(a+b-1)]);
requires a > 0;
requires b > 0;
requires Sorted(A, 0, a);
requires Sorted(B, 0, b);
assigns res[0..a + b - 1];
ensures Sorted(res, 0, a + b);
*/
void merge(int A[], int B[], int res[], int a, int b) {
int i = 0;
int j = 0;
int k = 0;
/*@
loop invariant 0 <= i <= a;
loop invariant 0 <= j <= b;
loop invariant k == i + j;
loop invariant 0 <= k <= a + b;
loop invariant res_sorted_1: Sorted(res, 0, k);
loop invariant less_than_A: \forall integer p; 0 <= p < k && i < a ==> res[p] <= A[i];
loop invariant less_than_B: \forall integer p; 0 <= p < k && j < b ==> res[p] <= B[j];
loop assigns i, j, k;
loop assigns res[0..a + b - 1];
loop variant (a + b) - k ;
*/
while (i < a && j < b) {
if (A[i] <= B[j]) {
res[k] = A[i];
k = k + 1;
i = i + 1;
}
else {
res[k] = B[j];
k = k + 1;
j = j + 1;
}
}
//@ ghost End_l: ;
/*@
loop invariant 0 <= i <= a;
loop invariant k == i + j;
loop invariant \at(k, End_l) <= k <= a + b;
loop invariant res_sorted_2: Sorted(res, 0, k);
loop invariant less_than_A_2: \forall integer p; 0 <= p < k && i < a ==> res[p] <= A[i];
loop invariant unchanged_beg: \forall integer p; 0 <= p < \at(k, End_l) ==> res[p] == \at(res[p], End_l);
loop invariant unchanged_end: \forall integer p; k <= p < a+b ==> res[p] == \at(res[p], End_l);
loop assigns res[0..a + b - 1];
loop assigns i, k;
loop variant a - i ;
*/
while (i < a) {
res[k] = A[i];
k = k + 1;
i = i + 1;
}
/*@
loop invariant 0 <= j <= b;
loop invariant k == i + j;
loop invariant 0 <= k <= a + b;
loop invariant res_sorted_3: Sorted(res, 0, k);
loop invariant less_than_B_2:\forall integer p; 0 <= p < k && j < b ==> res[p] <= B[j];
loop assigns res[0..a + b - 1];
loop assigns j, k;
loop variant b - j;
*/
while (j < b) {
res[k] = B[j];
k = k + 1;
j = j + 1;
}
}
Yet, the contract is not complete since it does not state that the elements of the original arrays are in the resulting one.