How to write a population-growth table in Ada? - ada

** I want my code to display the following table for the population but it seems that my code is not
even close to this. So how can I make my code right? my biggest problem here is using a range-based
for loop. This table shows the population for A and B and their population growth(in percent) and
asks the user to enter some data and when the population b bypass population a then the loop should
stop. The sample code:**
Enter start year: 1976
Enter country A's population (in million): 200
Enter country A's population increase (in%): 10.0
Enter country B population (in million): 100
Enter country B population increase (in%): 50.0
Year Increase A Population A Increase B Population B
1976 ---- 200.00 ------ 100.00
1977 20.00 220.00 50.00 150.00
1978 22.00 242.00 75.00 225.00
1979 24.20 266.20 112.50 337.50
In 1979, country B bypassed country A in population.
My code:
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 Population is
x,Population_A, Population_B:Float;
Growth_A, Growth_B, Growth_1,Growth_2: Float;
begin
Put("Write in the year: "); Get(x);
Put("Population_A: "); Get(Population_A);
Put("Population_B: "); Get(Population_B);
Put("Population grow for A: "); Get(Growth_A);
Put("Population growth for B: "); Get(Growth_B); New_Line;
Put("Year Growth A Population A Growth B Population B ");
for I in 1900..2018 loop
Put(Integer(I)); New_Line;
while Population_B < Population_A loop
Growth_1 := Growth_A +1.0;
Growth_2 := Growth_B +1.0;
Put(Growth_1, 13,2,0); New_Line;
Put(Population_A + (Population_A * Growth_A/100.0),15,2,0); New_Line;
Put(Growth_2, 17,2,0); New_Line;
Put(Population_B + (Population_B * Growth_B/100.0),19,2,0);
end loop;
end loop;
end Population;

Your problem is indeed using a range-based for loop. There are other forms.
I’d probably go for something like
-- read in the data
-- check that there is actually a solution
-- (B’s population not already greater than A’s)
Year := -- value read from data, e.g. 1976
loop
Year := Year + 1;
Population_Of_A := -- calculate it
Population_Of_B := -- calculate it
exit when Population_Of_B > Population_Of_A;
end loop;

You need a more complete concept of the problem you are trying to solve.
The problem assumes a constant population growth rate for each country. While this assumption simplifies the problem it is far from realistic.
Either country A or country B could start with the smaller initial population. If the country starting with the smaller population also has the lowest population growth rate the smaller country will never surpass the larger country in population. How will your program deal with the possibility that either country could initially have the larger population?
The following program deals with some of these problems but not all of them. Perhaps you can get an idea how to deal with all the problems by studying the following program.
with Ada.Text_Io; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Main is
Year : Positive;
type Country (Id : Character) is record
Population : Float;
Rate : Float;
Increase : Float;
end record;
type Country_Access is access all Country;
Country_A : aliased Country('A');
Country_B : Aliased Country('B');
Smaller : Country_Access;
Larger : Country_Access;
procedure Display(Yr : Positive; Cnt_A : Country; Cnt_B : Country) is
begin
Put(Item => Yr, Width => 4);
Put(Item => Cnt_A.Increase, Aft => 2, Fore => 8, Exp => 0);
Put(Item => Cnt_A.Population, Aft => 2, Fore => 8, Exp => 0);
Put(Item => Cnt_B.Increase, Aft => 2, Fore => 8, Exp => 0);
Put(Item => Cnt_B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
end Display;
begin
Put("Enter start year: ");
Get(Year);
Put("Enter country A's population (in million): ");
Get(Country_A.Population);
Put("Enter country A's population increase (in %): ");
get(Country_A.Rate);
Country_A.Rate := (Country_A.Rate / 100.0);
Put("Enter country B's population (in million): ");
Get(Country_B.Population);
Put("Enter country B's population increase (in %): ");
Get(Country_B.Rate);
Country_B.Rate := (Country_B.Rate / 100.0);
if Country_A.Population < Country_B.Population then
Smaller := Country_A'Access;
Larger := Country_B'Access;
else
Smaller := Country_B'Access;
Larger := Country_A'Access;
end if;
Put_Line("Year Increase A Population A Increase B Population B");
Put(Item => Year, Width => 4);
Put(" ----------");
Put(Item => Country_A.Population, Aft => 2, Fore => 8, Exp => 0);
Put(" ----------");
Put(Item => Country_B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
while Smaller.Population <= Larger.Population loop
Year := Year + 1;
Country_A.Increase := Country_A.Population * Country_A.Rate;
Country_A.Population := Country_A.Population + Country_A.Increase;
Country_B.Increase := Country_B.Population * Country_B.Rate;
Country_B.Population := Country_B.Population + Country_B.Increase;
Display(Yr => Year,
Cnt_A => Country_A,
Cnt_B => Country_B);
end loop;
Put_Line("In" & Year'Image & " country " & Smaller.Id &
" bypassed country " &
Larger.Id & " in population.");
end Main;
EDIT:
Following is a version of the above program that does not use access types.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure No_Access is
Year : Positive;
type Country is record
Population : Float;
Rate : Float;
Increase : Float;
end record;
Country_A : Country;
Country_B : Country;
procedure Increase
(Yr : in out Positive; A : in out Country; B : in out Country)
is
begin
Yr := Yr + 1;
A.Increase := A.Population * A.Rate;
A.Population := A.Population + A.Increase;
B.Increase := B.Population * B.Rate;
B.Population := B.Population + B.Increase;
Put (Item => Yr, Width => 4);
Put (Item => A.Increase, Aft => 2, Fore => 8, Exp => 0);
Put (Item => A.Population, Aft => 2, Fore => 8, Exp => 0);
Put (Item => B.Increase, Aft => 2, Fore => 8, Exp => 0);
Put (Item => B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
end Increase;
begin
Put ("Enter start year: ");
Get (Year);
Put ("Enter country A's population (in million): ");
Get (Country_A.Population);
Put ("Enter country A's population increase (in %): ");
Get (Country_A.Rate);
Country_A.Rate := (Country_A.Rate / 100.0);
Put ("Enter country B's population (in million): ");
Get (Country_B.Population);
Put ("Enter country B's population increase (in %): ");
Get (Country_B.Rate);
Country_B.Rate := (Country_B.Rate / 100.0);
Put_Line ("Year Increase A Population A Increase B Population B");
Put (Item => Year, Width => 4);
Put (" ----------");
Put (Item => Country_A.Population, Aft => 2, Fore => 8, Exp => 0);
Put (" ----------");
Put (Item => Country_B.Population, Aft => 2, Fore => 8, Exp => 0);
New_Line;
if Country_A.Population < Country_B.Population then
while Country_A.Population <= Country_B.Population loop
Increase (Year, Country_A, Country_B);
end loop;
Put_Line
("In" & Year'Image & " country A bypassed country B in population.");
else
while Country_B.Population <= Country_A.Population loop
Increase (Year, Country_A, Country_B);
end loop;
Put_Line
("In" & Year'Image & " country B bypassed country A in population.");
end if;
end No_Access;

Related

Float to String: Problem with string length

I would like to convert a float value to a String and created the following short example:
with Ada.Text_IO;
procedure Example is
A : constant Float := -1.234;
B : constant Float := 123_456.789;
C : constant Float := 987.654_321;
package Float_IO is new Ada.Text_IO.Float_IO (Num => Float);
String_Float_A : String := " ";
String_Float_B : String := " ";
String_Float_C : String := " ";
begin
Ada.Text_IO.Put_Line (Float'Image (A));
Ada.Text_IO.Put_Line (Float'Image (B));
Ada.Text_IO.Put_Line (Float'Image (C));
Float_IO.Put
(To => String_Float_A,
Item => A,
Aft => 2,
Exp => 0);
Float_IO.Put
(To => String_Float_B,
Item => B,
Aft => 2,
Exp => 0);
Float_IO.Put
(To => String_Float_C,
Item => C,
Aft => 2,
Exp => 0);
Ada.Text_IO.Put_Line (String_Float_A);
Ada.Text_IO.Put_Line (String_Float_B);
Ada.Text_IO.Put_Line (String_Float_C);
end Example;
My problem: I need to create the string variables before the call of the procedure Put with a sufficient length. How can this be done dynamically at runtime? Basically I need to figure out the number of digits before the dot. Then a sufficient string length would be: 1 (sign) + Number_Of_Dots + 1 (decimal separator) + Aft.
The number of digits before the dot of a decimal number can be computed calculating 1 plus the integer part of the common logarithm (base 10) of
the integer part of the number.
In Ada:
N := Integer (Float'Floor (Log (Float'Floor (abs X), 10.0))) + 1;
Your program must be with'ed with Ada.Numerics.Elementary_Functions.
You must add a space for the minus sign if the number is negative.
This formula does not work if the integer part is 0, but then N is obviously 1.
Your example uses Float, perhaps as a proxy for some more specific real type. If your actual data is better modeled as a decimal fixed point type, discussed here, don't overlook the convenience of Edited Output for Decimal Types, discussed here. The example below uses the string "+Z_ZZZ_ZZ9.99" to construct a picture of the desired output Image.
Console:
- 1.23
+ 123,456.79
+ 987.65
+1,000,000.00
Code:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Text_IO.Editing; use Ada.Text_IO.Editing;
procedure Editing is
type Number is delta 0.000_001 digits 12;
type Numbers is array (Positive range <>) of Number;
package Number_Output is new Decimal_Output (Number);
Format_String : constant String := "+Z_ZZZ_ZZ9.99";
Format : constant Picture := To_Picture (Format_String);
Values : constant Numbers :=
(-1.234, 123_456.789, 987.654_321, Number'Last);
begin
for Value of Values loop
Put_Line (Number_Output.Image (Value, Format));
end loop;
end Editing;
You could make a function to do the job, something like this:
with Ada.Text_IO;
with Ada.Strings.Fixed;
procedure Marcello_Float is
function Image (Item : Float;
Aft : Ada.Text_IO.Field := Float'Digits - 1;
Exp : Ada.Text_IO.Field := 3) return String
is
package Float_IO is new Ada.Text_IO.Float_IO (Float);
Result : String (1 .. 128);
begin
Float_IO.Put (To => Result,
Item => Item,
Aft => Aft,
Exp => Exp);
return Ada.Strings.Fixed.Trim (Result,
Side => Ada.Strings.Both);
end Image;
A : constant Float := -1.234;
B : constant Float := 123_456.789;
C : constant Float := 987.654_321;
A_Image : constant String := Image (A, Aft => 2, Exp => 0);
B_Image : constant String := Image (B, Aft => 2, Exp => 0);
C_Image : constant String := Image (C, Aft => 2, Exp => 0);
use Ada.Text_IO;
begin
Put_Line (A'Image & " => " & A_Image);
Put_Line (B'Image & " => " & B_Image);
Put_Line (C'Image & " => " & C_Image);
end Marcello_Float;
I made Result ridiculously long. I recognise that to calculate an exact size would in fact answer your original question; just being lazy.

Subtype Mark Required in this context - Ada

I'm working to implement a prime decomposition function in Ada. I need to return a Vector from calc_prime_numbers. I'm trying to store that vector in Y. However, whenever I compile, the compiler is saying prime.adb:40:07: subtype mark required in this context. I'm not sure what that means. What does subtype required mean? How do I fix it?
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Containers.Vectors;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Containers;
procedure Prime is
package Integer_Vectors is new Vectors(Natural, Integer);
function Is_Prime(I : Integer) return Boolean is
J : Integer := 2;
begin
for J in 2 .. I-1 loop
if I mod J = 0 then
return False;
end if;
end loop;
return True;
end Is_Prime;
function calc_prime_numbers(n : Integer) return Integer_Vectors.Vector is
i : Integer := 0;
m : Integer;
Numbers : Integer_Vectors.Vector;
begin
m := n + 1;
while (true) loop
i:=i + 1;
if Is_Prime(i) then
Integer_Vectors.Append(Numbers, i);
Put(Integer'Image(i) & " + ");
end if;
if i = m then
exit;
end if;
end loop;
New_Line;
return Numbers;
end calc_prime_numbers;
X : Integer;
Y : Integer_Vectors; — line 40
begin
while (true) loop
Get(X);
if Is_Prime(X) then
Put(Integer'Image(X) & " is prime.");
else
Put(Integer'Image(X) & " is not prime.");
end if;
New_Line;
Y := calc_prime_numbers(X); — line 40
end loop;
end Prime;
Your line number in the error message don't match the code you pasted, and you don't indicate where line 40 is, so I'll have to guess:
you instantiate a package called Integer_Vectors. Later you declare a variable Y : Integer_Vectors;. So the compiler complains because it expects a type for the variable whereas you provided the name of a package.

plsql select return value using dbms_sql 12c

Well in Oracle, EXECUTE IMMEDIATE is simpler than resorting to the dbms_sql package with the exception of cross tenant queries as dbms_sql as of 12c allows a container argument.
However, in a small test, I'm getting nothing. i.e.,
set serveroutput on
declare
ret pls_integer;
cnt pls_integer := 0;
cols number := 0;
ctx varchar2(128) := NULL;
cur number;
stmt varchar2(100);
begin
stmt := 'select count(*) from scott.emp';
cur := dbms_sql.open_cursor;
dbms_sql.parse(c => cur,
statement => stmt,
language_flag => dbms_sql.native,
container => ctx);
dbms_sql.define_column(cur, 1, cnt);
ret := dbms_sql.execute(cur);
dbms_output.put_line('execute = '||ret);
ret := dbms_sql.fetch_rows(cur);
dbms_output.put_line(' fetch = '||ret);
dbms_output.put_line(stmt||' = '||cnt);
dbms_sql.close_cursor(cur);
end;
/
The container when null - meaning current or ignored would allow the query to sent to the target container. I however suspect I'm missing something trivial, hence my question; thanks in advance. Sample output:
SQL> show con_name
CON_NAME
------------------------------
PDB1
SQL> set echo on
SQL> #f
SQL> set serveroutput on
SQL> declare
2 ret pls_integer;
3 cnt pls_integer := 0;
4 cols number := 0;
5 ctx varchar2(128) := NULL;
6 cur number;
7 stmt varchar2(100);
8 begin
9 stmt := 'select count(*) from scott.emp';
10 cur := dbms_sql.open_cursor;
11 dbms_sql.parse(c => cur,
12 statement => stmt,
13 language_flag => dbms_sql.native,
14 container => ctx);
15 dbms_sql.define_column(cur, 1, cnt);
16 ret := dbms_sql.execute(cur);
17 dbms_output.put_line('execute = '||ret);
18 ret := dbms_sql.fetch_rows(cur);
19 dbms_output.put_line('fetch = '||ret);
20 dbms_output.put_line(stmt||' = '||cnt);
21 dbms_sql.close_cursor(cur);
22 end;
23 /
execute = 0
fetch = 1
select count(*) from scott.emp = 0
PL/SQL procedure successfully completed.
You need to get the actual column value from the result set with:
dbms_sql.column_value(cur, 1, cnt);
From the documentation:
COLUMN_VALUE Procedure
This procedure returns the value of the cursor element for a given position in a given cursor. This procedure is used to access the data fetched by calling FETCH_ROWS.
So your code would be:
set serveroutput on
declare
ret pls_integer;
cnt pls_integer := 0;
cols number := 0;
ctx varchar2(128) := NULL;
cur number;
stmt varchar2(100);
begin
stmt := 'select count(*) from scott.emp';
cur := dbms_sql.open_cursor;
dbms_sql.parse(c => cur,
statement => stmt,
language_flag => dbms_sql.native,
container => ctx);
dbms_sql.define_column(cur, 1, cnt);
ret := dbms_sql.execute(cur);
dbms_output.put_line('execute = '||ret);
ret := dbms_sql.fetch_rows(cur);
dbms_output.put_line(' fetch = '||ret);
-- get the actual value from the column into its variable
dbms_sql.column_value(cur, 1, cnt);
dbms_output.put_line(stmt||' = '||cnt);
dbms_sql.close_cursor(cur);
end;
/
If I do that (admittedly in 11g, so without the container argument - which doesn't seem to really be relevant to your actual issue) and against the hr.employees table instead, I see:
execute = 0
fetch = 1
select count(*) from employees = 107
PL/SQL procedure successfully completed.
SQL> select count(*) from employees;
COUNT(*)
----------
107

How to select from a table using DBMS_SQL

I'm trying to use dbms_sql package to select from a table but I can't do it.. can anyone please tell me what I am doing wrong?
create or replace function dbsm_sql_SELECT(age IN NUMBER) return VARCHAR2 is
q VARCHAR2(500);
ret NUMBER;
c1 number;
begin
q := 'SELECT users_name FROM USERS_TABLE WHERE USERS_AGE = :1';
c1 := dbms_sql.open_cursor;
dbms_sql.parse(c1,q,dbms_sql.native);
dbms_sql.bind_variable(c => c1, name => ':1' , value => age );
ret := dbms_sql.execute(c1);
dbms_sql.close_cursor(c1);
dbms_output.put_line(ret);
return('a');
end dbsm_sql_SELECT;
here.. try this.. I've switched to querying from a common table
clear screen
SET serveroutput ON size 1000000
declare
function dbsm_sql_SELECT(age IN NUMBER) return VARCHAR2 is
q VARCHAR2(500);
ret NUMBER;
c1 number;
l_username VARCHAR2(40);
begin
q := 'SELECT username FROM user_USERS WHERE user_id < :1';
c1 := dbms_sql.open_cursor;
dbms_sql.parse(c1,q,dbms_sql.native);
DBMS_SQL.DEFINE_COLUMN(c1, 1, l_username,40);
--define any other columns you want
dbms_sql.bind_variable(c => c1, name => ':1' , value => age );
ret := dbms_sql.execute(c1);
LOOP
IF DBMS_SQL.FETCH_ROWS(c1)>0 THEN
DBMS_SQL.COLUMN_VALUE(c1, 1, l_username);
--get values of any columns you previously defined
ELSE
-- No more rows to copy:
EXIT;
END IF;
end loop;
dbms_sql.close_cursor(c1);
dbms_output.put_line(ret);
--return teh correct value
return(l_username);
end ;
begin
dbms_output.put_line(dbsm_sql_SELECT(1000));
end;
/

String and Character handling in Ada

Below is my Ada code,
What i want to achieve is,
1) Take input from user
2) If user enters value like 1;2;3 it is valid and continue to next input other wise it is print BAD and exit
Now problem with below code is when i enter 1;2;3 it prints just "1" not 2 and 3 also.
Where i am wrong ?
with
Ada.Command_Line,
Ada.Text_IO,
Ada.Integer_Text_IO,
Ada.Characters.Latin_1;
use Ada;
with Ada.Characters.Handling; use Ada.Characters.Handling;
procedure final is
NL : Character renames Ada.Characters.Latin_1.LF;
ESC: Character renames Ada.Characters.Latin_1.Esc;
len : Natural := 0;
Input : Character := Ada.Characters.Latin_1.Esc;
Index : Natural:=0;
user_str : String(1..50) := (others => Character'Val(0));
user_ip : Integer := 1;
function checkUserStr(S : String) return Boolean is
Test : Integer := 1;
Count : Integer := 1;
begin
for i in S'Range loop
if S(i) = Character'Val(0) and Count = 1 then
return false;
end if;
if Count = 1 or Count = 3 or Count = 5 then
if true = Is_Alphanumeric(S(i)) then
Ada.Text_IO.Put(S(i));
if Count = 5 and S(i) = ' ' then
user_ip := 0;
end if;
else
return false;
end if;
elsif Count = 2 or Count = 4 then
if S(i) = ';' then
exit;
else
return false;
end if;
elsif Count = 6 then
if S(i) /= Character'Val(0) then
return false;
else
return true;
end if;
end if;
Count := Count+1;
end loop;
return true;
end checkUserStr;
begin
While user_ip /= 0 loop
Ada.Text_IO.Get_Line (user_str,len);
if true = checkUserStr(user_str) then
user_str := (others => Character'Val(0));
else
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("BAD");
exit;
end if;
end loop;
end final;
"Like" is not a very good protocol specification, but here is a possible solution:
with Ada.Text_IO;
procedure CSV_Digits is
type Data is range 1 .. 3;
package Data_Text_IO is new Ada.Text_IO.Integer_IO (Data);
subtype Separators is Character range ';' .. ';';
procedure Not_EOL is
begin
if Ada.Text_IO.End_Of_Line then
raise Ada.Text_IO.Data_Error;
end if;
end Not_EOL;
procedure EOL is
begin
if not Ada.Text_IO.End_Of_Line then
raise Ada.Text_IO.Data_Error;
end if;
end EOL;
Digit : Data;
Separator : Separators;
begin
loop
Data_Text_IO.Get (Digit); Not_EOL;
Ada.Text_IO.Get (Separator); Not_EOL;
Data_Text_IO.Get (Digit); Not_EOL;
Ada.Text_IO.Get (Separator); Not_EOL;
Data_Text_IO.Get (Digit); EOL;
Ada.Text_IO.Skip_Line;
end loop;
exception
when Ada.Text_IO.Data_Error =>
Ada.Text_IO.Put_Line ("BAD");
end CSV_Digits;

Resources