Using Ada, using instance of Ada.Numerics.Generic_Elementary_Functions(Real), calculating Nth root and output same - ada

Using Ada 2018 (increment of 2012), within a loop structure, I need to calculate the Nth root of Integers.
In my package combinations.ads specification declaration (using GNAT GPS), I have
type Real is digits 6;
In package body combinations.adb, I have a procedure build, where before the begin, I instantiate Ada’s Generic_Elementary_Functions(Float), with
package Fermat_math is new
Ada.Numerics.Generic_Elementary_Functions(Real) ;
use Fermat_math
Later, in output section, I try:
-- -------------- buggy, fix
-- combo_sum_root := Fermat_math.Exp (Log (Integer(combo_sum_root) / n); — n is integer type
combo_sum_root := Real(combo_sum) ** (1/n) ;
-- -------------
put(" and sum's root is ");
put(combo_sum_root'image ); — -- gives all roots as 1.00000E+00
I had it working a few weeks back, with roots = 3.878… etc., but I lost that in careless version control.
Actual Code here:
— combinations.ads specification ------------------------------------------
with gearbox;
use gearbox;
with Ada.Float_Text_IO ; use Ada.Float_Text_IO;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics; use Ada.Numerics;
with Ada.Numerics.Elementary_Functions;
use Ada.Numerics.Elementary_Functions;
package combinations is
type combo_action is (add_element,clear, show, show_sum, Build);
type Real is digits 6 ;
combo_sum_root : Real ;
i,n,b, combos_cnt
,combo_sum : integer ;
procedure get_parms ;
Procedure build (b,n,r:integer) ;
end combinations;
-- combinations.adb BODY ---------------------------------------
with Text_IO ; use Text_IO;
with Ada.Text_IO ; use Ada.Text_IO;
with Ada.INteger_Text_IO ; use Ada.Integer_Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.UNbounded;
with gearbox ; use gearbox;
with Ada.Numerics.Generic_Elementary_Functions ;
package body combinations is
group, Intersection_count,r : Integer ;
done, get_value : boolean := false ;
CR: constant Character := Character'Val (13) ;
type gear_arrays is array(positive range <>) of integer;
-- ------------------------------------------------------------
procedure get_parms is
begin
...
end get_parms ;
-- --------------------------------------------------
procedure build (b,n,r: Integer) is
-- --------------------------------------------------
cnt, e_cnt, value : integer :=0 ;
launch, pause : character ;
run_again : String := " " ;
show_group : Unbounded_string ;
all_done, combo_done : boolean := false ;
combo_sum_root : Real ;
progress_string : Unbounded_String ;
gears:gear_array (1..r) ;
-- with Ada.Numerics.Generic_Elementary_Functions ; — in specification .ads file
package Fermat_math is new
Ada.Numerics.Generic_Elementary_Functions(Real) ;
use Fermat_math ;
begin
...
...
put("Selecting "); -- put(tot_combos, width=>1);
put(" Possible Combinations,"); New_line;
While Not all_done loop -- for all/x combiNatioNs
...
end loop;
-- ------------------------
combo_sum := 0;
for e in 1..r loop -- select r value, element of grou & size of combiatios
value := fermats(gears(e).position,1);
...
put ("Combination sum is "); put (combo_sum, width => 1);
…..
-- -------------- buggy, fix
-- combo_sum_root := Fermat_math.Exp (Log (Integer(combo_sum_root) / n);
combo_sum_root := Real(combo_sum) ** (1/n) ;
-- -------------
put(" and sum's root is ");
put(combo_sum_root'image ); -- gives all roots as 1.00000E+00
end loop;
group := group + 1; --
end if; -- is New group and shift
end loop; -- Not all doNe
eNd build;
begin -- package
Null;
end combinations;

The critical issue in your example is that "n is integer type." When you try to create a rational exponent, your expression 1/n evaluates to an Integer with value zero. The package Generic_Elementary_Functions requires that "Exponentiation by a zero exponent yields the value one."
The solution is to use the type Real when creating your exponent: 1.0 / Real(N). Expanding on #Jim Rogers' example, the code below also illustrates calculating the power via the logarithm:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Generic_Elementary_Functions;
procedure Nth_Root is
type Real is digits 6;
package Real_Functions is new Ada.Numerics.Generic_Elementary_Functions
(Real);
use Real_Functions;
N : constant Natural := 2;
Power : constant Real := 1.0 / Real(N);
Base : constant Real := 2.0;
begin
Put_Line ("√2:" & Real'Image (Sqrt (Base)));
Put_Line ("√2:" & Real'Image (Base**Power));
Put_Line ("√2:" & Real'Image (Exp (Power * Log (Base))));
end Nth_Root;
Console:
√2: 1.41421E+00
√2: 1.41421E+00
√2: 1.41421E+00

The "**" operator in Ada.Numerics.Generic_Elementary_Functions provides you with the ability to find the Nth root of a floating point number.
The following example compares use of the sqrt function and the "**" operator.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Generic_Elementary_Functions;
procedure Main is
type Real is digits 6;
package Real_Functions is new Ada.Numerics.Generic_Elementary_Functions(Real);
use Real_Functions;
base : Real := 2.0;
Power : Real := 1.0 / 2.0;
begin
Put_Line("sqrt of 2.0 is: " & Real'Image(sqrt(base)) & " and " &
Real'Image(base**Power));
end Main;
The result of this example is:
sqrt of 2.0 is: 1.41421E+00 and 1.41421E+00
The exponent for the "**" operator is simply the inverse of N.
Edit: Adding the manipulations to calculate an integer root of a number.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Numerics.Generic_Elementary_Functions;
procedure integer_roots is
function int_root (Num : Positive; Exponent : Positive) return Natural is
type Real is digits 6;
package real_functions is new Ada.Numerics.Generic_Elementary_Functions
(Real);
use real_functions;
Real_Num : Real := Real (Num);
Real_Exponent : Real := 1.0 / Real (Exponent);
Real_Result : Real := Real_Num**Real_Exponent;
begin
return Natural (Real'Truncation (Real_Result));
end int_root;
Num : Positive;
Exponent : Positive;
begin
Put ("Enter the base number: ");
Get (Num);
Skip_Line;
Put ("Enter the root: ");
Get (Exponent);
Put_Line
("The" & Exponent'Image & "th root of" & Num'Image & " is" &
Natural'Image (int_root (Num, Exponent)));
end integer_roots;
The function int_root shown above calculates the floating point root then converts the truncated result to the subtype Natural.
A sample execution of this program is:
Enter the base number: 10000
Enter the root: 20
The 20th root of 10000 is 1

Related

Constraint Error running Heapify SiftDown

I am writing this code in Ada for a class where we have to teach ourselves the code. I understand heap sort, but the Ada syntax is really confusing me. I don't understand why I am getting a constraint error in this sort function.
Essentially we have to pass array "A" into this procedure, and it should organize it. I get the constraint error at siftDown(A(Start...A'Last));
Thank you in advance
Procedure sort_3(A : in out array_type) is
procedure swap(Left : in out Integer; Right : in out Integer) is
temp : Integer;
begin
temp := Left;
Left := Right;
Right := Temp;
end swap;
procedure siftDown(A : in out array_type) is
Count : Integer := 1;
root : Integer := Integer'Pos(A'First);
child : Integer := Integer'Pos(A'Last);
last : Integer := Integer'Pos(A'Last);
begin
while root * 2 + 1 <= last loop
child := root * 2 + 1;
if child + 1 <= last and then A(Integer'Val(child)) < A(Integer'Val(child + 1)) then
child := child + 1;
end if;
if A(Integer'Val(root)) < A(Integer'Val(child)) then
swap(A(Integer'Val(root)), A(Integer'Val(child)));
root := child;
else
exit;
end if;
end loop;
end siftDown;
procedure heapify(A : in out array_type) is
Count : Integer := 0;
First_Pos : Integer;
Last_Pos : Integer;
Start : Integer;
begin
First_Pos := A'First;
Last_Pos := A'Last;
Start := Integer'Val((Last_Pos - First_Pos + 1) / 2);
loop
siftDown(A(Start...A'Last));
if Start > Integer'First then
Start := Integer'Pred(Start);
else
exit;
end if;
end loop;
end heapify;
Last_Index : Integer := Integer'Last;
begin
heapify(A);
while Last_Index > Integer'First loop
swap(A(Last_Index), A(A'First));
Last_Index := Integer'Pred(Last_Index);
siftDown(A(A'First..Last_Index));
end loop;
end sort_3;
You have a syntax error in the code - an extra dot in A(Start...A'Last).
The syntax A(Start..A'Last) means a slice, part of array from Start to the last element. The Constraint_Error means that Start not in array bounds. Try to add
Ada.Text_IO.Put_Line (Start'Image);
before that line and you will see Start values and when it became out of the A'Range.
Your code has some references to Integer'First and Integer'Last, which are huge values that have nothing to do with the array A and its values. I'm pretty sure you should use A'First and A'Last instead.
Also a note on style: Using the same identifier, "A", for the parameter of the local (inner, nested) procedures as for the parameter "A" of the containing (outer) procedure, when these arrays can be different, invites confusion and errors. Better to use different identifiers.

How to define "+" for a record type

I'm trying to create a package in ADA. I have three files, adb(main program), ads(package), and adb(body package). I can not see any problems with my main and package files. However, in my body package, I have trouble in writing a function that adds the values of P1 and P2 together and then returns its value.
My main program:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Price_Handling; use Price_Handling;
procedure Test_Price_Plus is
P1, P2 : Price_Type;
begin
P1.Dollar:= 19;
P1.Cents:= 50;
P2 := (Dollar => 10, Cents=> 0);
Put("P1 is ");
Put(P1);
New_Line;
Put("P2 is ");
Put(P2);
New_Line;
Put("If we add P1 and P2, then we get: ");
Put(P1 + P2);
New_Line;
end Test_Price_Plus;
My package:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
package Price_Handling is
type Price_Type is private;
procedure Get(Item: out Price_Type);
function "+"(Left, Right: in Price_Type) return Price_Type; -- Left(Dollar), Right(Cents)
private
type Price_Type is
record
Dollar, Cents: Integer;
end record;
end Price_Handling;
My Package Body:
Package Body Price_Handling is
procedure Put(Item:in Price_Type) is
begin
Put(Item.Dollar); Put(":");
Put(Item.Cents);
end Put;
function "+"(Left, Right: in Price_Type) return Price_type is
begin
-- Need to write a function that adds P1 and P2 together and return its value
end "+";
The easiest way for newcomers is probably just create a dummy result value and return it:
function "+"(Left, Right: in Price_Type) return Price_type is
Result : Price_Type;
begin
Result.Dollars := Left.Dollars + Right.Dollars;
Result.Cents := Left.Cents + Right.Cents;
-- If your cents are higher than 100, then we
-- need to increment dollars and adjust the cents
-- count
if Result.Cents > 99 then
Result.Dollars := Result.Dollars + 1;
Result.Cents := Result.Cents - 100;
end if;
return Result;
end "+";
However, this is really weak. Your Price_type is not design using types that can protect you from errors. If you want to leverage Ada's safety aspects, consider making a Cents subtype that restricts you to 0 to 99:
subtype Cents_Type is Natural range 0 .. 99;
That way, if you make a programming error and put in a value higher than 99 or negative, then the program will catch it and raise an exception. Same for dollars. Make a new type for non negative values:
subtype Dollars_Type is Natural;
Now update your record to use those types and also default initialize them:
type Price_Type is record
Dollars : Dollars_Type := 0;
Cents : Cents_Type := 0;
end record;
Then if you do that, you can update the + function to use a dummy variable to hold the cents in case you do go over 99 while adding them together.
function "+"(Left, Right: in Price_Type) return Price_type is
Result : Price_Type;
Cents_With_Overflow : Natural;
begin
Result.Dollars := Left.Dollars + Right.Dollars;
Cents_With_Overflow := Left.Cents + Right.Cents;
-- If your cents are higher than 100, then we
-- need to increment dollars and adjust the cents
-- count
if Cents_With_Overflow > 99 then
Result.Dollars := Result.Dollars + 1;
Result.Cents := Cents_With_Overflow - 100;
else
Result.Cents := Cents_With_Overflow;
end if;
return Result;
end "+";
Another approach for this problem is to convert the dollars and cents for each instance of Price_Type to a total number of cents. Simply add the two cents values then convert the result back to dollars and cents.
package Price_Handling is
type Price_Type is private;
function "+" (Left, Right : Price_Type) return Price_Type;
procedure Get(Item : out Price_type);
procedure Print(Item : in Price_Type);
private
subtype Cents_Type is Integer range 0..99;
type Price_Type is record
Dollars : Natural := 0;
Cents : Cents_Type := 0;
end record;
end Price_Handling;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
package body Price_Handling is
---------
-- "+" --
---------
function "+" (Left, Right : Price_Type) return Price_Type is
function To_Cents ( Item : Price_type) return Natural is
Temp : Natural := 100 * Item.Dollars + Item.Cents ;
begin
return Temp;
end To_Cents;
function To_Price(Item : in Natural) return Price_Type is
Dollars : Natural;
Cents : Cents_Type;
begin
Dollars := Item / 100;
Cents := Item mod 100;
return (Dollars, Cents);
end To_Price;
begin
return To_Price(To_Cents(Left) + To_Cents(Right));
end "+";
---------
-- Get --
---------
procedure Get (Item : out Price_type) is
begin
Put("Enter the dollars for the price: ");
Get(Item.Dollars);
Put("Enter the cents for the price: ");
Get(Item.Cents);
end Get;
-----------
-- Print --
-----------
procedure Print (Item : in Price_Type) is
begin
Put(Item => Item.Dollars, Width => 1);
Put (".");
if Item.Cents < 10 then
Put ("0");
end if;
Put (Item => Item.Cents, Width => 1);
end Print;
end Price_Handling;
with Price_Handling; use Price_Handling;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
P1 , P2 : Price_Type;
begin
Get(P1);
Get(P2);
Put("The sum of P1 and P2 is: ");
Print(P1 + P2);
New_Line;
end Main;
An example of executing this program produces the following output:
Enter the dollars for the price: 10
Enter the cents for the price: 41
Enter the dollars for the price: 0
Enter the cents for the price: 32
The sum of P1 and P2 is: 10.73
Another example showing proper incrementation of the dollars amount when the two cents amounts sum to more than a dollar is:
Enter the dollars for the price: 10
Enter the cents for the price: 52
Enter the dollars for the price: 3
Enter the cents for the price: 95
The sum of P1 and P2 is: 14.47

Formal parameter "Item" is not referenced

By using subprograms, I try to write a program that displays a VAT table. It asks the user to enter some data and based on these data, displays a VAT-table. However, I get a warning which says: "Formal parameter "Item" is not referenced". My program does display a table but it looks terrible. Here you can see how far I have come:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Underprogram is
V_percent, S_length, H_price, L_price, Price_Wm : Float;
X: Integer;
procedure Get_Pop(Item : out Float ) is
begin
Put_Line("Write ur first price:");
Get(H_price, width=>0);
while H_price < 0.0 loop
Put("Wrong inout, try again! : ");
Get(H_price);
end loop;
loop
Put_Line("Write in your second price:");
Get(L_price, width=>0);
if H_price > L_price then
exit; end if; end loop;
Put_Line("Which VAT percent do ypu want? ");
Get(V_percent, width=>0);
while V_percent > 100.0 or V_percent <= 0.0 loop
Put_Line("The vat percent you fed in is invalid, try again! ");
Get(V_percent);
end loop;
Put_Line("Which step length do you want? ");
Get(S_length, width=>0);
while S_length < 0.1 or S_length > 1.0 loop
Put_Line("The step length you just fed in is out of the range: ");
Get(S_length);
end loop; end Get_Pop;
procedure Put_pop(Item : in Float) is
begin
X := Integer(Float'Floor((H_price-L_price) / S_length + 1.0));
Put_Line(" === Vattabell === ");
Put_Line("Price without VAT Vat Price with VAT ");
Put_Line("--------------- ---- -------------- ");
for I in 0..X -1 loop
Price_Wm := L_price + Float(I) * S_length;
Put(Price_Wm, 5,2,0);
Put((L_price + Float(I) * S_length) * V_percent/100.0,
13,2,0);
Put(Price_Wm * (1.0 + V_percent/100.0), 15,2,0);
New_Line;
end loop;
end Put_pop;
begin
Get_Pop(V_percent); Put_pop(V_percent);
Get_Pop(S_length); Put_pop(S_length);
Get_Pop(H_price); Put_pop(H_price);
Get_Pop(L_price); Put_pop(L_price);
Get_Pop(Price_Wm); Put_pop(Price_Wm);
end Underprogram;
Here is an update: I just rewrote my code based in the comments and it works fine. Now I wonder how I can add more functions and subprograms to this code so that the Underprogram procedure looks cleaner?. Right now I have just two subprograms:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Underprogram is
Higher, Lower, VAT, Step, Price_without_VAT: Float;
X : Integer;
procedure Get_data(Item: out Float) is
begin
Get(Item);
end Get_data;
procedure Put_data(Item: in Float) is
begin
Put(Item, 2,2,0); Put(" ");
end Put_data;
begin
Put("Write the higher price: ");
Get_data(Higher);
while Higher < 0.0 loop
Put("Wrong input, try again: ");
Get_data(Higher); end loop;
loop
Put("Write the lower price: ");
Get_data(Lower);
if Lower < Higher then
exit; end if; end loop;
Put("Write the VAT : ");
Get_data(VAT);
while VAT > 100.0 or VAT < 0.0 loop
Put("Wrong input, try again:");
Get_data(VAT);
end loop;
loop
Put("Write the step: ");
Get_data(Step);
if Step > 0.0 then
exit; end if; end loop;
Put_data(Higher);
Put_data(Lower);
Put_data(VAT);
Put_data(Step);
X := Integer(Float'Floor((Higher-Lower) / Step + 1.0));
Put_Line(" === VATTABELL === ");
Put_Line("Price without VAT VAT Price with VAT ");
Put_Line("--------------- ---- -------------- ");
for I in 0..X -1 loop
Price_without_VAT := Higher + Float(I) * Step;
Put(Price_without_VAT, 5,2,0);
Put((Higher + Float(I) * Step) * VAT/100.0, 13,2,0);
Put(Price_without_VAT * (1.0 + VAT/100.0), 15,2,0);
New_Line;
end loop;
end Underprogram;
The following code is your code after re-organized by the pretty printer provided in the GNAT Studio 2020 Community Edition:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Underprogram is
V_percent, S_length, H_price, L_price, Price_Wm : Float;
X : Integer;
procedure Get_Pop (Item : out Float) is
begin
Put_Line ("Write ur first price:");
Get (H_price, Width => 0);
while H_price < 0.0 loop
Put ("Wrong inout, try again! : ");
Get (H_price);
end loop;
loop
Put_Line ("Write in your second price:");
Get (L_price, Width => 0);
if H_price > L_price then
exit;
end if;
end loop;
Put_Line ("Which VAT percent do ypu want? ");
Get (V_percent, Width => 0);
while V_percent > 100.0 or V_percent <= 0.0 loop
Put_Line ("The vat percent you fed in is invalid, try again! ");
Get (V_percent);
end loop;
Put_Line ("Which step length do you want? ");
Get (S_length, Width => 0);
while S_length < 0.1 or S_length > 1.0 loop
Put_Line ("The step length you just fed in is out of the range: ");
Get (S_length);
end loop;
end Get_Pop;
procedure Put_pop (Item : in Float) is
begin
X := Integer (Float'Floor ((H_price - L_price) / S_length + 1.0));
Put_Line (" === Vattabell === ");
Put_Line ("Price without VAT Vat Price with VAT ");
Put_Line ("--------------- ---- -------------- ");
for I in 0 .. X - 1 loop
Price_Wm := L_price + Float (I) * S_length;
Put (Price_Wm, 5, 2, 0);
Put ((L_price + Float (I) * S_length) * V_percent / 100.0, 13, 2, 0);
Put (Price_Wm * (1.0 + V_percent / 100.0), 15, 2, 0);
New_Line;
end loop;
end Put_pop;
begin
Get_Pop (V_percent);
Put_pop (V_percent);
Get_Pop (S_length);
Put_pop (S_length);
Get_Pop (H_price);
Put_pop (H_price);
Get_Pop (L_price);
Put_pop (L_price);
Get_Pop (Price_Wm);
Put_pop (Price_Wm);
end Underprogram;
I simply copied the source into the GNAT Studio IDE, saved the file, then chose the Pretty Print option under the Code tab in the IDE. I did nothing to manually format the code.
See how the use of indentation helps visually identify the beginning and ending of procedures, functions, and loops.
The reason that the first pass through the table reports the VAT data as zero is that you pass V_Percent to Get_Pop as the actual for the out parameter Item, but you never assign to Item, with the result that the compiler assigns something to it on exit from the procedure, overwriting the value of V_Percent which you already assigned explicitly.
Get_Pop can’t logically have a single out parameter, because its job (as you’ve coded it) is to read four values. Either no parameters at all, or give each required value its own out parameter, or create a record type to hold all 4 values.

Ada - Accessibility check raised within a procedure

I previously asked a question regarding accessibility checks being raised in Ada, which #Brian Drummond was kind enough to awnser. The accessibility check was in a function, now I have a similair problem within a procedure; any guidance as to why this is would be greatly appreciated.
The code I am working on has been taken from here: https://github.com/raph-amiard/ada-synth-lib
The code in main file below is from the the Simple_Sine example which can be found here:
https://github.com/raph-amiard/ada-synth-lib/blob/master/examples/simple_sine.adb
My main file looks like this:
with Write_To_Stdout;
with Command; use Command;
with Effects; use Effects;
with Sound_Gen_Interfaces; use Sound_Gen_Interfaces;
with Utils; use Utils;
procedure main is
pragma Suppress (Accessibility_Check);
BPM : Natural := 15;
Notes : Notes_Array :=
To_Seq_Notes ((C, G, F, G, C, G, F, A, C, G, F, G, C, G, F, G), 400, 4);
function Simple_Synth
(S : access Simple_Sequencer; Tune : Integer := 0; Decay : Integer)
return access Mixer
is
(Create_Mixer
((0 => (Create_Sine (Create_Pitch_Gen (Tune, S)), 0.5)),
Env => Create_ADSR (5, 50, Decay, 0.5, S)));
Volume : Float := 0.9;
Decay : Integer := 800;
Seq : access Simple_Sequencer;
Sine_Gen : access Mixer;
Main : constant access Mixer := Create_Mixer (No_Generators);
begin
for I in -3 .. 1 loop
Seq := Create_Sequencer (16, BPM, 1, Notes);
Sine_Gen := Simple_Synth (Seq, I * 12, Decay);
Main.Add_Generator (Sine_Gen, Volume);
BPM := BPM * 2;
Volume := Volume / 1.8;
Decay := Decay / 2;
end loop;
Write_To_Stdout (Main);
end main;
The error that's raised is this:
raised PROGRAM_ERROR : sound_gen_interfaces.adb:20 accessibility check failed
It is raised during a call to this procedure:
-- Register_Note_Generator --
-----------------------------
procedure Register_Simulation_Listener
(N : access I_Simulation_Listener'Class) is
begin
Simulation_Listeners (Simulation_Listeners_Nb) := N;
Simulation_Listeners_Nb := Simulation_Listeners_Nb + 1;
end Register_Simulation_Listener;
Which is line 20 of the code below:
with Ada.Containers.Vectors;
package body Sound_Gen_Interfaces is
package PA_Vectors
is new Ada.Containers.Vectors (Natural, Params_Scope);
Params_Aggregators : PA_Vectors.Vector;
function Current_FPA return Params_Scope is
(Params_Aggregators.Last_Element);
-----------------------------
-- Register_Note_Generator --
-----------------------------
procedure Register_Simulation_Listener
(N : access I_Simulation_Listener'Class) is
begin
Simulation_Listeners (Simulation_Listeners_Nb) := N;
Simulation_Listeners_Nb := Simulation_Listeners_Nb + 1;
end Register_Simulation_Listener;
---------------
-- Next_Step --
---------------
procedure Next_Steps is
begin
for I in 0 .. Simulation_Listeners_Nb - 1 loop
Simulation_Listeners (I).Next_Step;
end loop;
end Next_Steps;
----------------
-- Base_Reset --
----------------
procedure Base_Reset (Self : in out Generator) is
begin
null;
end Base_Reset;
--------------------
-- Reset_Not_Null --
--------------------
procedure Reset_Not_Null (Self : Generator_Access) is
begin
if Self /= null then
Self.Reset;
end if;
end Reset_Not_Null;
--------------------
-- Reset_Not_Null --
--------------------
procedure Reset_Not_Null (Self : Note_Generator_Access) is
begin
if Self /= null then
Self.Reset;
end if;
end Reset_Not_Null;
--------------------------
-- Compute_Fixed_Params --
--------------------------
procedure Compute_Params (Self : in out Generator) is
procedure Internal (Self : in out Generator'Class);
procedure Internal (Self : in out Generator'Class) is
begin
for C of Self.Children loop
if C /= null then
if C.Is_Param then
Add_To_Current (C);
end if;
Internal (C.all);
end if;
end loop;
end Internal;
begin
Self.Parameters := new Params_Scope_Type;
Enter (Self.Parameters);
Internal (Self);
Leave (Self.Parameters);
end Compute_Params;
-----------
-- Enter --
-----------
procedure Enter (F : Params_Scope) is
begin
Params_Aggregators.Append (F);
end Enter;
-----------
-- Leave --
-----------
procedure Leave (F : Params_Scope) is
begin
pragma Assert (F = Current_FPA);
Params_Aggregators.Delete_Last;
end Leave;
--------------------
-- Add_To_Current --
--------------------
procedure Add_To_Current (G : Generator_Access) is
use Ada.Containers;
begin
if Params_Aggregators.Length > 0 then
Current_FPA.Generators.Append (G);
end if;
end Add_To_Current;
------------------
-- All_Children --
------------------
function All_Children
(Self : in out Generator) return Generator_Array
is
function All_Children_Internal
(G : Generator_Access) return Generator_Array
is
(G.All_Children) with Inline_Always;
function Is_Null (G : Generator_Access) return Boolean
is (G /= null) with Inline_Always;
function Cat_Arrays
is new Generator_Arrays.Id_Flat_Map_Gen (All_Children_Internal);
function Filter_Null is new Generator_Arrays.Filter_Gen (Is_Null);
S : Generator'Class := Self;
use Generator_Arrays;
begin
return Filter_Null (S.Children & Cat_Arrays (Filter_Null (S.Children)));
end All_Children;
----------------
-- Get_Params --
----------------
function Get_Params
(Self : in out Generator) return Generator_Arrays.Array_Type
is
use Generator_Arrays;
function Internal
(G : Generator_Access) return Generator_Arrays.Array_Type
is
(if G.Parameters /= null
then Generator_Arrays.To_Array (G.Parameters.Generators)
else Generator_Arrays.Empty_Array) with Inline_Always;
function Cat_Arrays
is new Generator_Arrays.Id_Flat_Map_Gen (Internal);
begin
return Internal (Self'Unrestricted_Access)
& Cat_Arrays (Self.All_Children);
end Get_Params;
----------------------
-- Set_Scaled_Value --
----------------------
procedure Set_Scaled_Value
(Self : in out Generator'Class; I : Natural; Val : Scaled_Value_T)
is
V : Float :=
(if Self.Get_Scale (I) = Exp
then Exp8_Transfer (Float (Val)) else Float (Val));
Max : constant Float := Self.Get_Max_Value (I);
Min : constant Float := Self.Get_Min_Value (I);
begin
V := V * (Max - Min) + Min;
Self.Set_Value (I, V);
end Set_Scaled_Value;
end Sound_Gen_Interfaces;
Any help as to why this is happening would be greatly appreciated.
Thank you
What you're seeing here is the result of (over-)using anonymous access types (discussed in ARM 3.10.2, informally known as the “Heart of Darkness” amongst the maintainers of Ada).
I don't think there's a simple way around this (aside from using -gnatp, as we found earlier, to suppress all checks; though perhaps you might have luck with
pragma Suppress (Accessibility_Check);
in the units where there's a problem).
I managed to get a build without Program_Errors with a fairly brutal hack, changing the anonymous access I_Simulation_Listener'Class to the named Simulation_Listener_Access throughout and, for example,
function Create_Simple_Command
(On_Period, Off_Period : Sample_Period;
Note : Note_T) return access Simple_Command'Class
is
begin
return N : constant access Simple_Command'Class
:= new Simple_Command'(Note => Note,
Buffer => <>,
On_Period => On_Period,
Off_Period => Off_Period,
Current_P => 0)
do
Register_Simulation_Listener (N);
end return;
end Create_Simple_Command;
to
function Create_Simple_Command
(On_Period, Off_Period : Sample_Period;
Note : Note_T) return access Simple_Command'Class
is
Command : constant Simulation_Listener_Access
:= new Simple_Command'(Note => Note,
Buffer => <>,
On_Period => On_Period,
Off_Period => Off_Period,
Current_P => 0);
begin
Register_Simulation_Listener (Command);
return Simple_Command (Command.all)'Access;
end Create_Simple_Command;
Ideally I'd have thought about having Create_Simple_Command returning a named access type too.
You can see where I got to at Github.

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;

Resources