How-to define array of generic elements - arrays

With help from many sources, I have a working generic thing for a ring-buffer, with push and read of single elements:
q.ads:
generic
Q_SIZE : POSITIVE;
type T is private;
package Q is
subtype SIZE_TYPE is NATURAL range 0 .. Q_SIZE;
subtype Q_INDEX_TYPE is SIZE_TYPE range 1 .. SIZE_TYPE'last;
type Q_ARRAY_TYPE is array (Q_INDEX_TYPE) of T;
procedure INITIALIZE;
procedure PUSH(element : T);
function READ return T;
end Q;
q.adb:
package body Q is
Q_ARRAY : Q_ARRAY_TYPE;
TAIL : Q_INDEX_TYPE;
HEAD : Q_INDEX_TYPE;
...
end Q;
My test program instantiates the above for bytes and exercises the ring. It is basically as follows:
package body main is
package RING is new Q (15, UNSIGNED.BYTE);
procedure TEST is
byteval : UNSIGNED.BYTE;
begin
byteval := 16;
RING.PUSH(byteval);
...
I would now like to add the ability to pass an array of T in. I've add this to the ADS and ADB files:
procedure PUSH_ARRAY(DATA_ARRAY : Q_ARRAY_TYPE; COUNT : SIZE_TYPE);
My problem is in the test program. I've changed it to this:
BYTE_ARRAY : array (1 .. 10) of UNSIGNED.BYTE;
procedure TEST is begin
-- initialize the first 5 elements of BYTE_ARRAY, then
RING.PUSH_ARRAY(BYTE_ARRAY, 5);
this last line gives me an error message: expected type Q_ARRAY_TYPE defined at Q.ADS:xx. How do I pass a BYTE ARRAY to my method which expects an instance of the generic array?

Question: What is the purpose of SIZE_TYPE?
In Ada, the 'Length attribute will return the size of the array in Natural (the nonnegative Integer subtype). With that in mind, it doesn't seem to make sense to declare an extra subtype with an extra value for the index.
Ring_Buffer.ads
Generic
Type T is private;
Default : T;
Size : Positive;
Package Ring_Buffer is
SubType Index is Positive range 1..Size;
Type Ring is private;
Function Length( Obj : Ring ) return Natural;
Function Pop( Obj : in out Ring ) return T
with Pre => Length(Obj) in Positive,
Post => Length(Obj'Old)-1 = Length(Obj); --' --Highlight fix
Procedure Push( Obj : in out Ring; Item : in T )
with Pre => Length(Obj) < Size,
Post => Length(Obj'Old)+1 = Length(Obj); --'
Private
Type Internal_Data is Array(Index) of T;
Type Ring is record
Start : Positive:= Internal_Data'First; --'
Size : Natural:= 0;
Data : Internal_Data:= (Others => Default);
end record
with Type_Invariant => Ring.Size <= Size;
Function Length( Obj : Ring ) return Natural is
( Obj.Size );
End Ring_Buffer;
Ring_Buffer.adb
Package Body Ring_Buffer is
Function Pop( Obj : in out Ring ) return T is
Begin
return Result : constant T := Obj.Data(Obj.Start) do
Obj.Size:= Natural'Pred( Obj.Size ); --'
Obj.Start:= (if Obj.Start = Size then 1 else Obj.Start + 1);
end return;
End Pop;
Procedure Push( Obj : in out Ring; Item : in T ) is
Begin
Obj.Data( Natural'Succ((Obj.Start-1) + Obj.Size mod Size) ):= Item; --'
Obj.Size:= Obj.Size + 1;
End Push;
End Ring_Buffer;

Related

how do I insert values while using Hash.Lib while using while loop?

I have the following code... How would I be able to insert values in the array list with different indexes while its looping inside of a while loop? from the 2nd function(HashMine(CarID1))
local function HistoryHash() -- This function is to print out the Hashes "Mined" using Hash.Lib
for Hashindex = 1, #HashHistory do
print("Hash "..Hashindex..":", HashHistory[Hashindex])
end
end
--Mines the BTC pending transaction
local function HashMine(CarID1)
while stringtohash:sub(1,2) ~= "00" do
STRINGTOHASH = stringtohash..HASHNUMBER
stringtohash = HASHLIBRARY.sha256(STRINGTOHASH)
HASHNUMBER = HASHNUMBER + 1
wait(1)
table.insert()
end
HashGUI.Text = stringtohash
PendingTextGui.Text = ""
local CarID1 = CarBought
if CarID1 == 1 then
ConfirmedText.Text = ("Car1 ".. game.Workspace.Cars.Car1Buy.Car1.Value .. "BTC To Malta Car Dealer from " .. Players:GetChildren()[1].Name)
AfterCarPurchase()
elseif CarID1 == 2 then
ConfirmedText.Text = ("Car2 ".. game.Workspace.Cars.Car2Buy.Car2.Value.. "BTC To Malta Car Dealer from " .. Players:GetChildren()[1].Name)
AfterCarPurchase()
elseif CarID1 == 3 then
ConfirmedText.Text = ("Car3 ".. game.Workspace.Cars.Car3Buy.Car3.Value .. "BTC To Malta Car Dealer from " .. Players:GetChildren()[1].Name)
end
AfterCarPurchase()
end
table.insert() will cause the error message
bad argument #1 to 'insert' (table expected, got no value)
According to the Lua 5.4 Reference Manual - table.insert, it is mandatory to provide the table you want to insert to and the value you want to to insert into that table.
table.insert (list, [pos,] value)
Inserts element value at position pos in list, shifting up the
elements list[pos], list[pos+1], ···, list[#list]. The default value
for pos is #list+1, so that a call table.insert(t,x) inserts x at the
end of the list t.
If you want to assign a value to a specific table index you need to use indexing assignmet t[key] = value

List of valid operations in Tensorflow

I am using (learning) Tensorflow through the eager C API, or to be even more precise through a FreePascal wrapper around it.
When I want to do e.g. a matrix multiplication, I call
TFE_Execute(Op, #OutTensH, #NumOutVals, Status);
where Op.Op_Name is 'MatMul'. I have a couple of other instructions figured out, e.g. 'Transpose', 'Softmax', 'Inv', etc., but I do not have a complete list. In particular I want to get the determinant of a matrix, but cannot find it (assume it exists). I tried to find it on the web, as well as in the source on GitHub, but no success.
In Python there is tf.linalg.det, but already in C++ API I do not find it.
Could someone direct me to a place where I can find a complete list of supported operations?
Can someone tell me how to calculate the determinant with Tensorflow?
Edit: On Gaurav's request I attach a small program. As said above, it is in Pascal, and calls the C API through a wrapper. I therefore copied also the relevant part of the wrapper here (full version: https://macpgmr.github.io/). The set-up works, the "only" question is that I do not find a list of supported operations.
// A minimal program to transpose a matrix
program test;
uses
SysUtils,
TF;
var
Tensor:TTensor;
begin
Tensor:=TTensor.CreateSingle([2,1],[1.0,2.0]);
writeln('Before transpose ',Tensor.Dim[0],' x ',Tensor.Dim[1]); // 2 x 1
Tensor:=Tensor.Temp.ExecOp('Transpose',TTensor.CreateInt32([1,0]).Temp);
writeln('After transpose ',Tensor.Dim[0],' x ',Tensor.Dim[1]); // 1 x 2
FreeAndNil(Tensor);
end.
// extract from TF.pas ( (C) Phil Hess ). It basically re-packages the operation
// and calls the relevant C TFE_Execute, with the same operation name passed on:
// in our case 'Transpose'.
// I am looking for a complete list of supported operations.
function TTensor.ExecOp(const OpName : string;
Tensor2 : TTensor = nil;
Tensor3 : TTensor = nil;
Tensor4 : TTensor = nil) : TTensor;
var
Status : TF_StatusPtr;
Op : TFE_OpPtr;
NumOutVals : cint;
OutTensH : TFE_TensorHandlePtr;
begin
Result := nil;
Status := TF_NewStatus();
Op := TFE_NewOp(Context, PAnsiChar(OpName), Status);
try
if not CheckStatus(Status) then
Exit;
{Add operation input tensors}
TFE_OpAddInput(Op, TensorH, Status);
if not CheckStatus(Status) then
Exit;
if Assigned(Tensor2) then {Operation has 2nd tensor input?}
begin
TFE_OpAddInput(Op, Tensor2.TensorH, Status);
if not CheckStatus(Status) then
Exit;
end;
if Assigned(Tensor3) then {Operation has 3rd tensor input?}
begin
TFE_OpAddInput(Op, Tensor3.TensorH, Status);
if not CheckStatus(Status) then
Exit;
end;
if Assigned(Tensor4) then {Operation has 4th tensor input?}
begin
TFE_OpAddInput(Op, Tensor4.TensorH, Status);
if not CheckStatus(Status) then
Exit;
end;
{Set operation attributes}
TFE_OpSetAttrType(Op, 'T', DataType); //typically result type same as input's
if OpName = 'MatMul' then
begin
TFE_OpSetAttrBool(Op, 'transpose_a', #0); //default (False)
TFE_OpSetAttrBool(Op, 'transpose_b', #0); //default (False)
end
else if OpName = 'Transpose' then
TFE_OpSetAttrType(Op, 'Tperm', Tensor2.DataType) //permutations type
else if OpName = 'Sum' then
begin
TFE_OpSetAttrType(Op, 'Tidx', Tensor2.DataType); //reduction_indices type
TFE_OpSetAttrBool(Op, 'keep_dims', #0); //default (False)
end
else if (OpName = 'RandomUniform') or (OpName = 'RandomStandardNormal') then
begin
TFE_OpSetAttrInt(Op, 'seed', 0); //default
TFE_OpSetAttrInt(Op, 'seed2', 0); //default
TFE_OpSetAttrType(Op, 'dtype', TF_FLOAT); //for now, use this as result type
end
else if OpName = 'OneHot' then
begin
TFE_OpSetAttrType(Op, 'T', Tensor3.DataType); //result type must be same as on/off
TFE_OpSetAttrInt(Op, 'axis', -1); //default
TFE_OpSetAttrType(Op, 'TI', DataType); //indices type
end;
NumOutVals := 1;
try
// **** THIS IS THE ACTUAL CALL TO THE C API, WHERE Op HAS THE OPNAME
TFE_Execute(Op, #OutTensH, #NumOutVals, Status);
// ***********************************************************************
except on e:Exception do
raise Exception.Create('TensorFlow unable to execute ' + OpName +
' operation: ' + e.Message);
end;
if not CheckStatus(Status) then
Exit;
Result := TTensor.CreateWithHandle(OutTensH);
finally
if Assigned(Op) then
TFE_DeleteOp(Op);
TF_DeleteStatus(Status);
{Even if exception occurred, don't want to leave any temps dangling}
if Assigned(Tensor2) and Tensor2.IsTemp then
Tensor2.Free;
if Assigned(Tensor3) and Tensor3.IsTemp then
Tensor3.Free;
if Assigned(Tensor4) and Tensor4.IsTemp then
Tensor4.Free;
if IsTemp then
Free;
end;
end;
In the meantime, I found the description file of TensorFlow at: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/ops/ops.pbtxt. This includes all the operations and their detailed specification.
If someone is interested in a pascal interface to TF, I created one at https://github.com/zsoltszakaly/tensorflowforpascal.
Here is how you can obtain the list of valid operation names from Python:
from tensorflow.python.framework.ops import op_def_registry
registered_ops = op_def_registry.get_registered_ops()
valid_op_names = sorted(registered_ops.keys())
print(len(valid_op_names)) # Number of operation names in TensorFlow 2.0
1223
print(*valid_op_names, sep='\n')
# Abort
# Abs
# AccumulateNV2
# AccumulatorApplyGradient
# AccumulatorNumAccumulated
# AccumulatorSetGlobalStep
# AccumulatorTakeGradient
# Acos
# Acosh
# Add
# ...

How can I access a specific position of a JSON array in ada?

I have an array like this:
{
"Test":
[
0,
1,
2,
3,
4
]
}
I'm using GNATCOLL.JSON but I don't see any function to handle arrays and do something like this, for example:
integer = Test (2);
You might want to try:
function Get (Val : JSON_Value; Field : UTF8_String) return JSON_Array
and then
function Get (Arr : JSON_Array; Index : Positive) return JSON_Value
and then
function Get (Val : JSON_Value; Field : UTF8_String) return Integer
As an example, running the program:
main.adb
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded;
with GNATCOLL.JSON;
procedure Main is
use Ada.Text_IO;
use Ada.Strings.Unbounded;
Input : Unbounded_String := Null_Unbounded_String;
begin
-- Read.
declare
use Ada.Text_IO.Unbounded_IO;
Fd : File_Type;
begin
Open (Fd, In_File, "./example.json");
while not End_Of_File (Fd) loop
Input := Input & Unbounded_String'(Get_Line (Fd));
end loop;
Close (fd);
end;
-- Process.
declare
use GNATCOLL.JSON;
Root : JSON_Value := Read (Input);
Test : JSON_Array := Root.Get ("Test");
begin
for I in 1 .. Length (Test) loop
Put_Line ("Array element :" & Integer'Image (Get (Test, I).Get));
end loop;
end;
end Main;
with
example.json
{
"Test":
[
0,
1,
2,
3,
4
]
}
yields
$ ./main
Array element : 0
Array element : 1
Array element : 2
Array element : 3
Array element : 4

convert fixed size array to record of same size

This might be a nobrainer but as a novice i can not get my head around it.
I have a function returning a fixed size array. I am trying to convert this array into a record of the same size
The function signature is like this:
type Control is new UInt8_Array (1 .. 2);
function readControl (Command : Control) return Control;
I am trying to get the two bytes (UInt8) into the record Contro_Status_Bytes with the following definition:
type Control_Status_MSB is
record
RES_UP : Boolean;
QMAX_UP : Boolean;
BCA : Boolean;
CCA : Boolean;
CALMODE : Boolean;
SS : Boolean;
WDRESET : Boolean;
SHUTDOWNEN : Boolean;
end record;
for Control_Status_MSB use
record
RES_UP at 0 range 0 .. 0;
QMAX_UP at 0 range 1 .. 1;
BCA at 0 range 2 .. 2;
CCA at 0 range 3 .. 3;
CALMODE at 0 range 4 .. 4;
SS at 0 range 5 .. 5;
WDRESET at 0 range 6 .. 6;
SHUTDOWNEN at 0 range 7 .. 7;
end record;
type Control_Status_LSB is
record
VOK : Boolean;
RUP_DIS : Boolean;
LDMD : Boolean;
SLEEP : Boolean;
HIBERNATE : Boolean;
INITCOMP : Boolean;
end record;
for Control_Status_LSB use
record
VOK at 0 range 1 .. 1;
end record;
type Control_Status_Bytes is
record
HighByte : Control_Status_MSB;
LowByte : Control_Status_LSB;
end record;
I think it must be possible to convert the array to the record and vice versa without an unchecked conversion. But currently i am missing something.
Update: This might be an valid answer/way to do that i came up after reading #Simons answer.
function readControl (Command : Control) return Control_Status_Bytes is
CSB : Control_Status_Bytes;
begin
-- do stuff return UInt8_Array of size 2 as response
CSB.HighByte := response'First;
CSB.LowByte := response'Last;
return CSB;
end readControl;
Unchecked conversion is the usual way.
But for I/O ports and peripheral registers in MCUs (Atmel AVR, MSP430 etc) which can be addressed either as numbers, or arrays of booleans (or potentially, records) there's a hack ...
p1in : constant unsigned_8; -- Port 1 Input
Pragma Volatile(p1in);
Pragma Import(Ada, p1in); -- see ARM C.6 (13)
For p1in'Address use 16#20#;
p1in_bits : constant Byte; -- Port 1 Input Bits
Pragma Volatile(p1in_bits);
Pragma Import(Ada, p1in_bits);
For p1in_bits'Address use 16#20#;
This maps the inputs from I/O port 1 to the same address, viewed either as an 8 bit Unsigned or as a Byte (an array of 8 booleans).
The equivalent in your case would be something like
For Control_Status_Record'Address use Control_Status_Array`Address;
Note you probably need to attach "pragma volatile" to both views, as here, so that changes to one view aren't lost because the other view is cached in a register.
All in all, I recommend Unchecked_Conversion over this approach. It's designed for the job and avoids messing with Volatile.
It has to depend on what happens inside readControl, but couldn't you make it return the type you want directly?
function readControl (Command : Control) return Control_Status_Bytes;
(I expect that Command actually has some structure too?).
By the way, you only define the position of one component (VOK) in Control_Status_LSB, which leaves the rest up to the compiler.
The hint from #Simon Wright pointed me in the right direction.
This is what is use now and it works:
function convert (ResponseArray : Control) return Control_Status_Bytes is
Result : Control_Status_Bytes with
Import, Convention => Ada, Address => ResponseArray'Address;
begin
return Result;
end convert;

Ada Matchup Array

I have a function in ada which has to check a large array of booleans to return a (sparse) value. It is difficult to explain in words, so here's an 'ideal' solution that doesn't work in ada (note that I have more than 3 challenger types and tougher logic):
type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
type Challengers is array (ChallengerType) of Boolean;
pragma Pack(Challengers);
-- NOT legal, challengers is not an enumeration type
matchups : array (Challengers) of ChallengerType := (
-- Single challenger victories
(Rock => True, others => False) => Rock,
(Paper => True, others => False) => Paper,
(Scissors => True, others => False) => Scissors,
-- Double challenger victories
(Rock => True, Paper => True, others => False) => Paper,
(Rock => True, Scissors => True, others => False) => Rock,
(Paper => True, Scissors => True, others => False) => Scissors,
-- All the rest either are ambiguous (RPS) or suicided
others => None)
This is not legal in Ada, so I went with the more c-style version where my array was of Integer range 0..2#11111# and wrote a converter. However the code becomes much less clean (EG: (2#00101# => Scissors) is not as clear).
What would be the 'best' way to implement such a matchup array?
Summary: I want a mapping from the power set of an enumeration type to one of its values. IE: if my enumeration was {A,B,C} I would want a mapping from {{},{A},{B},{C},{A,B},{A,C},{B,C},{A,B,C}} to {A,B,C}. I also know in advance that most of the values in the mapping will be the same type, so the others keyword would be very nice to use. Currently I use binary indexing with '1' meaning that the specified enum element is present, but I wish I could be more explicit.
A somewhat simpler answer uses the Vector type from the Ada.Containers.Vectors package:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
use Type Ada.Containers.Count_Type;
procedure Mapping_Vector is
type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
type Challengers is array (ChallengerType) of Boolean with Pack;
package Challenge_Vect is new Ada.Containers.Vectors(Positive, ChallengerType);
use Challenge_Vect;
function Map_Challengers(Item : in Challengers) return Vector is
Result : Vector := Empty_Vector;
begin
for I in Item'Range loop
if Item(I) then
Result.Append(I);
end if;
end loop;
return Result;
end Map_Challengers;
Foo : Challengers := (Rock..Scissors => True, Others => False);
Mapped_Challengers : Vector := Map_Challengers(Foo);
begin
If Mapped_Challengers.Length > 0 then
for C of Mapped_Challengers loop
Put_Line(ChallengerType'Image(C));
end loop;
else
Put_Line("No challengers were TRUE");
end if;
end Mapping_Vector;
This is not a purely static solution, but it makes it possible to configure the mapping elegantly in terms of enumeration values, even if the mapping array in reality is indexed by a modular type:
Specification:
with Ada.Text_IO;
with Ada.Unchecked_Conversion;
generic
type Element_Type is (<>);
type Numeric_Type is mod <>;
package Set_With_Modular_Representation is
type Instance is array (Element_Type) of Boolean with Pack;
Empty_Set : constant Numeric_Type := 0;
E : Numeric_Type renames Empty_Set; -- Got a request not to use Ø.
function "&" (Left : in Instance;
Right : in Instance) return Numeric_Type;
function "&" (Left : in Numeric_Type;
Right : in Instance) return Numeric_Type;
function "&" (Left : in Element_Type;
Right : in Element_Type) return Numeric_Type;
function "&" (Left : in Numeric_Type;
Right : in Element_Type) return Numeric_Type;
private
pragma Assert (Numeric_Type'Modulus = 2 ** Instance'Size);
pragma Assert (Numeric_Type'Size = Instance'Size);
function Numeric is
new Ada.Unchecked_Conversion (Source => Instance,
Target => Numeric_Type);
function Numeric (Item : in Element_Type) return Numeric_Type;
end Set_With_Modular_Representation;
Implementation:
package body Set_With_Modular_Representation is
function "&" (Left : in Instance;
Right : in Instance) return Numeric_Type is
begin
return Numeric (Left) or Numeric (Right);
end "&";
function "&" (Left : in Numeric_Type;
Right : in Instance) return Numeric_Type is
begin
return Left or Numeric (Right);
end "&";
function "&" (Left : in Element_Type;
Right : in Element_Type) return Numeric_Type is
begin
return Numeric (Left) or Numeric (Right);
end "&";
function "&" (Left : in Numeric_Type;
Right : in Element_Type) return Numeric_Type is
begin
return Left or Numeric (Right);
end "&";
function Numeric (Item : in Element_Type) return Numeric_Type is
Buffer : Instance := (others => False);
begin
Buffer (Item) := True;
return Numeric (Buffer);
end Numeric;
end Set_With_Modular_Representation;
Demonstration:
with Ada.Command_Line;
with Ada.Text_IO;
with Set_With_Modular_Representation;
procedure Set_With_Modular_Representation_Demo is
type Outcomes is (Paper, Rock, Scissors, Suicide, None);
subtype Choices is Outcomes range Paper .. Scissors;
type Numeric_Choices is mod 2 ** 3;
package Choice_Set is
new Set_With_Modular_Representation (Element_Type => Choices,
Numeric_Type => Numeric_Choices);
use Choice_Set;
Mapping : array (Numeric_Choices) of Outcomes := (others => None);
begin
Set_Up_Mapping :
begin
-- Single challenger victories
Mapping (E & Rock) := Rock;
Mapping (E & Paper) := Paper;
Mapping (E & Scissors) := Scissors;
-- Double challenger victories
Mapping (Rock & Paper) := Paper;
Mapping (Rock & Scissors) := Rock;
Mapping (Paper & Scissors) := Scissors;
end Set_Up_Mapping;
Test :
declare
package Outcome_Text_IO is
new Ada.Text_IO.Enumeration_IO (Outcomes);
use Ada.Command_Line, Ada.Text_IO, Outcome_Text_IO;
Chosen : Numeric_Choices := E;
begin
for Index in 1 .. Argument_Count loop
Chosen := Chosen & Choices'Value (Argument (Index)); -- '
end loop;
Put ("Outcome: ");
Put (Mapping (Chosen));
New_Line;
end Test;
end Set_With_Modular_Representation_Demo;
NOT an answer... posting in case it helps someone see a way forward.
This expands on my comment above : as far as generating a discrete type from an array goes, it appears to work...
with Ada.Unchecked_Conversion;
package RPS is
type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
for ChallengerType use (Rock => 1, Paper => 2,Scissors => 4,Suicide => 8,None => 16);
type Challengers is array (ChallengerType) of Boolean with Pack;
type Challengers_int is range 0 .. 31;
function IDX is new Ada.Unchecked_Conversion(Challengers, Challengers_int);
Rock_Only : constant Challengers := (Rock => True, others => False);
Rock_IDX : constant Challengers_int := IDX(Rock_Only);
matchups : constant array (Challengers_int) of ChallengerType := (
-- Single challenger victories
-- Rock_IDX => Rock, -- fails
1 => Rock,
IDX((Paper => True, others => False)) => Paper, -- fails
IDX((Scissors => True, others => False)) => Scissors,
-- Double challenger victories
IDX((Rock => True, Paper => True, others => False)) => Paper,
IDX((Rock => True, Scissors => True, others => False)) => Rock,
IDX((Paper => True, Scissors => True, others => False)) => Scissors,
-- All the rest either are ambiguous (RPS) or suicided
others => None);
end RPS;
However, it fails to compile, dynamic or empty choice in aggregate must be the only choice at either of the commented lines in the array - even if the index is a constant rather than an expression.
A Case statement fails similarly :
case IDX(C) is
when Rock_IDX => return Rock;
when IDX((Paper => True, others => False)) => return Paper;
...
compiler reports:
rps.adb:11:10: choice given in case statement is not static
rps.adb:11:10: "Rock_IDX" is not a static constant (RM 4.9(5))
rps.adb:12:10: choice given in case statement is not static
rps.adb:12:10: non-static function call (RM 4.9(6,18)) rps.adb:12:14:
static expression must have scalar or string type (RM 4.9(2))
Here is one solution:
with Ada.Text_IO; use Ada.text_IO;
procedure Mapping_Question is
type ChallengerType is (Rock,Paper,Scissors,Suicide,None);
type Challengers is array (ChallengerType range <>) of Boolean with Pack;
Type Mapped_Challenges is array(Positive range <>) of ChallengerType;
function Map_Challengers(Item : Challengers) return Mapped_Challenges is
function Mapper(Item : Challengers) return Mapped_Challenges is
Single : Mapped_Challenges(1..1);
begin
for I in Item'Range loop
if Item(I) then
if I < Item'Last then
return I & Mapper(Item(ChallengerType'Succ(I)..Item'Last));
else
Single(Single'First) := I;
return Single;
end if;
end if;
end loop;
Single(Single'First) := None;
return Single;
end Mapper;
begin
return Mapper(Item);
end Map_Challengers;
procedure Print_Challenges(Item : Mapped_Challenges) is
begin
for I in Item'Range loop
Put_Line(ChallengerType'Image(Item(I)));
end loop;
end Print_Challenges;
Foo : Challengers(Rock..None) := (Rock..Scissors => True, Others => False);
begin
declare
Mapping : Mapped_Challenges := Map_Challengers(Foo);
begin
if Mapping'Length > 1 then
Print_Challenges(Mapping(Mapping'First..Mapping'Last - 1));
else
Print_challenges(Mapping);
end if;
end;
end Mapping_Question;
The following approach uses currying of sorts. To further simplify the example, I have shortened the enumeration type to three values. The solution is static, but the “elegance” of using the enumeration values directly is gone. If a static constant is not needed, then, as other approaches show, constant function tables F and T from ChallengerType into Boolean will be a possibility, the named components then appearing as T (Paper) => ... etc.
pragma Pure (Rps);
type ChallengerType is (Rock,Paper,None);
type Challengers1 is array (Boolean) of Challengertype;
type Challengers2 is array (Boolean) of Challengers1;
type Challengers3 is array (Boolean) of Challengers2;
Matchups : constant Challengers3 :=
-- Rock:
(True => --> Paper:
(True => --> None:
(False => Paper,
True => None),
False => --> None:
(others => Rock)),
-- Rock:
False => --> Paper:
(True => --> None:
(False => Paper,
True => None),
False =>
(others => None))
);
Solution using constants
This is not an elegant solution but it may help as a part of a better solution.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with System;
with System.Unsigned_Types;
procedure Main is
subtype S is System.Unsigned_Types.Unsigned range 0 .. 31;
use type S;
function Shift_Left (Value : S; Amount : Natural) return S renames System.Unsigned_Types.Shift_Left;
Rock : constant S := Shift_Left (1, 0);
Paper : constant S := Shift_Left (1, 1);
Scissors : constant S := Shift_Left (1, 2);
Suicide : constant S := Shift_Left (1, 3);
None : constant S := Shift_Left (1, 4);
P : array (S) of S := (others => None);
begin
P (Rock or Paper) := Paper;
P (Rock or Scissors) := Rock;
P (Paper or Scissors) := Scissors;
Put_Line ("P(Paper or Scissors) = Scissors");
Put ("P(");
Put (Integer (Paper), 0, 2);
Put (" or ");
Put (Integer (Scissors), 0, 2);
Put (") = P(");
Put (Integer (Paper or Scissors), 0, 2);
Put (") = ");
Put (Integer (P (Paper or Scissors)), 0, 2);
Put ("");
end;
Result
P(Paper or Scissors) = Scissors
P(2#10# or 2#100#) = P(2#110#) = 2#100#
Solution using enumeration
with Ada.Text_IO; use Ada.Text_IO;
with System;
with System.Unsigned_Types;
procedure Main is
type T is (Rock, Paper, Scissors, Suicide, None);
subtype S is System.Unsigned_Types.Unsigned range 0 .. 31;
use type S;
function B (Value : T) return S is (System.Unsigned_Types.Shift_Left (1, T'Pos (Value)));
function "&" (Left : T; Right : T) return S is (System.Unsigned_Types."or" (B (Left), B (Right)));
P : array (S) of T := (others => None);
begin
P (Rock & Paper) := Paper;
P (Rock & Scissors) := Rock;
P (Paper & Scissors) := Scissors;
Put_Line (T'Image (P (Rock & Paper)));
Put_Line (T'Image (P (Rock & Scissors)));
Put_Line (T'Image (P (Paper & Scissors)));
end;
Result
PAPER
ROCK
SCISSORS

Resources