Can I clamp a value into a range in Ada? - ada

Is there a nice way to clamp (clip?, coerce?) a value to a range in Ada?
I have done that for now:
timer := Integer'Max(timer, Integer(Half_Word'First));
timer := Integer'Min(timer, Integer(Half_Word'Last));
TIM8.TIM.ARR := Half_Word(timer);
But this is a bit clunky, I'd like to clamp the variable timer into the range of Half_Word.

Ada does not have a built-in way to do this. If this is the kind of thing you will be doing repeatedly, and on different types, a generic like this might help:
generic
type Source_Type is range <>;
type Destination_Type is range <>;
function Saturate (X : Source_Type) return Destination_Type;
function Saturate (X : Source_Type) return Destination_Type is
Result : Source_Type;
begin
Result := Source_Type'Max (X, Source_Type (Destination_Type'First));
Result := Source_Type'Min (Result, Source_Type (Destination_Type'Last));
return Destination_Type (Result);
end Saturate;
...
function Saturate_To_Half_Word is new Saturate (Integer, Half_Word);
...
TIM8.TIM.ARR := Saturate_To_Half_Word(timer);
EDIT: The above assumes that the bounds of Destination_Type will always be included in Source_Type. The code will raise a Constraint_Error if this is not the case. An alternative that will work even if Destination_Type could be larger than Source_Type:
function Saturate (X : Source_Type) return Destination_Type is
type Largest_Int is range System.Min_Int .. System.Max_Int;
Result : Source_Type;
begin
Result := X;
if Largest_Int (Destination_Type'First) in
Largest_Int (Source_Type'First) .. Largest_Int (Source_Type'Last) then
Result := Source_Type'Max (Result, Source_Type (Destination_Type'First));
end if;
if Largest_Int (Destination_Type'Last) in
Largest_Int (Source_Type'First) .. Largest_Int (Source_Type'Last) then
Result := Source_Type'Min (Result, Source_Type (Destination_Type'Last));
end if;
return Destination_Type (Result);
end Saturate;
You have to code it carefully to avoid type conflicts but also avoid raising Constraint_Error.
EDIT 2: I had First and Last backwards. Fixed.
EDIT 3: Following Simon's suggestion, this will also work:
function Saturate (X : Source_Type) return Destination_Type is
type Largest_Int is range System.Min_Int .. System.Max_Int;
Result : Largest_Int;
begin
Result := Largest_Int (X);
Result := Largest_Int'Max (Result, Largest_Int (Destination_Type'First));
Result := Largest_Int'Min (Result, Largest_Int (Destination_Type'Last));
return Destination_Type (Result);
end Saturate;
This looks simpler, but is potentially slightly less efficient because it involves more type conversions at run time. (Note that the complex-looking conditions in the previous example are all conditions that can be evaluated at compile time and therefore should not generate any code--plus it could cause the 'Max and/or 'Min operations to be eliminated. This is assuming the compiler uses a "macro expansion" model for generic instantiation, as GNAT does. A really good compiler may be able to figure out the same kinds of optimizations in the latter code too, but it's harder.)

You can use defaults to make this a bit easier to use -- the biggest problem [maintenance-wise] then is that it looks like there's more going on than there really is, and the biggest drawback to this is that the constraint of DESTINATION_TYPE being a subset of SOURCE_TYPE is not checked/enforced at compile-time by the compiler.
generic
type Source_Type is range <>;
type Destination_Type is range <>;
S_First : Long_Long_Integer:= Long_Long_Integer(Source_Type'First); --' FIX FOR
S_Last : Long_Long_Integer:= Long_Long_Integer(Source_Type'Last); --' BUGGY
D_First : Long_Long_Integer:= Long_Long_Integer(Destination_Type'First);--' SYNTAX
D_Last : Long_Long_Integer:= Long_Long_Integer(Destination_Type'Last); --' HIGHLIGHT.
function Saturate (X : Source_Type) return Destination_Type
with Pre =>
D_First >= S_First
and D_Last <= S_Last;
function Saturate (X : Source_Type) return Destination_Type is
X_Pos : constant Long_Long_Integer:= Long_Long_Integer(X);
begin
if X_Pos in D_First..D_Last then
return Destination_Type(X);
elsif X_Pos < D_First then
return Destination_Type'First; --'
else
return Destination_Type'Last; --'
end if;
end Saturate;
Example usage:
subtype K is Integer range 2..24;
subtype J is Integer range 3..10;
Function P is new Saturate(Source_Type => K,
Destination_Type => J
);

Related

How to use Assert and loop_invariants

Specification:
package PolyPack with SPARK_Mode is
type Vector is array (Natural range <>) of Integer;
function RuleHorner (X: Integer; A : Vector) return Integer
with
Pre => A'Length > 0 and A'Last < Integer'Last;
end PolyPack ;
I want to write body of PolyPack package with Assert and loop_invariants that the gnatprove program can prove my function RuleHorner correctness.
I write my function Horner but I don;t know how put assertions and loop_invariants in this program to prove its corectness :
with Ada.Integer_Text_IO;
package body PolyPack with SPARK_Mode is
function RuleHorner (X: Integer; A : Vector) return Integer is
Y : Integer := 0;
begin
for I in 0 .. A'Length - 1 loop
Y := (Y*X) + A(A'Last - I);
end loop;
return Y;
end RuleHorner ;
end PolyPack ;
gnatprove :
overflow check might fail (e.g. when X = 2 and Y = -2)
overflow check might fail
overflow check are for line Y := (Y*X) + A(A'Last - I);
Can someone help me how remove overflow check with loop_invariants
The analysis is correct. The element type for type Vector is Integer. When X = 2, Y = -2, and A(A'Last - I) is less than Integer'First + 4 an underflow will occur. How do you think this should be handled in your program? Loop invariants will not work here because you cannot prove that an overflow or underflow cannot occur.
Is there a way you can design your types and/or subtypes used within Vector and for variables X and Y to prevent Y from overflowing or underflowing?
I am also curious why you want to ignore the last value in your Vector. Are you trying to walk through the array in reverse? If so simply use the following for loop syntax:
for I in reverse A'Range loop

Is it necessary to wrap shared array data in a protected type?

I am aware that it is generally bad practice (and the ARM probably says that this is undefined behavior), but I am attempting to write a fast text parser containing many floating point numbers and it would be very expensive to wrap the loaded text into a protected type given that the data is examined character by character and may have up to a million floats or pass a slice on the stack.
Is it possible in Ada (GNAT) to "safely" divide up an unprotected array for consumption with multiple tasks given that the array is never written and only read?
As in:
Text : array (1..1_000_000) of Character := ...
begin
Task_1.Initialize (Start_Index => 1, End_Index => 10_000);
Task_2.Initialize (Start_Index => 10_001, End_Index => 20_000);
...
Yes. That is safe because there is no race condition associated with reading the data and there is no temporally overlapping write operation.
For example, the following code uses such a technique to perform parallel addition on an array of integers.
package Parallel_Addition is
type Data_Array is array(Integer range <>) of Integer;
type Data_Access is access all Data_Array;
function Sum(Item : in not null Data_Access) return Integer;
end Parallel_Addition;
package body Parallel_Addition is
---------
-- Sum --
---------
function Sum (Item : in not null Data_Access) return Integer is
task type Adder is
entry Set (Min : Integer; Max : Integer);
entry Report (Value : out Integer);
end Adder;
task body Adder is
Total : Integer := 0;
First : Integer;
Last : Integer;
begin
accept Set (Min : Integer; Max : Integer) do
First := Min;
Last := Max;
end Set;
for I in First .. Last loop
Total := Total + Item (I);
end loop;
accept Report (Value : out Integer) do
Value := Total;
end Report;
end Adder;
A1 : Adder;
A2 : Adder;
R1 : Integer;
R2 : Integer;
Mid : constant Integer := (Item'Length / 2) + Item'First;
begin
A1.Set (Min => Item'First, Max => Mid);
A2.Set (Min => Mid + 1, Max => Item'Last);
A1.Report (R1);
A2.Report (R2);
return R1 + R2;
end Sum;
end Parallel_Addition;
with Parallel_Addition; use Parallel_Addition;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;
procedure Parallel_Addition_Test is
The_Data : Data_Access := new Data_Array (1 .. Integer'Last / 5);
Start : Time;
Stop : Time;
The_Sum : Integer;
begin
The_Data.all := (others => 1);
Start := Clock;
The_Sum := Sum (The_Data);
Stop := Clock;
Put_Line ("The sum is: " & Integer'Image (The_Sum));
Put_Line
("Addition elapsed time is " &
Duration'Image (Stop - Start) &
" seconds.");
Put_Line
("Time per addition operation is " &
Float'Image(Float(Stop - Start) / Float(The_Data'Length)) &
" seconds.");
end Parallel_Addition_Test;

GNATprove: "postcondition might fail" in simple function

I want to write a simple function that finds the biggest number in given Integer array. Here is specification:
package Maximum with SPARK_Mode is
type Vector is array(Integer range <>) of Integer;
function Maximum (A : in Vector) return Integer
with
SPARK_Mode,
Pre => A'Length > 0,
Post =>
(for all i in A'Range => A(i) <= Maximum'Result)
and then
(for some i in A'Range => A(i) = Maximum'Result);
end Maximum;
And here is function's body:
package body Maximum with SPARK_Mode is
function Maximum (A : in Vector) return Integer
is
Max : Integer := A (A'First);
begin
if (A'Length = 1) then
return Max;
end if;
for I in A'First + 1 .. A'Last loop
pragma Loop_Invariant
(for all Index in A'First .. I - 1 => Max >= A(Index));
if A (I) > Max then
Max := A (I);
end if;
end loop;
return Max;
end Maximum;
end Maximum;
And when I try to prove this function with SPARK, it says that postcondition might fail. I'm trying to understand this for like 5 hours now and I have no idea why it says so. It's really annoying, this function MUST work. Do you have any idea why SPARK behaves so strange? What is a data example for this function to not fullfil its postcondition? It always returns a value taken directly from given array and it is always maximal.
Your mistake is to make a loop invariant, which is weaker than the postcondition:
Specification:
package Maximum
with SPARK_Mode
is
type Vector is array (Integer range <>) of Integer;
function Maximum (A : in Vector) return Integer
with
Pre => A'Length > 0,
Post => (for all i in A'Range => A(i) <= Maximum'Result)
and
(for some i in A'Range => A(i) = Maximum'Result);
end Maximum;
Implementation:
package body Maximum with SPARK_Mode is
function Maximum (A : in Vector) return Integer
is
Max : Integer := A (A'First);
begin
if (A'Length = 1) then
return Max;
end if;
for K in A'First + 1 .. A'Last loop
pragma Loop_Invariant
((for all I in A'First .. K - 1 => A (I) <= Max)
and
(for some I in A'First .. K - 1 => A (I) = Max));
if A (K) > Max then
Max := A (K);
end if;
end loop;
return Max;
end Maximum;
end Maximum;
Project file:
project Maximum is
for Main use ("maximum");
end Maximum;

How do you get the square root in Ada?

So I have been given an assignment to read in a file put the numbers into two matrices, multiply the matrices, and finally put the output into a .txt file.
I have never used Ada before and I figured it would be a good challenge. I am stuck in trying to determine the bounds for the two separate arrays.
This is what I currently have:
currentSpread := I;
g := Ada.Numerics.Generic_Complex_Elementary_Functions.Sqrt(I);
while J < g loop
if(I mod J = 0) THEN
if(currentSpread > ((I/J - J)/2)) THEN
currentSpread := ((I/J - J)/2);
arrayBounds := J;
end if;
end if;
J := J + 1;
end loop;
The problem I am having is with the sqrt function. I want to find the factors for the best bounds of the matrix multiplication and this was the only way that I thought to implement it.
The error I am getting is:
invalid prefix in selected component "Ada.Numerics.Generic_Complex_Elementary_Functions"
Thanks a lot for any help.
--Update
Full Code as requested:
with Ada.Text_IO;use Ada.Text_IO;
with Ada.Integer_Text_IO;
with Ada.Numerics.Generic_Complex_Elementary_Functions;
with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Numerics.Complex_Elementary_Functions;
with Ada.Numerics.Generic_Complex_Types;
procedure Main is
dataFile : File_Type;
resultFile : File_Type;
value : Integer;
I : Integer := 0;
J : Integer;
currentSpread : Integer;
arrayBounds : Integer;
g : Integer;
begin
Ada.Text_IO.Open(File => dataFile, Mode => Ada.Text_IO.In_File, Name =>"C:\Users\Jeffrey\Desktop\data.txt");
while not End_Of_File(dataFile) loop
Ada.Integer_Text_IO.Get(File => dataFile, Item => value);
Ada.Integer_Text_IO.Put(Item => value);
Ada.Text_IO.New_Line;
I := I + 1;
end loop;
Ada.Integer_Text_IO.Put(I);
I := I/2;
J := 1;
currentSpread := I;
g := Ada.Numerics.Generic_Complex_Elementary_Functions.Sqrt(I);
while J < g loop
if(I mod J = 0) THEN
if(currentSpread > ((I/J - J)/2)) THEN
currentSpread := ((I/J - J)/2);
arrayBounds := J;
end if;
end if;
J := J + 1;
end loop;
declare
type newArray is array(Integer range <>, Integer range<>) of Integer;
X : Integer := J;
Y : Integer := I/J;
Arr1 : newArray(1..Y, 1..X);
Arr2 : newArray(1..X, 1..Y);
finAnswer : newArray(1..X, 1..X);
begin
for z in 1 .. X loop
for k in 1 .. Y loop
Ada.Integer_Text_IO.Get(File => dataFile, Item => value);
Arr1(z, k) := value;
end loop;
end loop;
for z in 1 .. Y loop
for k in 1 .. X loop
Ada.Integer_Text_IO.Get(File => dataFile, Item => value);
Arr2(z, k) := value;
end loop;
end loop;
for l in 1 .. X loop
for m in 1 .. Y loop
for n in 1 .. X loop
finAnswer(l, n) := finAnswer(l, n) + Arr1(l, n)* Arr2(n, m);
end loop;
end loop;
end loop;
end;
Ada.Text_IO.Close(File => dataFile);
end Main;
I am using the square root exclusively to figure out the factors of a number nothing else. How I have it set up now is it will go up to the square root and then it will take the smallest spread of the factors. I do not care about rounding errors or anything else if it isn't a perfect square it can round either way.
Thanks.
Generic_Complex_Elementary_Functions is a generic package. It cannot be used directly. That is why the compiler gives you an error on this line:
Ada.Numerics.Generic_Complex_Elementary_Functions.Sqrt(I);
To use it, you have to instantiate the generic Generic_Complex_Types with the floating-point type you want to use, and then instantiate Generic_Complex_Elementary_Functions with an instance of Generic_Complex_Types. Fortunately, you don't have to go through all that if you're willing to use the built-in type Float; the language provides Ada.Numerics.Complex_Elementary_Functions for you, that uses Float as the floating-point type, or Ada.Numerics.Long_Complex_Elementary_Functions that uses Long_Float if your compiler vendors support it.
However, I don't think you want to use Complex anything. That deals with complex numbers, and I doubt that you want to use those. Use Ada.Numerics.Elementary_Functions (or Long_Elementary_Functions), which deal with real numbers.
Finally, even this isn't going to work:
Ada.Numerics.Elementary_Functions.Sqrt(I)
if I is an integer, because the argument type needs to be a Float. You'll have to use a type conversion.
Ada.Numerics.Elementary_Functions.Sqrt(Float(I))

Ada Generic Averaging Function

I have a function which averages a certain numeric value from an array of records. This value is either a natural or an enumerated type delta. I have it summing up the values correctly but my question is this: how do I get the length of an array into a generic type, so that it can divide both integers and delta type numbers?
On your array-of-records use the 'Length attribute; this has the advantage of always working even if your bounds are somewhat odd, like -18..3, or an enumeration, like cheeses..fruits.
Something like:
Function Average( Input : In Array_of_Records ) Return float is
-- You say you already have a summation function, so...
Sum : Natural:= Summation( Input );
Begin
Return Sum / Input'Length;
End Average;
You may need to convert the numeric types, by saying Float(Sum) or the like, as Ada does no automatic type "promotions."
This has some flaws in it, but is this closer to what you wanted ?
NWS.
with Ada.Text_Io;
procedure Main is
generic
type Element_T is private;
Zero : Element_T;
One : Element_T;
type Vec_T is array (Integer range <>) of Element_T;
with function "+"(Left, Right : in Element_T) return Element_T is <>;
with function "/"(Left, Right : in Element_T) return Element_T is <>;
package Arrayops is
function Sum (Vec : in Vec_T) return Element_T;
function Count (Vec : in Vec_T) return Element_T;
function Average (Vec : in Vec_T) return Element_T;
end Arrayops;
package body Arrayops is
function Sum (Vec : in Vec_T) return Element_T is
S : Element_T := Zero;
begin
for I in Vec'First .. Vec'Last loop
S := S + Vec(I);
end loop;
return S;
end Sum;
function Count (Vec : in Vec_T) return Element_T is
C : Element_T := Zero;
begin
for I in Vec'First .. Vec'Last loop
C := C + One;
end loop;
return C;
end Count;
function Average (Vec : in Vec_T) return Element_T is
S : constant Element_T := Sum (Vec);
Len : constant Element_T := Count (Vec);
begin
return S / Len;
end Average;
end Arrayops;
type Fl_Arr_T is array (Integer range <>) of Float;
package Fl_Arr is new Arrayops (Element_T => Float,
Zero => 0.0,
One => 1.0,
Vec_T => Fl_Arr_T);
type Int_Arr_T is array (Integer range <>) of Integer;
package Int_Arr is new Arrayops (Element_T => Integer,
Zero => 0,
One => 1,
Vec_T => Int_Arr_T);
My_Ints : constant Int_Arr_T (1 .. 5) := (6,7,5,1,2);
My_Floats : constant Fl_Arr_T (1 .. 7) := (6.1,7.2,5.3,1.4,2.5,8.7,9.7);
Int_Sum : constant Integer := Int_Arr.Sum (My_Ints);
Int_Count : constant Integer := Int_Arr.Count (My_Ints);
Int_Avg : constant Integer := Int_Arr.Average (My_Ints);
Float_Sum : constant Float := Fl_Arr.Sum (My_Floats);
Float_Count : constant Float := Fl_Arr.Count (My_Floats);
Float_Avg : constant Float := Fl_Arr.Average (My_Floats);
begin
Ada.Text_Io.Put_Line ("Integers => Sum: " & Integer'Image (Int_Sum) & ", Count: " & Integer'Image (Int_Count) & ", Avg: " & Integer'Image (Int_Avg));
Ada.Text_Io.Put_Line ("Floats => Sum: " & Float'Image (Float_Sum) & ", Count: " & Float'Image (Float_Count) & ", Avg: " & Float'Image (Float_Avg));
end Main;
Result :
Integers => Sum: 21, Count: 5, Avg: 4
Floats => Sum: 4.09000E+01, Count: 7.00000E+00, Avg: 5.84286E+00
Expanding on Shark8 a bit here...
Ada allows you to declare array types as unconstrained. Something like
type Array_of_Records is array (Natural range <>) of My_Record;
Gives you a type that can be used for arrays of records with starting and ending array indices that could be anywhere in the range of Natural.
One of the nifty things I can do with such a type is use it as a subroutine parameter, like so:
function Sum (Vector : in Array_of_Records) return Natural;
OK, so inside that routine, how do I know where the array bounds are? By using attributes, like so:
for index in Vector'first..Vector'last loop
or
for index in Vector'range loop
Of course for this to work, you must pass in a perfectly-sized array to your Sum routine. Supppose that isn't what you have. Suppose you instead have a huge array (kind of a buffer) and not all of the values are valid? Well, you keep track of what are the valid values, and pass in only those by using a slice.
Rec_Buffer : Array_of_Records (1..10_000);
Last_Valid_Rec : Natural := 0;
....
--// Rec_Buffer gets loaded with 2,128 values or something. We pass it into Sum
--// like so:
Ada.Text_IO ("Sum of vector is " &
natural'image(Sum (Rec_Buffer (1..Last_Valid_Rec));
(warning - uncompiled code)

Resources