So I have those two files.
testing.ads
package Testing with
SPARK_Mode
is
function InefficientEuler1Sum2 (N: Natural) return Natural;
procedure LemmaForTesting with
Ghost,
Post => (InefficientEuler1Sum2(0) = 0);
end Testing;
and testing.adb
package body Testing with
SPARK_Mode
is
function InefficientEuler1Sum2 (N: Natural) return Natural is
Sum: Natural := 0;
begin
for I in 0..N loop
if I mod 3 = 0 then
Sum := Sum + I;
end if;
if I mod 5 = 0 then
Sum := Sum + I;
end if;
if I mod 15 = 0 then
Sum := Sum - I;
end if;
end loop;
return Sum;
end InefficientEuler1Sum2;
procedure LemmaForTesting
is
begin
null;
end LemmaForTesting;
end Testing;
When I run SPARK -> Prove File, I get such message:
GNATprove
E:\Ada\Testing SPARK\search\src\testing.ads
10:14 medium: postcondition might fail
cannot prove InefficientEuler1Sum2(0) = 0
Why is that so? What I have misunderstood or maybe done wrong? Thanks in advance.
To prove the trivial equality, you need to make sure it is covered by the function's post-condition. If so, you can prove the equality using a simple Assert
statement as shown in the example below. No lemma is needed at this point.
However, a post-condition is not enough to prove the absence of runtime errors (AoRTE): given the allowable input range of the function, the summation may, for some values of N
, overflow. To mitigate the issue, you need to bound the input values of N
and show the prover that the value for Sum
remains bounded during the loop using a loop invariant (see here, here and here for some background information on loop invariants). For illustration purposes, I've chosen a conservative bound of (2 * I) * I
, which will severely restrict the allowable range of input values, but does allow the prover to proof the absence of runtime errors in the example.
testing.ads
package Testing with SPARK_Mode is
-- Using the loop variant in the function body, one can guarantee that no
-- overflow will occur for all values of N in the range
--
-- 0 .. Sqrt (Natural'Last / 2) <=> 0 .. 32767
--
-- Of course, this bound is quite conservative, but it may be enough for a
-- given application.
--
-- The post-condition can be used to prove the trivial equality as stated
-- in your question.
subtype Domain is Natural range 0 .. 32767;
function Inefficient_Euler_1_Sum_2 (N : Domain) return Natural
with Post => (if N = 0 then Inefficient_Euler_1_Sum_2'Result = 0);
end Testing;
testing.adb
package body Testing with SPARK_Mode is
-------------------------------
-- Inefficient_Euler_1_Sum_2 --
-------------------------------
function Inefficient_Euler_1_Sum_2 (N : Domain) return Natural is
Sum: Natural := 0;
begin
for I in 0 .. N loop
if I mod 3 = 0 then
Sum := Sum + I;
end if;
if I mod 5 = 0 then
Sum := Sum + I;
end if;
if I mod 15 = 0 then
Sum := Sum - I;
end if;
-- Changed slightly since initial post, no effect on Domain.
pragma Loop_Invariant (Sum <= (2 * I) * I);
end loop;
return Sum;
end Inefficient_Euler_1_Sum_2;
end Testing;
main.adb
with Testing; use Testing;
procedure Main with SPARK_Mode is
begin
pragma Assert (Inefficient_Euler_1_Sum_2 (0) = 0);
end Main;
output
$ gnatprove -Pdefault.gpr -j0 --level=1 --report=all
Phase 1 of 2: generation of Global contracts ...
Phase 2 of 2: flow analysis and proof ...
main.adb:5:19: info: assertion proved
testing.adb:13:15: info: division check proved
testing.adb:14:24: info: overflow check proved
testing.adb:16:15: info: division check proved
testing.adb:17:24: info: overflow check proved
testing.adb:19:15: info: division check proved
testing.adb:20:24: info: overflow check proved
testing.adb:20:24: info: range check proved
testing.adb:23:33: info: loop invariant preservation proved
testing.adb:23:33: info: loop invariant initialization proved
testing.adb:23:42: info: overflow check proved
testing.adb:23:46: info: overflow check proved
testing.ads:17:19: info: postcondition proved
Summary logged in /obj/gnatprove/gnatprove.out
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With