Найти множитель числа

#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;
  

приводит к автоматическому подтверждению постусловия.