Hexa decimal conversions in Ada - hex

I need to convert Integer numbers to a 3 characters long String Hexa decimal, such that for 10 I need to get 00A. I'm not sure what is the most efficient way of doing this. This is what I have at the moment, however the following method adds a 16# to the output, increasing the length of the string.
function Integer2Hexa(Hex_Int : Integer) return String is
Hexa : String(1..3);
begin
Ada.Integer_Text_IO.Put(Hexa,Hex_Int,16);
return Hexa;
end Integer2Hexa;
Thanks in advance.

This is an implementation using the Ada Standard Library. It is simple, but it might be inefficient.
with Ada.Integer_Text_IO;
with Ada.Text_Io;
with Ada.Strings.Fixed;
procedure Dec2Hex is
function Integer2Hexa (Hex_Int : Integer; Width : Positive := 3)
return String is
Hex_Prefix_Length : constant := 3;
Hexa : String (1 .. Hex_Prefix_Length + Width + 1);
Result : String (1 .. Width);
Start : Natural;
begin
Ada.Integer_Text_IO.Put (Hexa,Hex_Int, 16);
Start := Ada.Strings.Fixed.Index (Source => Hexa, Pattern => "#");
Ada.Strings.Fixed.Move
(Source => Hexa (Start + 1 .. Hexa'Last - 1),
Target => Result,
Justify => Ada.Strings.Right,
Pad => '0');
return Result;
end Integer2Hexa;
begin
Ada.Text_Io.Put_Line (Integer2Hexa (10));
Ada.Text_Io.Put_Line (Integer2Hexa (16#FFF#));
Ada.Text_Io.Put_Line (Integer2Hexa (6));
Ada.Text_Io.Put_Line (Integer2Hexa (32, Width => 4));
end Dec2Hex;

I am not sure about the most efficient way to do this either, but whenever I have the problem of converting an integer to a string representation using some other base other than 10 I would consider using Dmitry A. Kazakovs Simple Components and in particular its Strings_Edit package:
http://www.dmitry-kazakov.de/ada/strings_edit.htm

Related

Changing a certain index in a string to a preferred character

When I try creating a subprogram that receives a string and a character and I for instance want to change the first index in the string to the character then return the result I'm not getting it to work...
This is how I tried doing it:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Test is
function Change_Index_In_String(
Str : in String;
Char : in Character
) return String is
begin
Str(Str'First) := C
return Str;
end Change_Index_In_String
Str : String(1..5);
Char : Character;
begin
Put("Type a string (5 characters): ");
Get(String);
Put("Type a character: ");
Get(Character);
Put("Your new string is: ");
Put(Change_Index_In_String(Str, Char))
end Test;
How am I supposed to do this? I'm getting errors in my complier like this:
assignment to "in" mode parameter not allowed
expected type "Standard.Character"
found type "Standard.String"
I am aware on how to return the a certain index in a string as a character, like doing return Str(Str'Last) etc but what about changing a certain index and then returning it?
In the terminal if I type the string Stack and the character W it should return and print out Wtack
One of the key design decisions to make is whether your subprogram is modifying state (a procedure), or returning a new value (a function).
If you want to modify an argument to a subprogram, then A) it needs to be marked out or in out, and B) it should probably be a procedure.
If you want to write a function, you should work on a copy of the input data, rather than trying to modify the argument passed into the function.
Consider the below example of both approaches in one program.
with Ada.Text_IO; use Ada.Text_IO;
procedure Test is
function Change_First(s : in String; c : in Character) return String is
s2 : String(s'range) := s;
begin
s2(s2'First) := c;
return s2;
end Change_First;
procedure Sub_First(s : in out String; c : in Character) is
begin
s(s'First) := c;
end Sub_First;
s : String(1..5) := "Hello";
begin
Put_line(Change_First(s, 'W'));
Sub_First(s, 'Y');
Put_Line(s);
end Test;
This gives us as output:
Wello
Yello
Much is undefined for your function. What should happen if the String is null ('Length = 0)? What should the bounds of the returned String be? One simple solution for one set of answers is
function F (S : in String; C : in Character) return String is
(C & S (S'First + 1 .. S'Last) )
with Pre => S'Length > 0 and S'First < Integer'Last,
Post => (F'Result'First = 1 and
F'Result'Length = S'Length) and then
(F'Result (F'Result'First) = C and
F'Result (F'Result'First + 1 .. F'Result'Last) = S (S'First + 1 .. S'Last) );

Ada - Subprogram determining the biggest number

The task is simple :
Create a subprogram of type function that receives exactly two parameters, both floats and returns the value of the number that is the greatest.
The execution should go as following:
Type in two floats: -13.12 2
-- User types in two numbers marked in bold
The greatest value was : 2.00
This is my code:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Test is
function Value(Float_1, Float_2 : in Float) return Float is
begin
if Float_1 > Float_2 then
return Float_1;
elsif Float_2 > Float_1 then
return Float_2;
end if;
end Value;
Float_1, Float_2 : Float;
begin
Put("Type in two numbers: ");
Get(Float_1);
Get(Float_2);
Put("The greatest value was: ");
if Float_1 > Float_2 then
Put(Float_1, Fore => 0, Aft => 2, Exp => 0);
elsif Float_2 > Float_1 then
Put(Float_2, Fore => 0, Aft => 2, Exp => 0);
end if;
end Test;
Although it works, I feel like I've overcomplicated it and haven't executed it well. For instance I haven't even used my subprogram Value at all in my main program, which I should do.
Any help is greatly appreciated!
Another approach uses a conditional expression:
function Max_Value (Float_1, Float_2 : in float) return float is
(if Float_1 > Float_2 then Float_1 else Float_2);
The conditional expression does the same thing as you were attempting while eliminating the need to return from the function in two different places.
Note that the logic simply returns Float_1 if it is greater than Float_2, otherwise it returns Float_2. Obviously, returning Float_2 when the two values are equal is a valid consequence of this logic.
Perhaps a better name for Value is Max_Value. The conditional within it is also not exhaustive. If the two numbers are equal, there is no return value. This can be corrected by using >= instead of >, at which point the only other option is that Float_2 is greater.
function Max_Value(Float_1, Float_2 : in Float) return Float is
begin
if Float_1 >= Float_2 then
return Float_1;
else
return Float_2;
end if;
end Max_Value;
Alternatively, as suggested by others, the attribute 'Max on Float can accomplish this much more concisely.
function Max_Value(Float_1, Float_2 : in Float) return Float is
begin
return Float'Max(Float_1, Float_2);
end Max_Value;
Or even more concisely:
function Max_Value(Float_1, Float_2 : in Float) return Float renames Float'Max;
Having the ability to pick the larger of two Floats using Max_Value there's now no need to have a conditional later on.
Put(Max_Value(Float_1, Float_2), Fore => 0, Aft => 2, Exp => 0);
As others have mentioned, Ada already provides this functionality. Thus, the most concise implementation would be
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Test is
function Max_Value (Left, Right : Float) return Float renames Float'Max;
begin
Put ("Type in two numbers: ");
Get (Float_1);
Get (Float_2);
Put ("The greatest value was: ");
Put (Max_Value (Float_1, Float_2));
end Test;
What you do here is declare a function Max_Value that renames the existing Float'Max function. Other answers have shown you how to implement the logic yourself.

Using multidimensional array in Ada

In this code, I need help writing a multidimensional array with a range between 2020-01-01 to 2119-12-31.
My code works but as you see there are no arrays in it. How can I write this code with only arrays?
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
Procedure Date is
type date_type is record
Str : string (1..8);
Length : Natural := 0; end record;
A: date_type;
begin
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Get_Line (A.Str, A.Length);
exit when A.Length = 8;
Put_Line ("Wrong input. Try again.");
end loop;
Put_Line (A.Str (1 .. 4) & "-" & A.Str (5 .. 6) & "-" & A.Str (7 .. 8));
end Date;
Perhaps, rather than a multi-dimensional array you should consider using a record such as
type Year_Number is range 1900..3000;
type Month_Number is range 1..12;
type Day_Number is range 1..31;
type Date_Rec is record
Year : Year_Number;
Month : Month_Number;
Day : Day_Number;
end record;
subtype Year_String is string (1..4);
subtype Month_String is string (1..2);
subtype Day_String is string (1..2);
function To_Date (Yr : Year_String; Mnth : Month_String; Dy : Day_String)
return Date_Rec is
Result : Date_Rec;
begin
Result.Year := Year_Number'Value (Yr);
Result.Month := Month_Number'Value (Mnth);
Result.Day := Day_Number'Value (Dy);
return Result;
end To_Date;
You can now pass around instances of Date_
Rec doing whatever you want with the date.
If you go this far then you might want to consider using the Time type described in Ada Language Reference Manual sections 9.6 and 9.6.1.
You haven't asked a reasonable question here because "I want to use arrays" is not a good reason to use an array.
"This problem can be best solved with an array ... but how do I deal with ... ?" would be a reasonable question, but you haven't stated a problem,let alone one that needs an array.
This is important because "using an array" is thinking in the solution domain, like "using a chisel". It's not the way to think about programming in Ada, (or IMO in any language).
Try thinking in the problem domain first : instead of "I want to use a chisel", I think "I want to recess this hinge so the door fits precisely" and a chisel is the neatest way of doing the job.
Then "How can I best validate a date?" would be one reasonable question, or "how can I store events that happen on each day for 100 years?"
The answer to the first question is probably in the Ada.Calendar package. Possibly the Value function in Ada.Calendar.Formatting, with an exception handler to catch incomprehensible strings and make the user try again.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar;
with Ada.Calendar.Formatting;
procedure date is
Date : Ada.Calendar.Time;
Done : Boolean;
begin
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Done := TRUE;
declare
A : String := Get_Line & " 12:00:00";
begin
Date := Ada.Calendar.Formatting.Value(A); -- validate it's a date
Done := Ada.Calendar.Year(Date) >= 2020
and Ada.Calendar.Year(Date) < 2120; -- validate correct range
exception
when Constraint_Error => Done := False; -- Formatting.Value failed
end;
exit when Done;
Put("Try Again : ");
end loop;
end date;
The answer to the second is probably a 1-dimensional array indexed by Day_Count from Ada.Calendar.Arithmetic but let's use the wrong tool : a 3D array indexed by your range of years, Month_Number and Day_Number from the Ada.Calendar base package.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;
with Ada.Calendar.Formatting;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure date is
Date : Ada.Calendar.Time;
Done : Boolean := TRUE;
Event_Array : array(2020 .. 2119,
Ada.Calendar.Month_Number,
Ada.Calendar.Day_Number) of Unbounded_String;
begin
Event_Array := (others => (others => (others => Null_Unbounded_String)));
Event_Array(2020,11,3) := To_Unbounded_String("nothing much");
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Done := TRUE;
declare
A : String := Get_Line & " 12:00:00";
begin
Date := Ada.Calendar.Formatting.Value(A);
Done := Ada.Calendar.Year(Date) >= 2020
and Ada.Calendar.Year(Date) < 2120;
exception
when Constraint_Error => Done := False;
end;
exit when Done;
Put("Try Again : ");
end loop;
Put_Line("Today " & Ada.Calendar.Formatting.Image(Date) & " : " &
To_String(Event_Array(Year(Date), Month(Date), Day(Date))) & " happened");
end date;
Test with the string 2020-11-03

How to change the range of the range type?

Lets say I have
function x return boolean is
type range0 is range 1..1;
begin
canse x is
when 4=> range0:=firstArray'range;
when 5=> range0:=secondArray'range;
when 6=> range0:=1..100;
end case;
end x;
Basically I would like to change the range of range0 on the go? How may I accomplish this without using the declare block?
Basically I would like to change the range of range0 on the go? How may I accomplish this without using the declare block?
Hm...
In Ada 2012 you can use if- and case-expressions, so you could have something like this:
Type Array_Type is Array(Positive Range <>) of Integer;
Array_1 : Array_Type(1..128);
Array_2 : Array_Type(33..63);
-- your variant-selector
Use_1 : constant Boolean:= True;
-- Your variant-range here:
Subtype Variant_Range is Positive Range
(if Use_1 then Array_1'First else Array_2'First)
..(if Use_1 then Array_1'Last else Array_2'Last);
Array_3 : Array_Type( Variant_Range );
All that said, this probably isn't the best way to go about it and using a declare-block is very likely going to be more easily maintained.
You could technically satisfy the stated requirements by converting the obvious way (declare block) into a local procedure :
function x return boolean is
procedure use_dynamic_range(first,last : in integer) is
type range0 is new integer range first .. last;
begin
null;
end use_dynamic_range;
begin
case z is
when 4=> use_dynamic_range(firstArray'first, firstArray'last);
when 5=> use_dynamic_range(secondArray'first, secondArray'last);
when 6=> use_dynamic_range(1,100);
end case;
end x;
Because it's a local procedure it executes in the same scope as the equivalent declare block, therefore it can access everything visible within X, so you don't need to pass it a huge parameter list.
What about something like :
function x return Boolean is
type Range_Info_Type is
record
First : Integer;
Last : Integer;
end record;
function Get_Range_Info_Type return Range_Info_Type is
begin
case z is
when 4=> return Range_Info_Type'(First => firstArray'First,
Last => FirstArray'Last);
when 5=> return Range_Info_Type'(First => secondArray'First,
Last => secondArray'Last);
when 6=> return Range_Info_Type'(First => 1,
Last => 100);
when others => return Range_Info_Type'(First => 1,
Last => 1);
end case;
end;
MyTypeInfo : constant Range_Info_Type := Get_Range_Info_Type;
-- Now declare the actual type I want to use.
type range0 is new Integer range MyTypeInfo.First .. MyTypeInfo.Last;
begin
return true;
end x;
A declare block might be easier to understand by this should do the trick.
Note that you cannot write type range0 is range <expr>..<expr> in your case since expr should be a static expression (see RM 3.5.4)
Another non declare-block answer from Ada 2012:
Minimum : Integer := Integer'First; --' SO highlight correction
Maximum : Integer := Integer'Last; --' *same*
Function In_Range(X : Integer) return Boolean is
( X in range Minimum..Maximum );
Subtype Variant_Range is Integer Range Integer
with Dynamic_Predicate => In_Range(Variant_Range);
WARNING: Though this should work, I have not tested it.

Return a fat/thick pointer as an out parameter

I am having trouble creating a thick pointer. My current set of declarations look like this:
type Index_Typ is mod 20; -- will be larger in real life
type Data_Buffer_Typ is array (Index_Typ range <>) of Integer; --unconstrained array type
type Data_Buffer_Ptr is access all Data_Buffer_Typ; -- a thick pointer, contains the bounds of array subtype pointed to and address..
Data_Buffer : aliased Data_Buffer_Typ (Index_Typ) := (others => 0); -- this is private
type Result_Typ is (Ok, Overflow, Null_Pointer);
procedure Retrieve (Index : in Index_Typ;
Len : in Index_Typ;
Data_Ptr : out Data_Buffer_Ptr;
Result : out Result_Typ) is
begin
-- assuming range checks are ok, what goes here ?
end Retrieve;
so if i declare:
Ptr : Data_Buffer_Ptr := null;
and given a call of Retreive (2,3, Ptr,Result); how do i end up with a pointer that points at elements 2,3 & 4 of Data_Buffer ?
Notes:
Yes i know passing out an array slice will probably be done as a
pointer anyway, but we want to explicitly use pointers, not
implicitly (and not my choice!).
Yes i have experimented, i usually get : (object subtype must statically match designated subtype) error message..
Where possible use of new to be avoided.
This works for me, though I have to say it's repulsive! Note the order of the components in Fat_Pointer, which is the opposite to what I started with, and the size of the record on this 64-bit machine (I put the rep clause in to have make the order explicit, it works fine without). Also, I think you're stuck with the new.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Conversion;
with System;
procedure Fat is
type Index_Typ is mod 20;
type Data_Buffer_Typ is array (Index_Typ range <>) of Integer;
type Data_Buffer_Ptr is access all Data_Buffer_Typ;
Data_Buffer : aliased Data_Buffer_Typ (Index_Typ) := (others => 0);
type Result_Typ is (Ok, Overflow, Null_Pointer);
procedure Retrieve (Index : in Index_Typ;
Len : in Index_Typ;
Data_Ptr : out Data_Buffer_Ptr;
Result : out Result_Typ)
is
type Bound is (Lower, Upper);
type Bounds is array (Bound) of Index_Typ;
type Bounds_P is access Bounds;
type Fat_Pointer is record
The_Data : System.Address;
The_Bounds : Bounds_P;
end record;
for Fat_Pointer use record
The_Data at 0 range 0 .. 63;
The_Bounds at 8 range 0 .. 63;
end record;
function To_Data_Buffer_Ptr
is new Ada.Unchecked_Conversion (Fat_Pointer, Data_Buffer_Ptr);
Answer : constant Fat_Pointer
:= (The_Bounds => new Bounds'(Lower => Index,
Upper => Index + Len - 1),
The_Data => Data_Buffer (Index)'Address);
begin
Result := Ok;
Data_Ptr := To_Data_Buffer_Ptr (Answer);
end Retrieve;
Ptr : Data_Buffer_Ptr := null;
Result : Result_Typ;
begin
for J in Data_Buffer'Range loop
Data_Buffer (J) := Integer (J);
end loop;
Retrieve (2, 3, Ptr, Result);
for J in Ptr'Range loop
Put_Line (J'Img & " => " & Ptr (J)'Img);
end loop;
end Fat;

Resources