- •Лабораторная работа №2.
- •Часть 1. Подготовка среды
- •Лабораторная работа №2.
- •Часть 2. Массивы.
- •Лабораторная работа №2.
- •Часть 3. Описание предикатов.
- •Лабораторная работа №2.
- •Часть 4. Обращение к разным состояниям переменных.
- •Лабораторная работа №2.
- •Часть 5. Описание функций, возвращающих значение. Леммы.
- •Лабораторная работа №2.
- •Часть 5. Призрачные переменные. Поведение функции.
- •Лабораторная работа №2.
- •Часть 6. Самостоятельное задание.
Лабораторная работа №2.
Часть 5. Призрачные переменные. Поведение функции.
Закройте редактор Gedit и терминал, в котором он был запущен.
Проделайте пункты 1, 2, 4 части 1. Вы должны оказаться в каталоге с вашей фамилией.
Создайте каталог для следующего примера:
mkdir GCD
Перейдите в этот катлог:
cd GCD
Создайте файл исходного кода и откройте его в редакторе Gedit:
Gedit GCD.java
Вставьте в файл следующий текст программы:
//@+ CheckArithOverflow = no
/* complements for non-linear integer arithmetic */
/*@ lemma distr_right:
@ \forall integer x y z; x*(y+z) == (x*y)+(x*z);
@*/
/*@ lemma distr_left:
@ \forall integer x y z; (x+y)*z == (x*z)+(y*z);
@*/
/*@ lemma distr_right_minus:
@ \forall integer x y z; x*(y-z) == (x*y)-(x*z);
@*/
/*@ lemma distr_left_minus:
@ \forall integer x y z; (x-y)*z == (x*z)-(y*z);
@*/
/*@ lemma mul_comm:
@ \forall integer x y; x*y == y*x;
@*/
/*@ lemma mul_assoc:
@ \forall integer x y z; x*(y*z) == (x*y)*z;
@*/
/*@ predicate divides(integer x, integer y) =
@ \exists integer q; y == q*x ;
@*/
/*@ lemma div_mod_property:
@ \forall integer x y;
@ x >=0 && y > 0 ==> x%y == x - y*(x/y);
@*/
/*@ lemma mod_property:
@ \forall integer x y;
@ x >=0 && y > 0 ==> 0 <= x%y && x%y < y;
@*/
/*@ predicate isGcd(integer a, integer b, integer d) =
@ divides(d,a) && divides(d,b) &&
@ \forall integer z;
@ divides(z,a) && divides(z,b) ==> divides(z,d) ;
@*/
/*@ lemma gcd_zero :
@ \forall integer a; isGcd(a,0,a) ;
@*/
/*@ lemma gcd_property :
@ \forall integer a b d q;
@ b > 0 && isGcd(b,a % b,d) ==> isGcd(a,b,d) ;
@*/
class Gcd {
/*@ requires x >= 0 && y >= 0;
@ behavior resultIsGcd:
@ ensures isGcd(x,y,\result) ;
@ behavior bezoutProperty:
@ ensures \exists integer a b; a*x+b*y == \result;
@*/
static int gcd(int x, int y) {
//@ ghost integer a = 1, b = 0, c = 0, d = 1;
/*@ loop_invariant
@ x >= 0 && y >= 0 &&
@ (\forall integer d ; isGcd(x,y,d) ==>
@ \at(isGcd(x,y,d),Pre)) &&
@ a*\at(x,Pre)+b*\at(y,Pre) == x &&
@ c*\at(x,Pre)+d*\at(y,Pre) == y ;
@ loop_variant y;
@*/
while (y > 0) {
int r = x % y;
//@ ghost integer q = x / y;
x = y;
y = r;
//@ ghost integer ta = a, tb = b;
//@ ghost a = c;
//@ ghost b = d;
//@ ghost c = ta - c * q;
//@ ghost d = tb - d * q;
}
return x;
}
}
Здесь вычисляется НОД двух чисел. Известно, что существует разложение НОД(x, y) = x*a + y*b. В примере выполняется проверка этого разложения. Но, т.к. сама постановка задачи не требует нахождения разложения НОД(x, y), то, чтобы не вмешиваться в код программы используются призрачные переменные. Такие переменные и все действия над ними видны только для верификатора. Для применения этого инструмента используется блок /*@ ghost d;*/, где d – это объявление переменной или какое-либо присваивание с использованием выражений, см. пример.
Также в этом примере в постусловии используется конструкция /*@ behavior id: requires e1; ensures e2;*/. Такая конструкция применяется, когда есть ветвление в возвращаемых результатах. Например: функция возвращает значение, или функция выбрасывает исключение. В данном примере это ветвление не такое явное, но все-таки реализовано таким способом. Также в данном примере в блоке behavior пропущено предусловие. Но в общем случае оно может присутствовать, и тогда оно будет являться дополнительным предусловием к конструкции requires метода. Тоже самое касается постусловия ensures в блоке behavior. Оно будет являть дополнительным постусловием к постусловию функции.
Сохраните файл.
Откройте его для верификации.
Убедитесь, что утверждения доказаны (леммы не обязательно будут доказаны).