I am mainly interested in stable sorting large arrays of strings. With real-world data I succeed in beating Tarray.sort by a factor 10-100 (even though the latter is not stable). As an exercise I tried to repeat this for simple types. Works fine. And then to bundle it into a generic class as in Tarray. Consider my starting point:
procedure sortArray(var values : array of byte); overload;
var temp : array of byte;
function getSequence(from: integer) : integer;
procedure doThis(...);
procedure doThat(...);
begin
setLength(temp,high(values)+1);
// actual code calling one of the procedures
end;
procedure sortArray(var values : array of double); overload;
var temp : array of double;
... etc ...
All the local subroutines have access to values and temp.
It seemed to me like an ideal situation to try out how generics work. In a generic class you can't have local subroutines, so these are all changed into methods. I then have:
type
TsimpleArray<T> = class
private
class function getSequence(var values,temp : array of T; from: integer) : integer;
class procedure doThis(var values,temp : array of T; ...);
...
public
class procedure sort(var values : array of T);
end;
Class functions can not have access to global temp storage, so that has to be passed as a parameter to all these methods, as well as the source array. All comaprisons if v>values[k] and the like to be changed into if Tcomparer<T>.default.compare(v,values[k])>0.
Well, it works. But it is now around 10 times slower then my simple procedure approach. (Still a lot faster than Tarray.sort.) I guess this is the prize we pay for better maintainable generic code. Or are there things I could do better?
Related
I have a program with tasks.
I want to generate two random numbers,
First: between 0.5 and 3.5
Second: between 4.5 and 6.5
I have to implement it in a protected unit:
with Ada.Text_IO, Ada.Numerics.Float_Random;
use Ada.Text_IO, Ada.Numerics.Float_Random;
procedure simulation is
.
.
.
protected Helper is
procedure generateFirstRandomNumber;
procedure generateSecondRandomNumber;
end Helper;
.
.
.
protected helper is
procedure generateFirstRandomNumber is
begin
...
end generateFirstRandomNumber;
procedure generateSecondRandomNumber is
begin
...
end generateSecondRandomNumber;
end helper;
.
.
.
begin
.
.
.
end simulation;
I have two entry points where I have to pass those values.
So how can I implement that two random number generators in the protected unit?
This is a sketch of part of a solution.
protected Helper is
procedure Generate_First_Random_Number (Result : out Float);
procedure Generate_Second_Random_Number (Result : out Float);
First, I hope I don't tread on your toes, but this is a more standard Ada style (Title_Case_With_Underscores).
Second, you need to get the results out somehow. You can't use a function, because functions aren't allowed to change the internal state of the PO, and (part of) that internal state is the random number generator.
Third, I'd have declared subtypes to represent the ranges you have to provide: subtype Lower_Range is Float range 0.5 .. 3.5;, for example.
private
Gen : Ada.Numerics.Float_Random.Generator;
Internal variables have to be declared in the private part of the protected spec. And, as a general rule, it's better to use fully-qualified names, so it's obvious where the object's coming from (personally, I make an exception for Ada.Text_IO).
end Helper;
protected body Helper is
You have to use the word body here.
procedure Generate_First_Random_Number (Result : out Float)
is
Rnd : constant Float := Ada.Numerics.Float_Random.Random (Gen);
Rnd is in the range 0.0 .. 1.0,
begin
Result := 3.0 * Rnd + 0.5;
but you want a result in 0.5 .. 3.5.
end Generate_First_Random_Number;
... and similarly for the other procedure.
Since it appears to be for your studies, I'll only point you in the right direction. The Ada Language Reference Manual includes a detailed description of the Ada standard libraries. These happen to include provisions for generating random floating point values with an equal distribution in a well-defined range.
I developed the following sorting algorithm but there are some run time errors that I cant figure out. The program terminates when it comes to the part of filling the array. I'm still a beginner in ada so I couldn't figure out where the problem is...
With Ada.Text_IO;
With Ada.Integer_Text_IO;
Use Ada.Integer_Text_IO;
Use Ada.Text_IO;
Procedure sort is
n,i,x : Integer;
-- Max_Heapify Function...
Procedure Max_Heapify ( i, n : integer) is
j, Temp : Integer;
begin
Temp:=Int_Acc(i);
j:=2*i;
if Temp>Int_Acc(j) then
elsif Temp<=Int_Acc(j) then
Int_Acc(j/2):=Int_Acc(j);
j:=2*j;
end if;
end loop;
Int_Acc(j/2):=Temp;
end Max_Heapify;
begin
Get(n);
for i in MyArr'range loop
Put_Line("Enter Element " & Integer'Image(i));
Get(MyArr(i));
end loop;
end;
end sort;
Thanks in advance :)
Your Problem is that you are insisting on writing Ada code using c - Style programming paradigms.
Firstly:
The declarations:
Type Arr is array(1..20) of Integer;
Type int_access is access Arr;
MyArr : int_access;
Where you use Int_Acc : in out int_access as parameters to procedures are useless in Ada. You are trying to pass a pointer to an array in (which you are doing!), but you should just pass your Type Arr as in out - The Ada compiler knows to do this as a pointer!
Secondly:
I cannot see where you actually allocate any memory to MyArr. This is a possible source of your runtime error. (when you write to or index an array that does not exist, i would expect to have a problem!)
Thirdly:
You seem to be mixing fixed length arrays with variable length input. If N > 20, you will have a problem.
Fourthly:
Insulting the language is not the best way of getting help from those who like it.
NWS has nailed it : there is a pointer, but no array there.
But it's clear that you have learned C, which leaves you with a lot to learn about other languages including Ada. There really are better ways of doing many things, that aren't taught to C programmers because C doesn't allow them.
Allocating variable sized arrays without pointers, malloc and free for example...
Type Arr is array(positive range <>) of Integer; -- of any size
begin
Put_Line("Enter Number Of Elements Of Array");
Get(n);
declare -- now we know the size
My_Arr : Arr(1 .. n);
begin -- My_Arr is in scope until the end of this block
...
end;
end sort;
Using the type system better...
Bad programming :
for i in 1 .. n loop
Get(MyArr(i));
end loop;
HeapSort(MyArr,n);
for i in 1 .. n loop
Put_Line(Integer'Image(MyArr(i)));
end loop;
This is bad because it violates the DRY principle : loop bounds repeated, and something that hopefully represents the array size passed around as a separate parameter... a maintenance nightmare if you decide to rename n to something meaningful for example.
better programming : Use the type system. Recognise that merely declaring an array has declared a new subtype of integer, representing the index of the array. You can access it as My_Arr'range, and the high bound as My_Arr'last.
for i in My_Arr'range loop
Get(MyArr(i));
end loop;
HeapSort(MyArr);
for i in My_Arr'range loop
Put_Line(Integer'Image(MyArr(i)));
end loop;
And accidents such as redefining n after the array declaration can no longer generate buffer overflows.
NOTE: Heapsort now gets its range from the array. (Max_Heapify may still need a separate parameter to operate on subsets of the array)
Arguably best - if it makes the intent clearer - name the index datatype explicitly and use it...
declare -- now we know the size
subtype My_Range is positive range 1 .. n;
My_Arr : Arr(My_Range);
begin -- My_Arr is in scope until the end of this block
for i in My_Range loop ...
And lastly, which do you prefer; a Storage_Error exception immediately the bug occurs (writing to memory you forgot to allocate) or something odd happening much later because something scribbled across another variable?
EDIT
Having cleared up the major issues two more subtle ones remain...
If I compile the modified program (in Gnat 4.8) I get several warnings : one of them is important and tells you exactly what the problem is...
Most of the warnings stem from the fact that
for i in My_Arr'range loop
declares its own loop variable i which hides any existing in-scope declaration. So you can tidy up the code by removing the unnecessary declarations.
What remains is:
sort.adb:51:28: warning: loop range may be null
sort.adb:51:28: warning: bounds may be wrong way round
The for loop bounds are empty ranges, reversed...
1 .. 3 declares a subtype with 3 values
reverse 1 .. 3 declares the same subtype and iterates backwards over it.
But 3 .. 1 declares an EMPTY subtype (containing NO valid values) so iterating over it - either way round - does precisely nothing.
Hopefully that is the missing part of the puzzle. I'm not clear why one faulty loop gets this warning while the other (at line 38) doesn't...
if j<n **and then** Int_Acc(j+1)>Int_Acc(j) then
j:=j+1;
I think you want just 'and' instead of 'and then,' although I haven't looked at Ada code in years.
Did that compile?
I have a system consisting out of several components that has to be attached to a bus. However to keep the system bus independent, I gave the system a generic bus port that I run through a bus specific module that translates between my system and a specific bus. Thus it is easy to connect the entire system to different buses by switching the translating module.
However I don't want to go through the hassle of connecting the system with the translation module every time. Thus I wonder if it is possible to instantiate a module from a generic parameter, and use its types for the outputs of the architecture.
The whole becomes clearer with a little illustration, my translator modules have the following "signature".
entity translate<BusName>
port(toSystem: out toSystem_t,
fromSystem: in fromSystem_t,
toBus: out to<BusName>_t,
fromBus: in from<BusName>_t
end entity;
I now want to build an entity containing the system and a translator, based on a generic, somewhat like so:
entity entireSystem is
generic(busType := translate<BusName>)
port(toBus: out to<BusName>_t,
fromBus: in from<BusName>_t)
end entity
architecture genArc of entireSystem is
signal toTrans: fromSystem;
signal fromTrans: toSystem;
begin
system: system(toTrans,fromTrans)
translator: translate<BusName>(
fromTrans,
toTrans,
toBus,
fromBus
)
end architecture;
My problems are:
Can I use a generic parameter to directly instantiate a component, or do I have to go the if generic=xxx generate path? Can I derive the type of ports from a generic parameter.
It would be best if I can use one generic parameter to determine the ports and the entity, so that one can not pick wrong types for an entity by accidents. It would be fine to derive the types from the generic parameter with a function.
I don't think so, no.
If you want to instantiate different things based on a generic, you have to use if..generate.
The only influence that generics can have on the types of ports is that of changing the width. You can't switch between (say) an integer and a boolean based on a generic.
Can I use a generic parameter to directly instantiate a component,
or do I have to go the if generic=xxx generate path?
You can use the generic map syntax in module instantiation to pass the bus size to your sub-components. i.e.:
u_system: system
generic map ( INPUT_WIDTH => INPUT_WIDTH, OUTPUT_WIDTH => OUTPUT_WIDTH )
port map ( ... )
In the top level, you will need to have two generics instead of one.
Alternatively, assuming that your top level component must have the same bus size as your sub-components, you can try to do this in a package file and define the bus size by using function call. i.e.:
package pack is
-- # Define support bus name here
constant BusA : integer := 0;
...
constant BusZ : integer := 25;
-- # Bus name here
constant busname : integer := BusA;
-- # Input and Output Width
constant IWIDTH : integer := getIWIDTH( busname )
constant OWIDTH : integer := getOWIDTH( busname )
-- # Function declaration
function getIWIDTH( ii: integer ) return integer;
function getOWIDTH( ii: integer ) return integer;
end pack;
package body pack is
function getIWIDTH( ii: integer ) return integer is
variable oo : integer;
begin
-- # get iwidth here
return oo;
end function;
function getOWIDTH( ii: integer ) return integer is
variable oo : integer;
begin
-- # get owidth here
return oo;
end function;
end package body;
Functions are obviously less verbose to write than entities. But it implies many drawbacks, including:
No generic keyword equivalent
Only one output possible
It appears that functions can be called recursively. May it not be the case with entities? If so, is there any good reason to use functions except for aesthetic purposes?
Functions can't create hardware directly - they have to exist within an architecture to do so. There's nothing to stop you putting all your functionality into a function (or procedure) and then just calling that within a process though.
Regarding some of your other points:
With procedures you can have multiple inout or out parameters.
Entities can recurse... Consider:
entity recurse is
generic (
depth : integer := 1;
param : integer := 3);
port (
a : in integer;
b : out integer);
end entity recurse;
architecture a1 of recurse is
signal c : integer;
begin
c <= a + 1;
bottom: if depth = param generate
b <= a + 1;
end generate bottom;
mid:if depth /= param generate
recurse_1: entity work.recurse
generic map (
param => param,
depth => depth+1)
port map (
a => c,
b => b);
end generate mid;
end architecture a1;
Not very useful, but it synthesises and simulates just fine.
And finally, of course you only use functions for aesthetic purposes (assuming you include maintainability and readability into the definition of aesthetic, which most programming types do in my experience). You only use enumerated types, entities, records and a whole host of other language features for 'aesthetic purposes'. Even assembly mnemonics are aesthetic! Maybe should return to toggling DIP switches :)
Functions in vhdl make the code easy to maintain and read. generally architectures are very big and while debugging if something is not working you can easily find the problematic function and correct it and no need to analyse the entire architecture body.
in case of small codes it's useless but in more big machines it makes you better understand if you consider it function wise.
There is no rule for this so all comments are welcome.
in short : the advantage of functions are
overloading
operators definition
overloading of operators therefore
Better Structure of code
I can see why you are confused, another good question would be why there's both procedure and function. (VHDL seems quite inelegant sometimes!)
That being said, I use both procedures and functions all the time, although mostly in testbenches. For example, for a testbench for a firewall system I made a while back I wrote a procedure called pd_tb_send_udp_packet() that I use repeatedly in the main process, e.g.,
pd_tb_send_udp_packet("10.10.10.2", 1234, false);
pd_tb_send_udp_packet("10.10.10.1", 1234, true);
pd_tb_send_udp_packet("10.10.10.1", 1235, false);
pd_tb_send_udp_packet("ff02:100::1", 1234, false);
pd_tb_send_udp_packet("ff02:101::1", 1234, true);
This procedure generates a random UDP packet with the given addr/port and sends it to the firewall system, then tests whether it is forwarded or not based on the final boolean parameter. Here are the first lines of it, where I use functions from a library:
if f_atvtb_is_ipv6_addr(dest_ip_addr) then
v_ipv6 := true;
v_ipv6_addr := f_atvtb_ipv6_addr(dest_ip_addr);
else
v_ipv6 := false;
v_ipv4_addr := f_atvtb_ip_addr(dest_ip_addr);
end if;
The latter two return 128 and 32 bit std_logic_vectors from the string input, respectively.
While I could probably do all this without using procedures and functions somehow, it would definitely be a lot more messy.
It was not me who wrote this code, it was the previous programmer. However, I noticed he didn't provide a decryption algorithm, rendering the encryption useless.
How can I decrypt this?
function Encrypt(jstr: String): String;
var
I: Integer;
A: Real;
begin
if Length(jstr) = 0 Then begin
Result := '';
Exit;
end;
A := 0;
for I := 0 To Length(jstr) do
A := A + (Ord(jstr[I]) * Pos(jstr[I],jstr)) / 33;
Result := FormatFloat('0000000000.0000000000',A);
if Pos(',',Result) > 0 then begin
Insert('.',Result,Pos(',',Result));
Delete(Result,Pos(',',Result),1);
end;
end;
Thanks!
It looks like a one way hash and hence is not reversible. For example, is the string is very big the result is still a string representation of a float.
That function cannot be reversed. Since it takes input of arbitrary length and returns output of finite length, simple information theory tells you the futility of attempting to write a general inverse. Even for shorter input strings it seems to me that different input strings can result in the same encrypted string.
Even as a hash this function seems very brittle to me due to the bizarre use of floating point code. If I were you I would replace this function with something more fit for purpose.
Finally, I recommend that you undertake a review of all code produced by this developer. The low quality of this code and algorithm suggests to me that everything that this developer touched is liable to have defects.