#ada #formal-verification #spark-ada #spark-2014
#ada #формальная проверка #spark-ada #spark-2014 #искровая формальная проверка
Вопрос:
Я хочу найти наименьший коэффициент значения с приведенной ниже спецификацией
procedure S_Factor (N : in out Positive; Factor : out Positive) with
SPARK_Mode,
Pre => N > 1,
Post => (Factor > 1) and
(N'Old / Factor = N) and
(N'Old rem Factor = 0) and
(for all J in 2 .. Factor - 1 => N'Old rem J /= 0)
is
begin
...
end S_Factor;
Я написал тело процедуры, пытаясь охватить все утверждения, но всегда одно из условий post завершается неудачей…
procedure S_Factor (N : in out Positive; Factor : out Positive) with
SPARK_Mode,
Pre => N > 1,
Post => (Factor > 1) and
(N'Old / Factor = N) and
(N'Old rem Factor = 0) and
(for all J in 2 .. Factor - 1 => N'Old rem J /= 0)
is
begin
Factor := N;
for J in 2 .. Factor loop
if N rem J /= 0 then
null;
else
Factor := J;
N := N / Factor;
exit;
end if;
end loop;
end S_Factor ;
Что я делаю не так? Может ли кто-нибудь помочь мне пройти через все утверждения из спецификации?
Комментарии:
1. Нужно ли использовать а затем вместо и для обеспечения функциональности короткого замыкания? Я не так хорошо знаком со SPARK, но в Ada вы знакомы.
2. @Jere, @john-perry и я обнаружили, что это
and then
вызвало проблемы с условиями в защищенных подпрограммах.3. @SimonWright Это была ошибка или что-то, определенное реализацией? Я не выполнял много сложных условий публикации, поэтому эта тема интересна (или, если есть ссылка на проблему, это тоже нормально, все, что вам проще).
4. @Jere,
and then
означает, что та или иная сторона может не быть оценена; это противоречит последнему предложению ARM 6.1.1(27) : «Префикс старого attribute_reference, который потенциально не оценен, должен статически обозначать сущность» (нет, я этого тоже не понимаю 🙂5. @SimonWright Спасибо!
Ответ №1:
Я не уверен, что вы подразумеваете под условием post N'Old / Factor = N
, но показанная ниже подпрограмма Smallest_Factor
(которая также могла быть написана как чистая функция) подтверждается в GNAT CE 2018 и может помочь вам:
package Foo with SPARK_Mode is
procedure Smallest_Factor
(Number : in Positive;
Factor : out Positive)
with
Pre => (Number > 1),
Post => (Factor in 2 .. Number)
and then (Number rem Factor = 0)
and then (for all J in 2 .. Factor - 1 => Number rem J /= 0);
end Foo;
с телом
package body Foo with SPARK_Mode is
procedure Smallest_Factor
(Number : in Positive;
Factor : out Positive)
is
begin
Factor := 2;
while (Number rem Factor) /= 0 loop
pragma Loop_Invariant
(Factor < Number);
pragma Loop_Invariant
(for all J in 2 .. Factor => (Number rem J) /= 0);
Factor := Factor 1;
end loop;
end Smallest_Factor;
end Foo;
Небольшой тестовый запуск:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Foo;
procedure Main is
Factor : Positive;
begin
for Number in 2 .. 20 loop
Foo.Smallest_Factor (Number, Factor);
Put (" Number : "); Put (Number, 2);
Put (" Factor : "); Put (Factor, 2);
New_Line;
end loop;
end Main;
Шоу
Number : 2 Factor : 2
Number : 3 Factor : 3
Number : 4 Factor : 2
Number : 5 Factor : 5
Number : 6 Factor : 2
Number : 7 Factor : 7
Number : 8 Factor : 2
Number : 9 Factor : 3
Number : 10 Factor : 2
Number : 11 Factor : 11
Number : 12 Factor : 2
Number : 13 Factor : 13
Number : 14 Factor : 2
Number : 15 Factor : 3
Number : 16 Factor : 2
Number : 17 Factor : 17
Number : 18 Factor : 2
Number : 19 Factor : 19
Number : 20 Factor : 2
Комментарии:
1. Приятно! Я обнаружил, что для этого не нужны
and then
вwhile
строке или инвариант первого цикла. Я полагаю, что наличие цикла с ранним завершением делает рассуждения gnatprove такими же сложными, как и для человека.2. @SimonWright — Да, вы правы! Я думаю, что (неявное) условие post для
rem
оператора уже доказывает инвариантность циклаFactor < Number
. В этом случае явное условие цикла, которое ограничиваетFactor
, не требуется. Я обновил код, а также немного обновил условие post: диапазонFactor
также должен быть ограничен сверху. Наибольшее возможное значение дляFactor
равноNumber
(когдаNumber
является простым). Спасибо!3. Спасибо, это действительно помогло. Я добавляю N: = N / Factor; в конечной программе, потому что моей программе это тоже нужно для передачи (N ‘Old / Factor = N) и
Ответ №2:
Хорошей идеей будет как можно чаще использовать систему типов для принудительного выполнения предварительных и постусловий. Упрощаем ваш пример до
package Factoring with SPARK_Mode is
subtype Includes_Primes is Integer range 2 .. Integer'Last;
procedure S_Factor (N : in out Includes_Primes; Factor : out Includes_Primes) with
Post => N'Old / Factor = N and
N'Old rem Factor = 0 and
(for all J in 2 .. Factor - 1 => N'Old rem J /= 0);
завершение факторинга;
package body Factoring is
procedure S_Factor (N : in out Includes_Primes; Factor : out Includes_Primes) is
-- Empty
begin -- S_Factor
Search : for I in 2 .. N loop
if N rem I = 0 then
Factor := I;
N := N / 1;
return;
end if;
end loop Search;
end S_Factor;
end Factoring;
приводит к автоматическому подтверждению постусловия.