Defining a bag Multiset with elements and multiplicity pairs In Ada - ada

I have to meet a specification which says that I have to write a bag type which is a record with discriminant , which is the maximum capacity of the bag in the record I have to put an array that contains element and multiplicity pairs , I have written some code and updated it as well
type TArray is array (Integer range <>) of Elem,Multi;
type Bag (Max : Positive) is record
Data : TArray (1 .. Max);
Pointer : Natural := 0;
end record;
The code has been updated please check and suggest

The answer is quite simple : if you don't have any error messages at compilation phase, it's ok.
Well according to what you described, your code looks good. I have a question about it.
Do you plan to have negative indexes on your type TArray ? If not, why don't you use the same type as your discriminant ?
The only thing is that you'll have only half the values using Positive instead of Integer but in your record, you anyway already start at 1 and thus have the same range as Positive.

Related

How to add index to array in Ada, like append for array in Java?

I want to append an index to an Integer array during a loop. Like add 3 to [1,2] and get an array like [1,2,3]. I don't know how to write it in the format and I cannot get the answer on the Internet.
You can use Vectors to do the something similar using the & operator. You can access the individual elements just like an array, though you use () instead of []. Or you can just use a for loop and get the element directly.
See the below example:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
procedure jdoodle is
-- Create the package for a vector of integers
package Integer_Vectors is new Ada.Containers.Vectors(Positive,Integer);
-- Using subtype to make just the Vector type visible
subtype Vector is Integer_Vectors.Vector;
-- Make all the primitive operations of Vector visible
use all type Vector;
-- Create a variable
V : Vector;
begin
-- Add an element to the vector in each iteration
for i in 1..10 loop
V := V & i;
end loop;
-- Show the results
for Element of V loop
Put_Line(Element'Image);
end loop;
-- Print the 2nd element of the array
Put_Line(Integer'Image(V(2)));
end jdoodle;
Ada arrays are first class types, even when the array type is anonymous. Thus a 2-dimensional array is a different type than a 3-dimensional array. Furthermore, arrays are not defined by directly specifying the number of elements in a dimension as they are in languages derived from C.
For instance, if you define a 2-dimensional array such as
type array_2d is array (Integer range 0..1, Integer range 1..2);
You have defined an array with a first range of 0..1 and a second range of 1..2. This would be a square matrix containing 4 elements.
You cannot simply add another dimension to an object of the type array_2d described above. Such an array must be of a different type.
Furthermore, one cannot change the definition of an array object after it is created.
Ok, so while this is a simple question it gets into the interesting details of design very quickly. The first thing is that an array "always knows its bounds" -- this language design element impacts the usage of the language profoundly: instead of having to pass the length of the array as a parameter, and possibly going out-of-sync, like in C you simply pass the array and let the "it knows its bounds" take care of the management.
Second, the Array is always static-length, meaning it cannot be changed once created. Caveat: Creating an array can be done in a "dynamic" setting, like querying user-input.
Third, if the subtype is unconstrained than it can have "variable" length. -- This means that you can have something like Type Integer_Vector is Array(Positive Range <>) of Integer and have parameters that operate on any size value passed in (and return-values that can be any size). This, in turn, means that your handling of such subtypes tends itself toward the more general.
Fourth, all of these apply and combine so that a lot of the 'need' for dynamically sized arrays aren't needed -- yes, there are cases where it is needed, or where it is more convenient to have a single adjustable object; this is what Ada.Containers.Vectors addresses -- but in the absence of needing a truly dynamically-sizing object you can use processing for achieving your goals.
Consider the following example:
Type Integer_Vector is Array(Positive range <>) of Integer;
Function Append( Input : Integer_Vector; Element : Integer ) return Integer_Vector is
( Input & Element );
X : Constant Integer_Vector:= (1,2);
Y : Integer_Vector renames Append(X, 3);
These three design choices combine to allow some intere

Why does GNAT reject an array type with a default discriminant value?

This (declaration) code raises a storage error:
type Vector is array (Integer range <>) of Integer;
type Array_Starting_At_One (Max : Integer := 0) is
record
Mat : Vector (1 .. Max);
end record;
X : Array_Starting_At_One;
I can't figure out why. If I specify the default, as in X : Array_Starting_At_One (Max => 0); the error disappears, although the Array_Starting_At_One type specification still triggers a warning that creation of such objects may raise Storage_Error.
I am not even trying to store a single bit, so this error doesn't make any sense to me:
raised STORAGE_ERROR : System.Memory.Alloc: object too large
When a variable is declared using the default discriminant, then the discriminant can later be changed via an assignment. This means that the compiler (at least GNAT does this) will allocate (on the stack) enough room to hold an object with any discriminant up to the maximum allowed value (Integer'Last in this case).
Either increase your stack size (not neccessarily recommended), or use a different subtype more suited to your problem:
subtype Foo is Integer range 1..10;
type Vector is array (Foo range <>) of Integer;
Certainly any array with an index range of 1..Integer'Last could raise Storage_Error. The compiler is warning you of this possibility. Try restricting the index range for your array type such as:
subtype Indices is Integer range 0..1024;
type Vector is Array (Indices range <>) of Integer;
type Array_Ending_At (Max : Indices := 0) is
record
Mat : Vector(1..Max);
end record;
As you notice, this is a compiler issue. The Janus/Ada compiler would accept your code without complaint or run-time exception.
Your variable X is an unconstrained object; the discriminant (and so the size of the object) can be changed by full-record assignment. The ARM is silent about how such things should be implemented. GNAT has chosen to allocate enough space for the largest possible value; since X is allocated on the stack, there will not be enough space for it with GNAT's default stack size (if you allocate it on the heap, you might not have a problem). Janus instead allocates only enough space for the current value, which may result in X.Mat being implicitly allocated on the heap.
The Janus way is more developer-friendly and acceptable for most applications, where the developer doesn't care where things are allocated, but there are situations where implicit heap allocation cannot be accepted, so the GNAT way is more general.

Ada "constant" keyword memory usage

Consider the following code :
My_Constant : constant := 2;
Is "My_Constant" a variable or a C language macro-like, so does it have a storage in memory ?
Note that constant just means that you cannot modify the object. It doesn't necessarily mean that the value of the constant is known at compile time. So there are three cases to consider:
(1) A constant with a type, whose value is known at compile time:
My_Constant : constant Integer := 3;
In this case, there's no reason for the compiler to allocate memory for the constant; it can use the value 3 whenever it sees My_Constant (and is likely to use 3 as the immediate operand of instructions, where possible; if it sees something like My_Constant * 2 then it can use the value 6 as an immediate operand). The compiler is allowed to allocate memory for the constant, but I don't think any decent compiler would do so, in a simple case like this with a small number. If it were a really large number that wouldn't fit into an immediate operand, it might make more sense to allocate space for the number somewhere (if it can do so in a way that saves on code space).
In a more complex case:
My_Record_Constant : constant Rec := (Field_1 => 100, Field_2 => 201, Field_3 => 44);
Here, a good compiler can decide whether or not to store the constant in memory based on how it's used. If the only uses are accesses of individual fields (My_Record_Constant.Field_1), the compiler could replace those with the integer values, as if they were integer constants, and there would be no need to store the entire record in memory.
However, using aliased will cause any constant to be forced into memory:
My_Constant : aliased constant Integer := 3;
Now memory has to be allocated because the program could say My_Constant'Access (the access type has to be access constant Integer).
(2) A constant whose value is not known at compile time:
My_Constant : constant Integer := Some_Function_Call (Parameter_1);
The function is called once, when the integer's declaration is elaborated. Since it is not a macro expansion, uses of My_Constant do not generate calls to the function. Example:
procedure Some_Procedure is
My_Constant : constant Integer := Some_Function_Call (Parameter_1);
begin
Put_Line (Integer'Image (My_Constant));
Put_Line (Integer'Image (My_Constant));
end Some_Procedure;
Some_Function_Call is called each time Some_Procedure is called, but it is called once, not two or three times.
Most likely, this requires the value to be stored in memory to hold the function result, so space will be allocated for My_Constant. (This still isn't a requirement. If a good optimizing compiler can somehow figure out that Some_Function_Call will return a known value, it can use that information.)
(3) A named number. This is the example you have, where there is no type:
My_Constant : constant := 2;
The language rules say the value must be known at compile time. This is the equivalent of using that number every time My_Constant is seen, so it's the closest thing to a C macro you're going to get in Ada. But the effect is basically the same as in (1) [except with fewer restrictions on type compatibility]. The compiler probably will not allocate space for it, but it might do so for a larger value. Note that this syntax is allowed only for numeric values (integer or real).
Another variation on option (1) above is for a constant array.
primes : constant array(integer range <>) of integer := (1, 3, 5, 7, 11, 13, 17, 19, 23);
If the compiler can see it being accessed by an index, it will have to store the array.
I doubt the compiler writers would try and special case any other obscure corner condition to save some memory - they have enough other special corner cases to worry about in Ada!

Stackoverflow with specialized Hashtbl (via Hashtbl.make)

I am using this piece of code and a stackoverflow will be triggered, if I use Extlib's Hashtbl the error does not occur. Any hints to use specialized Hashtbl without stackoverflow?
module ColorIdxHash = Hashtbl.Make(
struct
type t = Img_types.rgb_t
let equal = (==)
let hash = Hashtbl.hash
end
)
(* .. *)
let (ctable: int ColorIdxHash.t) = ColorIdxHash.create 256 in
for x = 0 to width -1 do
for y = 0 to height -1 do
let c = Img.get img x y in
let rgb = Color.rgb_of_color c in
if not (ColorIdxHash.mem ctable rgb) then ColorIdxHash.add ctable rgb (ColorIdxHash.length ctable)
done
done;
(* .. *)
The backtrace points to hashtbl.ml:
Fatal error: exception Stack_overflow Raised at file "hashtbl.ml",
line 54, characters 16-40 Called from file "img/write_bmp.ml", line
150, characters 52-108 ...
Any hints?
Well, you're using physical equality (==) to compare the colors in your hash table. If the colors are structured values (I can't tell from this code), none of them will be physically equal to each other. If all the colors are distinct objects, they will all go into the table, which could really be quite a large number of objects. On the other hand, the hash function is going to be based on the actual color R,G,B values, so there may well be a large number of duplicates. This will mean that your hash buckets will have very long chains. Perhaps some internal function isn't tail recursive, and so is overflowing the stack.
Normally the length of the longest chain will be 2 or 3, so it wouldn't be surprising that this error doesn't come up often.
Looking at my copy of hashtbl.ml (OCaml 3.12.1), I don't see anything non-tail-recursive on line 54. So my guess might be wrong. On line 54 a new internal array is allocated for the hash table. So another idea is just that your hashtable is just getting too big (perhaps due to the unwanted duplicates).
One thing to try is to use structural equality (=) and see if the problem goes away.
One reason you may have non-termination or stack overflows is if your type contains cyclic values. (==) will terminates on cyclic values (while (=) may not), but Hash.hash is probably not cycle-safe. So if you manipulate cyclic values of type Img_types.rgb_t, you have to devise your one cycle-safe hash function -- typically, calling Hash.hash on only one of the non-cyclic subfields/subcomponents of your values.
I've already been bitten by precisely this issue in the past. Not a fun bug to track down.

Returning a variable-sized matrix from a function in Ada

I'm trying to learn Ada for a course at the University, and I'm having a lot of problems wrapping my head around some of the ideas in it.
My current stumbling block: Let's say I have a function which takes a Matrix (just a 2-dimensional array of Integers), and returns a new, smaller matrix (strips out the first row and first column).
I declare the matrix and function like this:
type MATRIX is array(INTEGER range <>, INTEGER range <>) of INTEGER;
function RemoveFirstRowCol (InMatrix: in MATRIX) return MATRIX is
Then I decide on the size of the Matrix to return:
Result_matrix: MATRIX (InMatrix'First(1) .. InMatrix'Length(1) - 1, InMatrix'First(2) .. InMatrix'Length(2) - 1);
Then I do the calculations and return the Result_matrix.
So here's my problem: when running this, I discovered that if I try to return the result of this function into anything that's not a Matrix declared with the exact proper size, I get an exception at runtime.
My question is, am I doing this right? It seems to me like I shouldn't have to know ahead of time what the function will return in terms of size. Even with a declared Matrix bigger than the one I get back, I still get an error. Then again, the whole idea of Ada is strong typing, so maybe this makes sense (I should know exactly the return type).
Anyways, am I doing this correctly, and is there really no way to use this function without knowing in advance the size of the returned matrix?
Thanks,
Edan
You don't need to know the size of the returned matrix in advance, nor do you need to use an access (pointer) type. Just invoke your function in the declarative part of a unit or block and the bounds will be set automatically:
procedure Call_The_Matrix_Reduction_Function (Rows, Cols : Integer) is
Source_Matrix : Matrix(1 .. Rows, 1 .. Cols);
begin
-- Populate the source matrix
-- ...
declare
Result : Matrix := RemoveFirstRowCol (Source_Matrix)
-- Result matrix is automatically sized, can also be declared constant
-- if appropriate.
begin
-- Process the result matrix
-- ...
end;
end Call_The_Matrix_Reduction_Function;
Caveat: Since the result matrix is being allocated on the stack, you could have a problem if the numbers of rows and columns are large.
Because your MATRIX type is declared with unbound indexes, the type is incomplete. This means that it can be returned by a function. In this case, this acts as it were pointer. Of course the compiler does not know the exact indexes in compile time, the result matrix will always be allocated in heap.
Your solution should be working. The only problem is when you create the result matrix is, that it will work only if the original matrix index starts with 0.
m:MATRIX(11..15,11..20);
In this case m'first(1) is 11, m'length(1) is 5! So you get:
Result_matrix:MATRIX(11..4,11..9);
which is CONSTRAINT_ERROR...
Use the last attribute instead. Even if you usually use with 0 index.
But remember, you do not need to use pointer to the MATRIX because the MATRIX is also incomplete, and that's why it can be used to be returned by a function.
The caller knows the dimensions of the matrix it passes to your function, so the caller can define the type of the variable it stores the function's return value in in terms of those dimensions. Does that really not work?
your function cannot know the size of the result matrix in compile time
you need to return a pointer to the new matrix :
type Matrix is array (Positive range <>, Positive range <>) of Integer;
type Matrix_Ptr is access Matrix;
-- chop the 1'th row and column
function Chopmatrix (
Inputmatrix : in Matrix )
return Matrix_Ptr is
Returnmatrixptr : Matrix_Ptr;
begin
-- create a new matrix with is one row and column smaller
Returnmatrixptr := new Matrix(2 .. Inputmatrix'Last, 2.. Inputmatrix'Last(2) );
for Row in Inputmatrix'First+1 .. Inputmatrix'Last loop
for Col in Inputmatrix'First+1 .. Inputmatrix'Last(2) loop
Returnmatrixptr.All(Row,Col) := Inputmatrix(Row,Col);
end loop;
end loop;
return Returnmatrixptr;
end Chopmatrix ;

Resources