0

Good day to all! I wrote the Shell sort verification code, but I can’t build the correct loop invariants.It is not possible to correctly compose invariants and prove the correctness of the program... Please help me!

/*@ predicate Sorted{L}(int* a, integer m, integer n) =
  @ \forall integer i, j; m <= i <= j < n ==> a[i] <= a[j];
*/
/*@ predicate GapSorted(int* a, integer m, integer n, integer gap) =
  @   \forall integer i, j; (m <= i <= j < n && j % gap == i % gap) ==> a[i] <=a[j];
*/
/*@
  @ requires \valid(arr + (0..n-1));
  @ requires n > 1;
  @ ensures GapSorted(arr, 0, n, 1);
*/
void shell_lr(int *arr, int n) {
int i, j, tmp, gap;
/*@ ghost int gap1 = n
  @ loop invariant 0 <= gap1 <= n/2;
  @ loop invariant gap1 < n/2 ==> GapSorted(arr, 0, n, gap+1);
  @ //loop invariant \forall integer k; gap < k <= n/2 ==> GapSorted(arr, 0, n, k);
  @ loop variant gap1;
*/
for (gap = n / 2; gap > 0; gap--) {
    /*@ loop invariant 0 <= i <= n;
       @ //loop invariant \forall integer m; gap < m <= n/2 ==> GapSorted(arr, 0, i, m);
       @ loop invariant GapSorted(arr, 0, i, gap);
       @ loop variant n - i; */
    for (i = gap; i < n; i++) {
        tmp = arr[i];
        /*@
          @ loop invariant 0 <= j <= i;
          @ //loop invariant arr[j] >= tmp;
          @ loop invariant \forall integer k; (j < k <= i) ==> GapSorted(arr, 0, i, k);
          @// loop invariant \forall integer k; j <= k <= gap ==> GapSorted(arr, k, i,               gap);
          @ loop variant j;
          @*/
        for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap) {
            arr[j] = arr[j - gap];
            //@ assert arr[j] >= arr[j - gap];
            //@ assert tmp < arr[j - gap];
        }
        //@ assert j>=0;
        arr[j] = tmp;
    }
    //@ assert i == n;
    //@ assert GapSorted(arr, 0, i, gap);
    //@ assert gap > 0;
    // assert GapSorted(arr, 0, n, gap);
    }
Virgile
  • 9,724
  • 18
  • 42
Gersa
  • 39
  • 6

1 Answers1

0

First, the code you provided is not syntactically correct:

  • a brace closing the function's body is missing
  • the ghost declaration of gap1 cannot be mixed with the loop annotations. This should be two distinct annotations. Anyway, I don't see the use of gap1 (especially as it is not updated inside the loop), everything can be expressed with gap. If what you were trying to achieve was to have a local variable for the whole loop annotation, this is not possible in ACSL: you have the \let gap1 = ...; ... construction, but its scope is only a single term/predicate: you can't share it with across two loop invariants (or loop invariants and loop variant)

Now, your most pressing issue here is the lack of loop assigns in your loop annotations. You must provide such clauses for all your loops, or WP will not be able to assume much about the state of the program after the loops (see for instance this answer for more detail). You might also want to strengthen a bit your invariant on i in the middle loop as gap <= i <= n, but this is a detail.

With the following loop assigns, most of your annotations gets proved (Frama-C 20.0 Calcium with -wp -wp-rte).

/*@ predicate Sorted{L}(int* a, integer m, integer n) =
  @ \forall integer i, j; m <= i <= j < n ==> a[i] <= a[j];
*/
/*@ predicate GapSorted(int* a, integer m, integer n, integer gap) =
  @   \forall integer i, j; (m <= i <= j < n && j % gap == i % gap) ==> a[i] <=a[j];
*/
/*@
  @ requires \valid(arr + (0..n-1));
  @ requires n > 1;
  @ ensures GapSorted(arr, 0, n, 1);
*/
void shell_lr(int *arr, int n) {
int i, j, tmp, gap;
/*@
  @ loop invariant 0 <= gap <= n/2;
  @ loop invariant gap < n/2 ==> GapSorted(arr, 0, n, gap+1);
  @ loop assigns gap, i, j, tmp, arr[0 .. n - 1];
  @ //loop invariant \forall integer k; gap < k <= n/2 ==> GapSorted(arr, 0, n, k);
  @ loop variant gap;
*/
for (gap = n / 2; gap > 0; gap--) {
    /*@ loop invariant gap <= i <= n;
       @ //loop invariant \forall integer m; gap < m <= n/2 ==> GapSorted(arr, 0, i, m);
       @ loop invariant GapSorted(arr, 0, i, gap);
       loop assigns i,j,tmp,arr[0..n-1];
       @ loop variant n - i; */
    for (i = gap; i < n; i++) {
        tmp = arr[i];
        /*@
          @ loop invariant 0 <= j <= i;
          @ //loop invariant arr[j] >= tmp;
          @ loop invariant \forall integer k; (j < k <= i) ==> GapSorted(arr, 0, i, k);
          @// loop invariant \forall integer k; j <= k <= gap ==> GapSorted(arr, k, i,               gap);
            loop assigns j, arr[gap .. i];
          @ loop variant j;
          @*/
        for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap) {
            arr[j] = arr[j - gap];
            //@ assert arr[j] >= arr[j - gap];
            //@ assert tmp < arr[j - gap];
        }
        //@ assert j>=0;
        arr[j] = tmp;
    }
    //@ assert i == n;
    //@ assert GapSorted(arr, 0, i, gap);
    //@ assert gap > 0;
    // assert GapSorted(arr, 0, n, gap);
    }
}

What remains to be proved are the GapSorted invariants for the two inner loops, which probably require more work and an answer much longer than what is fit for this format.

Virgile
  • 9,724
  • 18
  • 42
  • How to construct invariants and what to consider? I do not understand, please help. – Gersa Dec 21 '19 at 13:23
  • There's no general rule as how to construct invariants, and certainly nothing that can fit in the format of a SO comment. Very broadly, you have to answer a question like "how does each step of the loop contribute to progress towards the ultimate goal (i.e. the post-condition of the function)?" For the outer loop, this would indeed be GapSorted(arr,0,n,gap+1). For the loop in the middle, that everything up to `i` is `gap`-GapSorted seems also reasonable. For the inner loop, however, this is not true, since you're in the process of moving the old `arr[i]` to its appropriate place... – Virgile Dec 26 '19 at 09:47
  • ... you thus need an invariant indicating that `tmp` is less than `arr[j]`, in addition to the GapSorted predicate itself, which might be slightly more complicated than before, as when entering the loop you don't have `GapSorted(arr,0,i,gap)`, but only `GapSorted(arr,0,i-1,gap)` (the former becomes true after one loop step though). Please not that these explanations are based on a broad overview of your code and that I've not attempted any further proof. Feel free to open to open new questions if you still have some issues. – Virgile Dec 26 '19 at 09:54