Frama-c 希尔排序算法验证

问题描述 投票:0回答:1

我不明白为什么frama-c不能证明这部分的shell排序算法。我将 Astraver 与 Alt-Ergo 2.4.3 和 CVC v1.8 一起使用,它们将最后一个循环中的条件标记为证明问题。怎么了?

#include <stdlib.h>
#include <string.h>

/*@ predicate ArraysEquality{L}(int *a, int *b, integer m, integer n) =  
  @ \forall integer k; m <= k < n ==> a[k] == b[k];
  @
  @ predicate Sorted{L}(int *a, integer n) =
  @     \forall integer i, j; 0 <= i <= j < n ==> a[i] <= a[j];
  @
  @ predicate CopiedArray(int *dest, int *src, integer n) =
  @   \forall integer i; 0 <= i < n ==> dest[i] == src[i];
  @
  @ predicate Swap{L1, L2}(int *a, integer n, integer i, integer j) =
  @ 0 <= i < n && 0 <= j < n &&
  @ \at(a[i], L1) == \at(a[j], L2) &&
  @ \at(a[j], L1) == \at(a[i], L2) &&
  @ \forall integer k; 0 <= k < n && k != i && k != j
  @     ==> \at(a[k], L1) == \at(a[k], L2);
  @
  @ inductive Permutation{L1, L2}(int *a, integer n) {
  @     case reflexive{L1}:
  @     \forall int *a, integer n;
  @         Permutation{L1, L1}(a, n);
  @
  @     case transitive{L1, L2, L3}:
  @     \forall int *a, integer n;
  @         Permutation{L1, L2}(a, n) && Permutation{L2, L3}(a, n)
  @             ==> Permutation{L1, L3}(a, n);
  @
  @ case swapstep{L1, L2}:
  @     \forall int *a, integer n, i, j;
  @         Swap{L1, L2}(a, n, i, j)
  @             ==> Permutation{L1, L2}(a, n);  
  @ }
  @
  @*/
  
/* lemma subarray_of_copy:
    \forall int *a, *b, integer n, l, r; 
        0 <= l < r < n && CopiedArray(a, b, n) ==> ArraysEquality(a, b, l, r);
  */
  
/* lemma sorted_continuation:
    \forall int *a, integer n, i;
        1 <= i < n && Sorted(a, i) && a[i - 1] <= a[i]
            ==> Sorted(a, i);
  
  */

/*@ requires \valid(dest + (0..n-1));
  @ requires \valid(src + (0..n-1));
  @ requires n > 1;
  @ assigns dest[0..n-1];
  @ ensures CopiedArray(dest, src, n);
  @*/
void copyArray(int* dest, const int* src, size_t n) {
    memcpy(dest, src, n * sizeof(int));
}

/*@ requires \valid(arr + (0..n-1));
  @ requires n > 1;
  @ requires 0 < gap < n;
  @ //ensures Swap{Pre, Post}(arr, n, i, i - gap);
  @ //ensures Permutation{Pre, Post}(arr, n);
  @*/
void fun(int *arr, int n, int gap, int i) {
    int tmp, j;
    //@ ghost int tmp_for_copy;
    //@ ghost int *arr_copy = malloc(n * sizeof(int));
    //@ ghost copyArray(arr_copy, arr, n);
    //@ assert CopiedArray(arr_copy, arr, n);
    //@ assert CopiedArray(arr_copy, arr, n) ==> ArraysEquality(arr, arr_copy, 0, n);

    //@ ghost beforeEntry:
    /*@ loop invariant gap <= i <= n;
      @ //loop invariant gap == 1 ==> Sorted(arr, 0, i);
      @ //loop invariant Permutation{Pre, Here}(arr, n);
      @ //loop invariant ArraysEquality(arr, arr_copy, 0, i);
      @ loop assigns i, j, tmp, arr[0..n-1];
      @ loop variant n - i;
      @*/
    for (i = gap; i < n; ++i) {
        tmp = arr[i];
        // assert ArraysEquality(arr, arr_copy, 0, i);
        //@ ghost int counter = 0;
        
        /*@ loop invariant gap <= i < n;
          @ loop invariant 0 <= j <= i;
          @ loop invariant j != i ==> arr[j] > tmp && arr[j] == arr[j + gap];
          @ loop invariant j == i ==> arr[j] == tmp; 
          @ loop invariant Permutation{LoopEntry, Here}(arr_copy, n);
          @ loop invariant Permutation{LoopEntry, Here}(arr_copy, n) ==> Permutation{beforeEntry, Here}(arr_copy, n);
          @ loop assigns j, arr[0..i], arr_copy[0..i], tmp_for_copy;
          @ loop variant i - counter;
          @*/
        for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap) {
            //@ ghost counter = counter + 1;
            //@ assert j >= gap && j - gap >= 0;
            arr[j] = arr[j - gap];      
            //@ assert arr[j] > tmp;
                // assert arr[j - gap] > tmp;
                // assert arr[j] == arr[j - gap];
                
                //@ ghost beforeSwap:
            //@ ghost tmp_for_copy = arr_copy[j];
            //@ ghost arr_copy[j] = arr_copy[j - gap];
            //@ ghost arr_copy[j - gap] = tmp_for_copy;
            //@ assert Swap{beforeSwap, Here}(arr_copy, n, j, j - gap);
            //@ assert Swap{beforeSwap, Here}(arr_copy, n, j, j - gap) ==> Permutation{beforeSwap, Here}(arr_copy, n);
            //@ assert Permutation{beforeSwap, Here}(arr_copy, n) ==> Permutation{LoopEntry, Here}(arr_copy, n);
            // assert ArraysEquality(arr, arr_copy, j - gap + 1, i);
            //@ assert j >= gap;
        }
        // assert ArraysEquality(arr, arr_copy, j + 1, i);    
        arr[j] = tmp;
        //@ assert j >= 0;
        // assert ArraysEquality(arr, arr_copy, j, i);
        //@ assert Permutation{beforeEntry, Here}(arr_copy, n);
    }
}

如果您删除与排列和幽灵变量相关的注释以创建

arr

 数组的副本,以便通过它检查 
arr
 的排列,
Frama-c 会用绿色勾号标记循环不变持久性检查。但如果你看一下报告,即使它被标记为已证实,但嵌套循环中带有条件的部分仍然被标记为红色。

c sorting verification frama-c
1个回答
0
投票

希望以下内容对您有所帮助。它是使用 Spark 而不是 Frama-C 编写的,但可能会给您一些关于断言和循环不变量需要什么的想法。

我在Spark包中创建了shell排序功能,它将排序过程与排序过程的实现分离。

包规范包含排序过程的API,包括要排序的数组类型的规范和排序过程的后置条件。

package shell_sort with
   SPARK_Mode
is
   type idx_t is range 0 .. 9;
   type arr_t is array (Idx_t) of Natural;
   procedure sort (arr : in out arr_t) with
     SPARK_Mode,
      Post =>
      (for all I in arr'First .. arr'Last - 1 => arr (I) <= arr (I + 1)) and then
      (for some I in arr'Old'Range =>
         (for some J in arr'Range => arr'Old (I) = arr (J)));
end shell_sort;

后置条件指出,对于数组中从第一个元素到最后一个元素的前一个元素的所有元素,每个元素都小于或等于其下一个元素,并且排序数组中的每个元素也在未排序数组中.

包体包含排序过程的实现。

package body shell_sort with
   SPARK_Mode
is
   procedure sort (arr : in out arr_t) with
      SPARK_Mode
   is
      procedure swap (left, right : in out Natural) with
         Post => left'Old = right and right'Old = left
      is
         temp : Natural := left;
      begin
         left  := right;
         right := temp;
      end swap;

      original : arr_t := arr;
      function found(value : natural) return boolean with
        Post =>
          ( if (for some I in original'range => original(I) = value) then
              found'result = True
                else
                  found'result = False) is
         result : boolean := false;
      begin
         if (for some I in original'range => original(I) = value) then
            result := true;
         end if;
         return result;
      end found;

   begin
      loop
         pragma Loop_Invariant
           (for all I in arr'range => found(arr(I)));
         for I in arr'First .. arr'Last - 1 loop
            if arr (I) > arr (I + 1) then
               swap (arr (I), arr (I + 1));
               pragma Assert (arr (I) < arr (I + 1));
            end if;
            pragma Assert (arr (I) <= arr (I + 1));
         end loop;
         if (for all I in arr'First .. arr'Last - 1 => arr (I) <= arr (I + 1))
         then
            exit;
         end if;
      end loop;
   end sort;
end shell_sort;

Spark 允许在其他函数和过程的范围内定义函数和过程。此示例实现了交换过程和查找函数,以简化排序过程中的逻辑并简化第一个循环不变式。

名为 GnatProve 的 Spark 证明器的输出为:

Summary of SPARK analysis
=========================

-------------------------------------------------------------------------------------------------------------
SPARK Analysis results        Total       Flow   CodePeer                      Provers   Justified   Unproved
-------------------------------------------------------------------------------------------------------------
Data Dependencies                 .          .          .                            .           .          .
Flow Dependencies                 .          .          .                            .           .          .
Initialization                    .          .          .                            .           .          .
Non-Aliasing                      1          1          .                            .           .          .
Run-time Checks                   .          .          .                            .           .          .
Assertions                        4          .          .          4 (CVC4 95%, Z3 5%)           .          .
Functional Contracts              3          .          .    3 (CVC4 88%, Trivial 13%)           .          .
LSP Verification                  .          .          .                            .           .          .
Termination                       .          .          .                            .           .          .
Concurrency                       .          .          .                            .           .          .
-------------------------------------------------------------------------------------------------------------
Total                             8    1 (13%)          .                      7 (88%)           .          .


max steps used for successful proof: 9816

Analyzed 2 units
in unit main, 0 subprograms and packages out of 1 analyzed
  Main at main.adb:5 skipped
in unit shell_sort, 4 subprograms and packages out of 4 analyzed
  shell_sort at shell_sort.ads:1 flow analyzed (0 errors, 0 checks and 0 warnings) and proved (0 checks)
  shell_sort.sort at shell_sort.ads:6 flow analyzed (0 errors, 0 checks and 0 warnings) and proved (5 checks)
  shell_sort.sort.found at shell_sort.adb:17 flow analyzed (0 errors, 0 checks and 0 warnings) and proved (1 checks)
  shell_sort.sort.swap at shell_sort.adb:7 flow analyzed (0 errors, 0 checks and 0 warnings) and proved (1 checks)
© www.soinside.com 2019 - 2024. All rights reserved.