ACSL-доказательство функции, которая проверяет, отсортирован ли массив в порядке возрастания или убывания

Я пытаюсь доказать правильность функции, которая проверяет, отсортирован ли массив в порядке возрастания/убывания или не отсортирован. Поведение должно возвращать -1, если отсортировано в порядке убывания, 1, если отсортировано в порядке возрастания, размера 1 или содержит одно и то же значение и 0, если нет отсортировано или пусто. Для запуска: Frama-c-gui -wp -wp-rte filename.c

#include "logics.h"
#include <stdio.h>

/*@
requires size > 0;
requires \valid (t +(0..size-1));
ensures \forall unsigned int i; 0 <= i < size ==> (t[i] == \old(t[i]));
ensures size == \old(size);
ensures \result == -1 || \result == 0 || \result == 1;
ensures is_sortedInc(t,size) ==> (\result == 1);
ensures is_sortedDec(t,size) ==> (\result == -1);
ensures not_sorted(t,size) ==> (\result == 0);
ensures size <= 0 ==> (\result == 0);
ensures size == 1 ==> (\result == 1);
assigns \nothing;
*/
int isSortedIncDec (int * t, unsigned int size){
    if (size <= 0){
        return 0;
    }
    else if (size == 1)
    {
        return 1;
    }
    else
    {
        unsigned int i = 0; 
        /*@
        loop assigns i;
        loop invariant 0 <= i <= size-1;
        loop invariant \forall unsigned int j; 0 <= j < i < size ==> t[j] == t[i];
        loop variant size - i;
        */
        while ( i < size - 1 && t[i] == t[i+1] )
        {
            i++;
        }

        if (i == size-1)
        {
            return 1;
        }

        else
        {
            if (t[i] < t[i+1])
            {   
                /*@
                loop assigns i;
                loop invariant 0 <= i <= size-1;
                loop invariant \forall unsigned int j; (0 <= j < i < size - 1) ==> (t[j] <= t[i]);
                loop variant size - i;
                */
                for (i+1; i < size-1; i++)
                {
                    if (t[i] > t[i+1])
                    {
                        return 0;
                    }
                }
                return 1;
            }

            if (t[i] > t[i+1])
            {   
                /*@
                loop assigns i;
                loop invariant 0 <= i <= size-1;
                loop invariant \forall unsigned int j; (0 <= j < i < size - 1) ==> (t[j] >= t[i]);
                loop variant size - i;
                */
                for(i+1 ; i < size-1; i++)
                {
                    if (t[i] < t[i+1])
                    {
                        return 0;
                    }
                }
                return -1;
            } 
        } 
    }
}

Вот логика.h:

#ifndef _LOGICS_H_
#define _LOGICS_H_
#include <limits.h>


/* Informal specification: 
    Returns -1 if an array 't' of size 'size' is sorted in decreasing order
    or 1 if it is sorted in increasing order or of size 1
    or 0 if it is either not sorted, empty or of negative size.
    Note that an array filled with only one value is considered sorted increasing order ie [42,42,42,42].
 */

/*@
lemma limits:
    \forall unsigned int i;  0 <= i <= UINT_MAX;

predicate is_sortedInc(int *t, unsigned int size) =
\forall unsigned int i,j; ( 0<= i <= j < size ) ==> t[i] <= t[j];

predicate is_sortedDec(int *t, unsigned int size) =
\forall unsigned int i,j; ( 0<= i <= j < size ) ==> t[i] >= t[j];


predicate not_sorted(int *t, unsigned int size)=
\exists unsigned int i,j,k,l; (0 <= i <= j <= k <= l < size) ==> (t[i] > t[j] && t[k] < t[l]);

*/

#endif

Проблема возникает из-за того, что Frama-c не может доказать постусловия:

ensures is_sortedInc(t,size) ==> (\result == 1);
ensures is_sortedDec(t,size) ==> (\result == -1);

Это ожидаемая проблема, так как предикаты перекрываются в случае массива, содержащего одно и то же значение, а это означает, что массив [42,42,42] может привести к тому, что оба предиката вернут true, что сбивает с толку Frama-c.

Я хотел бы изменить предикат is_sortedDec(t,size), чтобы выразить следующую идею: массив отсортирован по убыванию, и с гарантированным этим свойством существует как минимум 2 индекса x, y, таких как array[x] != array[y ].

Существуют два индекса x,y такие, что t[x] != [y] и массив отсортирован в порядке убывания для всех индексов.

Я пробовал что-то вроде этого:

predicate is_sortedDec(int *t, unsigned int size) =
\forall unsigned int i,j; ( 0<= i <= j < size ) 
==> (t[i] >= t[j] 
    && (\exists unsigned int k,l ; (0<= k <= l < size) ==> t[k] != t[j]) );

но Frama-c был не слишком доволен синтаксисом.

Любая идея о том, как я могу решить эту проблему? И, может быть, улучшает общее доказательство? Спасибо.


person Nanoboss    schedule 06.12.2019    source источник


Ответы (1)


Я не уверен, что вы подразумеваете под «Frama-C не слишком доволен синтаксисом». Ваш предикат выглядит синтаксически правильным для меня, а также для моей версии Frama-C.

Однако с семантической точки зрения действительно существует проблема: вы должны использовать не импликацию (==>), а союз (&&) под квантором существования. В противном случае любой size<=k<=l был бы свидетелем, удовлетворяющим формуле.

В более общем смысле вы почти всегда используете такие количественные определения, как \forall x, P(x) ==> Q(x) и \exists x, P(x) && Q(x). Действительно, первый читается как «для любого x, если P(x) выполняется, то Q(x) выполняется, а второй: «Я хочу найти x, проверяющий как P(x), так и Q(x). Если вы замените конъюнкцию импликацией, вы запросите x, такое, что если P(x) выполняется, то Q(x) также верно, чего можно достичь (по крайней мере, в классической логике), найдя x, для которого P(x) не выполняется.

Тем не менее, у автоматических пруверов часто возникают трудности с кванторами существования (потому что они в основном должны выставлять какое-то свидетельство для формулы), и, согласно вашей неофициальной спецификации, есть пара (k,l), которая очевидна: 0 и size-1. Конечно, для этого вам нужно добавить к вашему предикату условие, что size>=2, но вам это все равно нужно, иначе вы столкнетесь с той же проблемой, что оба предиката верны для одноэлементного массива. Кстати, вам, вероятно, также нужно добавить size>=1 в предикат is_sortedInc. В противном случае предикат будет истинным для size==0 (всеобщая квантификация по пустому набору значений всегда верна), но в этом случае ваша функция возвращает 0, так что соответствующее ensures не выполняется.

Я не проверял подробно ваши инварианты циклов, но они выглядят вполне разумными.

ОБНОВЛЕНИЕ Судя по вашему комментарию ниже, ваша новая версия предикатов по-прежнему вызывает некоторую путаницу в использовании соединителей и квантификаторов:

  • условия для самого size должны быть вне любого квантификатора.
  • В isSortedDec у вас должна быть импликация под фораллом и союз под экзистентом, который сам не должен быть под фораллом.

Подводя итог, предикаты должны выглядеть примерно так:

predicate is_SortedInc(int *t, unsigned int size) =
  size > 0 &&
  \forall unsigned int i,j; ( 0<= i <= j < size ) ==> t[i] <= t[j];

predicate is_sortedDec(int *t, unsigned int size) =
  size > 1 &&
  \forall unsigned int i,j; ( 0<= i <= j < size ) ==> t[i] >= t[j] &&
  \exists unsigned int i,j; (0<=i<j<size) && t[i] < t[j];

Кроме того, если вы удалите пост-условие not_sorted, то вы в основном позволите функции возвращать что угодно в этом случае, включая 1 или -1, так что вызывающий может ошибочно заключить, что массив отсортирован.

person Virgile    schedule 06.12.2019
comment
Благодарю. Это моя ошибка, так как я сделал опечатку при написании сказуемого. Не могли бы вы рассказать об isortedInc? Как и в предикате 0<= i <= j < size, размер‹=1. predicate is_sortedInc(int *t, unsigned int size) = \forall unsigned int i,j; ( 0<= i <= j < size && size >=1) ==> t[i] <= t[j]; predicate is_sortedDec(int *t, unsigned int size) = \forall unsigned int i,j; ( 0<= i <= j < size && size >= 2) && (t[i] >= t[j] && (\exists unsigned int k,l ; (0<= k <= l < size) ==> t[k] != t[j])); проверены, но теперь notSorted нет. Есть идеи, почему? Извините за уродливую каплю - person Nanoboss; 06.12.2019
comment
после дальнейшего исследования. Если я удалю все вместе, постусловие ensures not_sorted(t,size) ==> (\result == 0); frama-c будет считаться действительным. Поэтому мне было интересно, действительно ли это необходимое постусловие, поскольку мы уже знаем, что функция возвращает 1 или -1 при сортировке? - person Nanoboss; 07.12.2019
comment
@Nanoboss Я более подробно объяснил, как должен выглядеть предикат, насколько я понимаю, к чему вы стремитесь. Если у вас все еще есть проблема, я предлагаю открыть новый вопрос, иначе ответ может стать трудным для чтения. - person Virgile; 09.12.2019
comment
Спасибо, я сделаю так. - person Nanoboss; 10.12.2019