How to use different fix point types in mathematical operations in Ada? - ada

For the program at the end I get the following error messages from gnat:
test2.adb:23:61: error: invalid operand types for operator "-"
test2.adb:23:61: error: left operand has type "Gain_Type" defined at line 11
test2.adb:23:61: error: right operand has type "Offset_Type" defined at line 12
Unfortunately I did not find a good example how to resolve this in a way resulting in speed optimized code for rather small embedded targets.
Always casting everything to the biggest type does not make that much sense I feel.
What is the best way to do that/ isn't there a good reference existing how to efficiently use fixed point for a bit more complicated mathematical problems?
procedure Test2 is
Adc_Width : constant Positive := 10;
Adc_Delta : constant Float := 2.0**(-Adc_Width);
Adc_Mod : constant := 2**Adc_Width;
Error_Delta : constant := 2.0**(-1);
Gain_Min : constant Float := 1.0 - 2.0 * Adc_Delta;
Gain_Max : constant Float := 1.0 + 2.0 * Adc_Delta;
Offset_Min : constant Float := -0.5 * Adc_Delta;
Offset_Max : constant Float := 2.0 * Adc_Delta;
type Gain_Type is delta Adc_Delta * Error_Delta range Gain_Min .. Gain_Max;
type Offset_Type is
delta Adc_Delta * Error_Delta range Offset_Min .. Offset_Max;
type Adc_Encoded_Type is mod Adc_Mod with
Size => 16;
subtype Adc_Value_Type is natural range 0 .. Adc_Encoded_Type'Modulus - 1;
type Adc_Delta_Type is delta Adc_Delta range 0.0 .. 1.0 - Adc_Delta;
function Compensate
(Adc : in Adc_Encoded_Type; Gain : in Gain_Type; Offset : in Offset_Type)
return Adc_Delta_Type
is
begin
return Adc_Delta_Type (((Adc_Value_Type (Adc) * Gain) - Offset) / Adc_Mod);
end Compensate;
begin
end Test2;

If Gain_Type and Offset_Type are physically compatible, you could make them subtypes of a common type since they have the same delta.
procedure Test2 is
Adc_Width : constant := 10;
Adc_Delta : constant := 2.0**(-Adc_Width);
Adc_Mod : constant := 2**Adc_Width;
Error_Delta : constant := 2.0**(-1);
Gain_Min : constant := 1.0 - 2.0 * Adc_Delta;
Gain_Max : constant := 1.0 + 2.0 * Adc_Delta;
Offset_Min : constant := -0.5 * Adc_Delta;
Offset_Max : constant := 2.0 * Adc_Delta;
--
type Super_Type is delta Adc_Delta * Error_Delta range -1.0 .. 2.0;
subtype Gain_Type is Super_Type range Gain_Min .. Gain_Max;
subtype Offset_Type is Super_Type range Offset_Min .. Offset_Max;
--
type Adc_Encoded_Type is mod Adc_Mod with
Size => 16;
subtype Adc_Value_Type is natural range 0 .. Adc_Encoded_Type'Modulus - 1;
type Adc_Delta_Type is delta Adc_Delta range 0.0 .. 1.0 - Adc_Delta;
function Compensate
(Adc : in Adc_Encoded_Type; Gain : in Gain_Type; Offset : in Offset_Type)
return Adc_Delta_Type
is
begin
return Adc_Delta_Type (((Adc_Value_Type (Adc) * Gain) - Offset) / Adc_Mod);
end Compensate;
begin
null;
end Test2;
BTW, I have removed the types in the constants in order to remove rounding errors.

Related

Why is it giving me this error? This expression has type Z.t but an expression was expected of type int

EDITED: Alright, this is the complete error :
45 | | _ -> Z.of_int 3 * Z.(s1 (num-1)) + Z.(sum_s1 num)
^^^^^^^^^^
Error: This expression has type Z.t but an expression was expected of type
int
And this is the code in question :
let rec s1 num =
match num with
| 0 -> Z.of_int(1)
| 1 -> Z.of_int(2)
| _ -> Z.of_int 3 * Z.(s1 (num-1)) + Z.(sum_s1 num)
and
sum_s1 num =
let rec sum_s1_impl (num, k) =
if (num-2 < 1) || (k > num-2) then 0
else (s1 k) * (s1 (num-k-1)) + (sum_s1_impl (num, k+1))
in sum_s1_impl (num, 1);;
I don't know where is the problem/how can I fix it (some tips)
Thanks!!
EDIT#2 :
let rec s1 num =
match num with
| 0 -> Z.of_int(1)
| 1 -> Z.of_int(2)
| _ -> Z.of_int(3 * (s1 (num-1)) + (sum_s1 num))
and
sum_s1 num =
let rec sum_s1_impl (num, k) =
if (num-2 < 1) || (k > num-2) then 0
else (s1 k) * (s1 (num-k-1)) + (sum_s1_impl (num, k+1))
in sum_s1_impl (num, 1);;
Even with the use of Z.of_int(3 * (s1 (num-1)) + (sum_s1 num))
I stil get the same error
Generally speaking you need to carefully track which of your parameters are of type int (ordinary OCaml integer) and which are Z.t (big integer). You seem to treat them as if they're the same type, which doesn't work in a strongly typed language.
The first reported error is for this expression:
Z.of_int 3 * Z.(s1 (num-1)) + Z.(sum_s1 num)
If I look at the code for s1 it shows that it expects an int parameter, since it matches the parameter against 0, 1, etc. Similarly, the code for sum_s1 expects an int parameter since it applies the built-in - operator to the parameter.
With these assumptions, the first problem in this expression is that Z.of_int returns a big integer (Z.t). You can't multiply a big integer using the built-in * operator.
But note that this subexpression looks wrong also:
Z.(s1 (num - 1))
Since the expression is prefixed with Z., the operators will come from the Z module. Hence the - is of type Z.t -> Z.t -> Z.t. But you're applying it to num and 1 which are ordinary OCaml ints.
You need to go through the expressions and figure out the type you want for each subpart. Generally you want to do everything using big integers, so you should convert using Z.of_int whenever you have a regular OCaml int. Most of the parameters and return values of your functions should (in my opinion) be big integers.
You are multiplying non int type Z.of_int 3 by an int (* operator).
Try to do your operations with ints, and then convert the end result to Z.of_int (your result)
EDIT : Also, you can use built-in zerith operators, ie :
val add : t -> t -> t
Addition.
Check https://www-apr.lip6.fr/~mine/enseignement/l3/2015-2016/doc-zarith/Z.html

How to perform arithmetic contract operations on function taking in 2D array type as parameter in Ada

I have a function that should return the count of Islands found.
I name this function Count_Islands that takes in a parameter of
Map_Array of type Map, of which Map is an array of Islands.
Islands is an enumerator type with set of Land, Water.
I have the function specification in the .ads and the body in the
.adb
The problem I face now is how to proof that my function
Count_Islands'Result will be less than (X * Y)
I have tried: with post => Count_Islands'Result < X * Y
-- Whenever I ran prove all I got: medium: postcondition might
fail cannot prove Count_Islands'Result < X * Y
Function in .ads:
function Count_Islands(Map_Array : Map)
return Integer with Pre => Map_Array'Length /= 0,
Post => Count_Islands'Result < X * Y;
Function in .adb:
function Count_Islands(Map_Array : Map) return Integer
is
Visited_Array : Visited := (others => (others=> False));
Count : Integer := 0;
begin
if (Map_Array'Length = 0)then
return 0;
end if;
for i in X_Range loop
for j in Y_Range loop
if (Map_Array(i, j) = Land and then not Visited_Array(i,j)) then
Visited_Array := Visit_Islands(Map_Array, i, j,Visited_Array);
Count := Count + 1;
end if;
end loop;
end loop;
return Count;
end Count_Islands;
In a matrix of 4 * 5 for instance,i.e my X = 4 And Y = 5:
I expect the output result of an Islands(Lands) found to be 1 which is less than 4 * 5. But GNATprove cannot prove my initial code to analyze that,using Post => Count_Islands'Result < X * Y;
Is there any better way to prove this arithmetic? Thanks for your help.
As the example is not complete, I took the liberty to change it a little bit. You can prove the post condition by adding loop invariants. The program below proves in GNAT CE 2019:
main.adb
procedure Main with SPARK_Mode is
-- Limit the range of the array indices in order to prevent
-- problems with overflow, i.e.:
--
-- Pos'Last * Pos'Last <= Natural'Last
--
-- Hence, as Natural'Last = 2**31 - 1,
--
-- Pos'Last <= Sqrt (2**31 - 1) =approx. 46340
--
-- If Pos'Last >= 46341, then overflow problems might occur.
subtype Pos is Positive range 1 .. 46340;
type Map_Item is (Water, Land);
type Map is
array (Pos range <>, Pos range <>) of Map_Item;
type Visited is
array (Pos range <>, Pos range <>) of Boolean;
function Count_Islands (Map_Array : Map) return Natural with
Post => Count_Islands'Result <= Map_Array'Length (1) * Map_Array'Length (2);
-------------------
-- Count_Islands --
-------------------
function Count_Islands (Map_Array : Map) return Natural is
Visited_Array : Visited (Map_Array'Range (1), Map_Array'Range (2)) :=
(others => (others => False));
Count : Natural := 0;
begin
for I in Map_Array'Range (1) loop
pragma Loop_Invariant
(Count <= (I - Map_Array'First (1)) * Map_Array'Length (2));
for J in Map_Array'Range (2) loop
pragma Loop_Invariant
(Count - Count'Loop_Entry <= J - Map_Array'First (2));
if Map_Array(I, J) = Land and then not Visited_Array(I, J) then
Visited_Array (I, J) := True; -- Simplified
Count := Count + 1;
end if;
end loop;
end loop;
return Count;
end Count_Islands;
begin
null;
end Main;

SPARK Integer overflow check

I have the following program:
procedure Main with SPARK_Mode is
F : array (0 .. 10) of Integer := (0, 1, others => 0);
begin
for I in 2 .. F'Last loop
F (I) := F (I - 1) + F (I - 2);
end loop;
end Main;
If I run gnatprove, I get the following result, pointing to the + sign:
medium: overflow check might fail
Does this mean that F (I - 1) could be equal to Integer'Last, and adding anything to that would overflow? If so, then is it not clear from the flow of the program that this is impossible? Or do I need to specify this with a contract? If not, then what does it mean?
A counterexample shows that indeed gnatprove in this case worries about the edges of Integer:
medium: overflow check might fail (e.g. when F = (1 => -1, others => -2147483648) and I = 2)
Consider adding a loop invariant to your code. The following is an example from the book "Building High Integrity Applications with Spark".
procedure Copy_Into(Buffer : out Buffer_Type;
Source : in String) is
Characters_To_Copy : Buffer.Count_Type := Maximum_Buffer_Size;
begin
Buffer := (Others => ' '); -- Initialize to all blanks
if Source'Length < Characters_To_Copy then
Characters_To_Copy := Source'Length;
end if;
for Index in Buffer.Count_Type range 1..Characters_To_Copy loop
pragma Loop_Invariant
(Characters_To_Copy <= Source'Length and
Characters_To_Copy = Characters_To_Copy'Loop_Entry);
Buffer (Index) := Source(Source'First + (Index - 1));
end loop;
end Copy_Into;
This is already an old question, but I would like to add an answer anyway (just for future reference).
With the advancement of provers, the example as stated in the question now proves out-the-box in GNAT CE 2019 (i.e. no loop invariant needed). A somewhat more advanced example can also be proven:
main.adb
procedure Main with SPARK_Mode is
-- NOTE: The theoretical upper bound for N is 46 as
--
-- Fib (46) < 2**31 - 1 < Fib (47)
-- 1_836_311_903 < 2_147_483_647 < 2_971_215_073
-- NOTE: Proved with Z3 only. Z3 is pretty good in arithmetic. Additional
-- options for gnatprove:
--
-- --prover=Z3 --steps=0 --timeout=10 --report=all
type Seq is array (Natural range <>) of Natural;
function Fibonacci (N : Natural) return Seq with
Pre => (N in 2 .. 46),
Post => (Fibonacci'Result (0) = 0)
and then (Fibonacci'Result (1) = 1)
and then (for all I in 2 .. N =>
Fibonacci'Result (I) = Fibonacci'Result (I - 1) + Fibonacci'Result (I - 2));
---------------
-- Fibonacci --
---------------
function Fibonacci (N : Natural) return Seq is
F : Seq (0 .. N) := (0, 1, others => 0);
begin
for I in 2 .. N loop
F (I) := F (I - 1) + F (I - 2);
pragma Loop_Invariant
(for all J in 2 .. I =>
F (J) = F (J - 1) + F (J - 2));
-- NOTE: The loop invariant below helps the prover to proof the
-- absence of overflow. It "reminds" the prover that all values
-- from iteration 3 onwards are strictly monotonically increasing.
-- Hence, if absence of overflow is proven in this iteration,
-- then absence is proven for all previous iterations.
pragma Loop_Invariant
(for all J in 3 .. I =>
F (J) > F (J - 1));
end loop;
return F;
end Fibonacci;
begin
null;
end Main;
This loop invariant should work - since 2^(n-1) + 2^(n-2) < 2^n - but I can't convince the provers:
procedure Fibonacci with SPARK_Mode is
F : array (0 .. 10) of Natural := (0 => 0,
1 => 1,
others => 0);
begin
for I in 2 .. F'Last loop
pragma Loop_Invariant
(for all J in F'Range => F (J) < 2 ** J);
F (I) := F (I - 1) + F (I - 2);
end loop;
end Fibonacci;
You can probably convince the provers with a bit of manual assistance (showing how 2^(n-1) + 2^(n-2) = 2^(n-2) * (2 + 1) = 3/4 * 2^n < 2^n).

Backtracking in Pascal: finding maximal weighted branch

I've been learning Pascal (using the Free Pascal compiler) for a week now, and came across a seemingly easy exercise. Given a structure as shown below, find the maximal weighted branch:
1
4 9
7 0 2
4 8 6 3
A branch is any sequence of numbers, starting from the top (in this case: 1), when for every number the branch can expand either down-left or down-right. For example, the branch 1-4-0 can expand into either 1-4-0-8 or 1-4-0-6. All branches must start from the top and end at the bottom.
In this example, the maximal branch is 1-4-7-8, which gives us 20. In order to solve this question, I tried to use backtracking. The triangular structure was stored in an array of type 'triangle':
type triangle = array[1..MAX_NUM_OF_ROWS, 1..MAX_NUM_OF_ROWS] of integer;
Here's my implementation:
function findAux(data: triangle; dim: integer; i: integer; j:integer) : integer;
begin
if i = dim then
findAux := data[i][j]
else
if findAux(data, dim, i + 1, j + 1) > findAux(data, dim, i + 1, j) then
findAux := data[i+1][j+1] + findAux(data, dim, i + 1, j + 1);
else
findAux := data[i+1][j] + findAux(data, dim, i + 1, j);
end;
function find_heaviest_path(data: triangle; dim: integer) : integer;
begin
find_heaviest_path := findAux(data, dim, 1, 1);
end;
As you can see, I've used an auxiliary function. Unfortunately, it doesn't seem to give the right result. For the structure seen above, the result I get is 27, which is 7 points off. What am I missing? How does the implementation look overall? I should add that the maximal number of rows is 100, for this exercise. If clarification is needed, please don't hesitate to ask.
Your findAux is adding the wrong value to the recursively obtained result. As an aside, you can neaten the code a bit using some local variables. A corrected version of findAux:
uses math;
...
function findAux(data: triangle; dim: integer; i: integer; j:integer) : integer;
var
left, right: integer;
begin
if i = dim then
findAux := data[i][j]
else begin
left := findAux(data, dim, i + 1, j);
right := findAux(data, dim, i + 1, j + 1);
findAux := data[i][j] + Max(left, right);
end;
end;

PL/SQL float addition

I would like to add a value to a float but it's not working.
I have a float, like 3.14 and I want to add 0.005 but I get 3.14.
The code :
create or replace package pck_test is
PROCEDURE ArrondiGeo(coord_x IN FLOAT,coord_y IN FLOAT,temp_x OUT FLOAT);
end pck_test;
/
create or replace package body pck_test is
PROCEDURE ArrondiGeo(coord_x IN FLOAT,coord_y IN FLOAT,temp_x OUT FLOAT,temp_y OUT FLOAT)
IS
tp_x FLOAT;
BEGIN
tp_x := mod(coord_x*10000,10);
if(tp_x>50)
then
if(tp_x>75)
then
temp_x:=trunc(coord_x,2);
temp_x:=temp_x+0.01;
else
temp_x:=trunc(coord_x,2);
temp_x:=temp_x+0.005;
end if;
else
if(tp_x>25)
then
temp_x:=trunc(coord_x,2);
temp_x:=temp_x+0.005;
else
temp_x:=trunc(coord_x,2);
end if;
end if;
END;
END pck_test;
/
Does anyone know why it's not working ?
Even if you are consistent in the sample code, maybe are you mixing FLOAT and BINARY_FLOAT in the rest of your program.
FLOAT is a subtype of NUMBER with up to 38 digits precision. 0.005 is a literal float.
BINARY_FLOAT is an IEEE 754 floating point numbers with roughly speaking 7 decimal digits of precision. 0.005f would be a literal binary_float.
See this example:
declare
w float := 3.14;
x float := 3.14*100000;
y binary_float := 3.14;
z binary_float := 3.14*100000;
begin
w := w + 0.005;
x := x + 0.005;
y := y + 0.005f;
z := z + 0.005f;
dbms_output.put_line(w);
dbms_output.put_line(x);
dbms_output.put_line(y);
dbms_output.put_line(z);
end;
Producing:
3.145
314000.005
3.14500022E+000
3.14E+005

Resources