-- -*- Ada -*-

-- @@PLEAC@@_NAME
-- @@SKIP@@ Ada

-- @@PLEAC@@_WEB
-- @@SKIP@@ http://www.adapower.com/

-- @@PLEAC@@_INTRO
-- @@SKIP@@ Ada requires data items to be declared in a declarative region at
-- @@SKIP@@ the beginning of a code block, before the "begin" reserved word.
-- @@SKIP@@ Many of the PLEAC subsections are implemented in their own unnamed
-- @@SKIP@@ code block to define local variables, functions, and procedures
-- @@SKIP@@ close to their usage, as would be done in Perl.

-- @@PLEAC@@_APPENDIX
with Ada.Text_Io;
with Ada.Integer_Text_IO;
with Ada.Float_Text_Io;
with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;
with Ada.Numerics.Discrete_Random;
with Ada.Numerics.Float_Random;
with Ada.Numerics.Elementary_Functions;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Text_IO.Editing;
with Ada.Strings.Bounded;
with Ada.Strings.Unbounded;
with Gnat.Regpat;

-- @@PLEAC@@_1.0
----------------------------------------------------------------------------
---- Charater are enumeration values and use single quotes for delimiters
-- except for non-printing characters, which have their own labels
c : character := LF;
-- a newline (line feed) character
----------------------------------------------------------------------------
---- Strings are fixed length arrays of characters with no NULL terminator
s1 : string(1..1) := (1 => LF);   -- A string containing only a line feed
s2 : string := "Jon " & '"' &
               "Maddog" & '"' & "Orwant"; -- a string containing double quotes
Put_Line("Jon 'Maddog' Orwant"); -- a string containing single quotes
------------------------------------------------------------------------------
s : string := "This is a multiline string declaration "
            & "using the concatenation operator "
            & "to append separate parts.";
------------------------------------------------------------------------------

-- @@PLEAC@@_1.1
------------------------------------------------------------------------------
-- accessing substrings
s1 : string := data(start..ending);
-- "end" is a reserved word in Ada
s2 : string := data(start..data'Last); -- substrings accessed through array slices
-----------------------------------------------------------------------------
-- different string types
-- Bounded strings have a fixed maximum length and may vary in size up to the
-- maximum length.
package Bounded is new Ada.Strings.Bounded.Generic_Bounded_Length(80);
s : Bounded.Bounded_String := Bounded.To_Bounded_String("Hello World!");
-- Unbounded strings have no maximum size and may vary in size and content
use Ada.Strings.Unbounded;
s : Unbounded_String := To_Unbounded_String("Hello World!");
------------------------------------------------------------------------------
-- No pack/unpack equivalents exist in standard Ada libraries.
-- Examples here use a custom implementation to split an original string into
-- chunks of specified length.

-- TO BE DONE

------------------------------------------------------------------------------
-- Aggregates repeating a basic pattern s :
string := 5 * "Wow! ";
-- result: "Wow! Wow! Wow! Wow! Wow! "
------------------------------------------------------------------------------
data : string := "This is what you have";
-- forward        123456789012345678901
-- Any data element can be accessed directly through its index.
-- Substrings can be accessed directly through array slices.
first : character := data(1);                    -- 'T'
start : string := data(6..7);                    -- "is"
rest  : string := data(14..data'Last);           -- "you have"
last  : character := data(data'Last);            -- 'e'
final : string := data(data'Last - 3..data'Last);-- "have"
piece : string := data(14..16);                  -- "you"
------------------------------------------------------------------------------
-- String contents can be rearranged as long as the length of the string does
-- not change. If you want to change the length then use either Bounded or
-- Unbounded strings.
data : string := "This is what you have";
data(18..21) := "were";  -- "This is what you were"
data(1..7) := "Is this"; -- "Is this what you were"
------------------------------------------------------------------------------
use GNAT.Regpat;

-- roughly the same set of regular expressions as are available in Perl or
-- Python.

Match_Position : Natural;

Match_Position := Match(Compile("pattern"),data(data'last - 9..data'last));
if Match_Position > 0 then
   Put_Line("Pattern matches in last 10 characters");
end if;
-- substitute "at" for "is", restricted to the first five characters
declare
   Matches : Match_Array(0..4);
   Regexp  : string := "is";
   Data    : string := "This is what you have";
begin
   Match(Regexp, Data(1..5), Matches);
   if Matches(0).First > 0 then
      Data(Matches(0).First .. Matches(0).Last) := "at";
   end if;
end;
-- exchange the first and last letters in a string
data : string := "make a hat";
temp : character := data(1);
data(1) := data(data'Last);
data(data'Last) := temp; -- "take a ham"
-- extract a column with array slicing
data : string := "to be or not to be";
a1   : string := data(7..12); -- "or not"
-- forward 6, grab 2, backward 5, grab 2
a2   : string := data(7..8) & data(4..5); -- "orbe"
------------------------------------------------------------------------------

-- @@PLEAC@@_1.2
------------------------------------------------------------------------------
-- boolean values
-- use b if b is true, else use c
b, c : boolean;
-- [..]
a : boolean := b or else c; -- lazy evaluation

-- set x to y unless x is already true
x, y : boolean;
-- [..]
x := x or else y; -- Ada has no ternary operator
-------------------------------------
-- find the user name on Unix systems
with Gnat.Os_Lib; -- Gnat.Os_Lib is a Gnat extension

username : string := Gnat.Os_Lib.Getenv("USER");
logname  : string := Gnat.Os_Lib.Getenv("LOGNAME");
------------------------------------------------------------------------------

-- @@PLEAC@@_1.3
------------------------------------------------------------------------------
-- Ada always requires temporary variables to swap values
------------------------------------------------------------------------------

-- @@PLEAC@@_1.4
------------------------------------------------------------------------------
num : integer := character'Pos(char);
char : character := character'Val(num);
------------------------------------------------------------------------------

-- @@PLEAC@@_1.5
------------------------------------------------------------------------------
s : string := "Some value"; c : character;
for index in S'Range loop
   c := s(index);
end loop;
------------------------------------------------------------------------------

-- @@PLEAC@@_2.0
procedure Pleac_Numbers is
   type Small_Int is range -10..10;
   Numeric_String : String(1..80) := (1=> '6', Others => ' ');
-- Ada defaults to fixed length strings.
   Int : Integer;
   Flt : Float;
   Small : Small_Int;


-- @@PLEAC@@_2.1
   begin
      Int := Integer'Value(Numeric_String);
   exception
      when Constraint_Error =>
         Put_Line(Numeric_String & " is not an Integer");
   end;
   begin
      Flt := Float'Value(Numeric_String);
   exception
      when Constraint_Error =>
         Put_Line(Numeric_String & " is not a float");
   end;

-- Ada also allows you to define your own numeric types and check
-- for valid conversion from a string, including out of range
-- errors
   begin
      Small := Small_Int'Value(Numeric_String);
   exception
      when Constraint_Error =>
         Put_Line(Numeric_String & " is not a Small_Int");
   end;

-- @@PLEAC@@_2.2
function equal(Num1, Num2 : Float; Accuracy : Integer) return Boolean is
   My_Delta : Float := 10.0 ** (-Accuracy);
   Diff     : Float := abs (Num1 - Num2);
begin
   return Diff < My_Delta;
end equal;
----------------------------------------------------------------------
-- Ada offers another form of Real numbers : Fixed point types
-- You specify the fixed precision of the type in the "delta"
-- parameter for the type
-- The following type will have a clearly specified range:
-- -999_999_999.999 to 999_999_999.999

type Real is delta 0.001 digits 12;

R1, R2 : Real;
-- [...]
if R1 = R2 then
   Ada.Text_Io.Put_Line (Real'Image(R1) & " equals"
                       & Real'Image(R2));
end if;

wage : Float := 5.36;
week : Float := 40.0 * wage;
begin
   Put("One week's wage is: $");
   Put(Item => week, Exp => 0, Aft => 2);
   New_Line;
end;

-- @@PLEAC@@_2.3
declare
   a : float := 0.255;
begin
   Put("Unrounded: ");
   Put(Item => a, Exp => 0, Aft => 3);
   New_Line;
   Put("Rounded: ");
   Put(Item => a, Exp => 0, Aft => 2);
   New_Line;
end;

------

declare
   type vals is array(1..4) of float;
   a : vals := (3.3, 3.5, 3.7, -3.3);
begin
   for num in vals'range loop
      Put(Item => a(num), Exp => 0, Aft => 1);
      Put(Ada.Characters.Latin_1.HT);
      Put(Item => Float'Truncation(a(num)), Aft =>1, Exp => 0);
      Put(Ada.Characters.Latin_1.HT);
      Put(Item => Float'Floor(a(num)), Aft => 1,Exp => 0);
      Put(Ada.Characters.Latin_1.HT);
      Put(Item => Float'Ceiling(a(num)), Aft => 1,Exp => 0);
      New_Line;
   end loop;
end;
------------

-- @@PLEAC@@_2.4
-- Ada uses the notation Base#value# for a non-decimal value
-- Thus, a binary number is defined as 2#0011_0110#.
-- The underscores are optional, for readability only.

declare
   num : Integer := 2#0011_0110#;
begin
   Put("Decimal: ");
   Put(Item => num);
   New_Line;
   Put("Binary: ");
   Put(Item => num, Base => 2);
   New_Line;
end;

-- @@PLEAC@@_2.5
declare
   x : Integer := 5;
   y : Integer := 10;
begin
   for i in x..y loop
   -- i is set to every Integer from x to y, inclusive
      null;
   end loop;
end;

declare
   x : Integer := 5;
   y : Integer := 12;
   num : Integer := x;
begin
   loop
   -- operate on num
      num := num + 7; -- step size = 7
      exit when num > y;
   end loop;
end;

Put("Childhood is: ");
for num in 5..12 loop
   Put(Integer'Image(num));
end loop;
New_Line;
---------------

-- @@PLEAC@@_2.6
---------------
-- No standard roman numeral package available
---------------

-- @@PLEAC@@_2.7
-- Generating random discrete values
declare
   type Rand_Range is range 25..75;
   package Rand_Int is new Ada.Numerics.Discrete_Random(Rand_Range);
   seed : Rand_Int.Generator;
   Num : Rand_Range;
begin
   Rand_Int.Reset(seed);
   Num := Rand_Int.Random(seed);
   Put_Line(Rand_Range'Image(Num));
end;
-- Generating a random 8 character password
declare
   subtype alphas is Character range 'A'..'z';
   package Rand_Alpha is new Ada.Numerics.Discrete_Random(alphas);
   seed : Rand_Alpha.Generator;
   Password : String(1..8);
begin
   Rand_Alpha.Reset(seed);
   for index in Password'Range loop
      Password(index) := Rand_Alpha.Random(seed);
   end loop;
   Put_Line("Random Password: " & Password);
end;

-- Generating Random Floating Point Numbers
declare
   seed : Ada.Numerics.Float_Random.Generator;
begin
   Ada.Numerics.Float_Random.Reset(seed);
   Put_Line("Random Float: " &
         Float'Image(Ada.Numerics.Float_Random.Random(seed)));
end;
--------

-- @@PLEAC@@_2.8
------------
-- Ada random number generators are required to yield a period of
-- at least (2^31)-2 or greater.
--------

-- @@PLEAC@@_2.9
------------
-- Ada does not provide a standard biased random number package
------------

-- @@PLEAC@@_2.10
------------
-- Using the Cycle parameter of the Elementary Functions to define
-- a cycle of 0.0..359.99
declare
   use Ada.Numerics.Elementary_Functions;
   angle   : float := 45.0;
   degrees : constant float := 360.0;
   degreeSine : float;
begin
   DegreeSine := sin(X => angle, Cycle => degrees);
   Put_Line("Sine of " & float'Image(angle) &
            " degrees is " & float'Image(degreeSine));
end;

-- @@PLEAC@@_2.11
declare
   y: float;
   package Elm renames Ada.Numerics.Elementary_Functions;
begin
   y := Elm.Arccos(X => 0.7);
   Put_Line("arccos(0.7) = " & float'Image(y));
end;

-- @@PLEAC@@_2.12
-- The Ada.Numerics.Elementary_Functions.Log function returns
-- a natural logarithm when no base is specified, and a log to
-- the specified base when a base is specified.
declare
   use Ada.Numerics.Elementary_Functions;
   answer : float := log(X => 10000.0, Base => 10.0);
begin
   Put_Line("Log10(10000) = " & Float'Image(answer));
end;

-- @@PLEAC@@_2.13
-- Compute the multiplication of two given matrices.
declare
   type Data is array(Positive range <>,Positive range <>) of Integer;
   type Matrix(Rows : Positive; Cols : Positive) is record
      Mat : Data(1..Rows,1..Cols);
   end record;

   Matrix_Mismatch : exception;

   function Mat_Mult(Matrix1 : Matrix;
                     Matrix2 : Matrix) return Matrix is
      result : Matrix(Matrix1.Rows, Matrix2.Cols);
   begin
      if Matrix1.Rows /= Matrix2.Cols then
         raise Matrix_Mismatch;
      end if;
      for i in 1..Matrix1.Rows loop
         for j in 1..Matrix2.Cols loop
            for k in 1..Matrix1.Cols loop
               result.Mat(i,j) := result.Mat(i,j)+ (Matrix1.Mat(i,k) *
                         Matrix2.Mat(k,j));
            end loop;
         end loop;
      end loop;
      return result;
   end Mat_Mult;

   Mat1 : Matrix(Rows => 2,Cols => 3);
   Mat2 : Matrix(Rows => 3,Cols => 2);
   Mat3 : Matrix(Rows => 2,Cols => 2);
begin
   Mat1.Mat := ((3,2,3), (5,9,8));
   Mat2.Mat := ((4,7),(9,3),(8,1));
   Mat3 := Mat_Mult(Mat1, Mat2);
end;

-- @@PLEAC@@_2.14
declare
   package Cmplx_Flt is new
        Ada.Numerics.Generic_Complex_Types(float);
   use Cmplx_Flt;
   a : Cmplx_Flt.Complex := (3.0, 5.0);
   b : Cmplx_Flt.Complex := (2.0, -2.0);
   c : Cmplx_Flt.Complex;
begin
   c := a * b;
end;

-- @@PLEAC@@_2.15
-- Ada allows representation of any base from 2 through 16 on output
-- all Integers are stored internally as binary.
declare
   Num : Integer;
begin
   Put_Line("Gimme a number in decimal, octal, or hex:");
   Get(Num);
   Put_Line("Decimal value: " & Integer'Image(Num));
   Put("Octal Value: ");
   Put(Item => Num, Base => 8);
   New_Line;
   Put("Hexadecimal Value: ");
   Put(Item => Num, Base => 16);
   New_Line(2);
------
   Put_Line("Enter a file permission in octal: ");
   Get(Num);
   Put_Line("Decimal Value:" & Integer'Image(Num));
end;

-- @@PLEAC@@_2.16
-- The Text Editing routines in Ada work for fixed decimal types
declare
   type Large_Type is delta 0.01 digits 10;
   package with_Commas is new
            Ada.Text_Io.Editing.Decimal_Output(Large_Type);
   use with_Commas;
   use Ada.Text_IO.Editing;

   Val : Large_Type := 12345678.90;
begin
   put(Val, To_Picture("ZZ_ZZZ_ZZ9.99"));
end;
----------------

-- @@PLEAC@@_2.17
-----------------
-- There are no standard Ada regular expression packages,
-- however, then GNAT compiler does provide some regular expression
-- packages as extensions.

end Pleac_Numbers;

-- @@PLEAC@@_2.18
-----------------------------------------------------------------
------- Program Calculating Prime Factors
-----------------------------------------------------------------
-----with Ada.Text_Io; use Ada.Text_IO;

with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Command_Line; use Ada.Command_Line;

procedure Prime_Factors is
   type Found_List is array(Positive range <>) of Natural;
   type Factor_List (Max_Size : Positive) is record
      Factors : Found_List(1..Max_Size) := (Others => 0);
      Num_Found : Natural := 0;
   end record;

function getFactors(n : in Integer) return Factor_List is
   orig : Integer := n;
   new_n : Integer := n;
   Temp : Factor_List(Max_Size => n / 2);
   sqi  : Integer := 4;
   i    : Integer := 2;
begin
   while sqi <= new_n loop
     while (new_n mod i) = 0 loop
        new_n := new_n / i;
        if Temp.Factors(i) = 0 then
           Temp.Factors(i) := 1;
        else
           Temp.Factors(i) := Temp.Factors(i) + 1;
           Temp.Num_Found := Temp.Num_Found + 1;
        end if;
      end loop;
      sqi := (i + 1)**2;
      i := i + 1;
   end loop;
   if (new_n /= 1) and (new_n /= orig) then
      Temp.Factors(new_n) := 1;
      Temp.Num_Found := Temp.Num_Found + 1;
   end if;
   return Temp;
end getFactors;

procedure Display_Factors(Num : Integer; Item : in Factor_List) is
begin
   Put(Integer'Image(Num) & ": ");
   if Item.Num_Found = 0 then
      Put_Line("PRIME");
   else
      for index in Item.Factors'Range loop
         if Item.Factors(index) /= 0 then
            Put(Item => index, width => 1);
            if Item.Factors(index) > 1 then
               Put("^");
               Put(Item => Item.Factors(index), width => 1);
            end if;
            Ada.Text_Io.Put(" ");
         end if;
      end loop;
      New_Line;
   end if;
end Display_Factors;

begin
   if Argument_Count < 1 then
      Put_Line("Usage: " & Command_Name & " number...");
      return;
   end if;
   for arg in 1..Argument_Count loop
     Display_Factors(Positive'Value(Argument(arg)),
                     getFactors(Positive'Value(Argument(arg))));
   end loop;
end Prime_Factors;
------------------------------------------------------------------------------

-- @@PLEAC@@_3.0
------------------------------------------------------------------------------
-- All standard Ada date / time subprograms are defined in the packages
-- Ada.Calendar and Ada.Real_Time
--
-- Time values can be split into a Year number, a Month number, a Day
-- (of the month) number, and a Seconds number.
------------------------------------------------------------------------------

-- @@PLEAC@@_3.1
------------------------------------------------------------------------------
Now : Time;

Now := Clock; -- A parameter-less function returning the current time value.
Put("The current date is ");
Put(Item => Year(Now), Width => 4);
Put(Item => Month(Now), Width => 3);
Put(Item => Day(Now), Width => 3);

The current date is 2002  2 25
------------------------------
Now : Time;
This_Year  : Year_Number;
This_Month : Month_Number;
This_Day   : Day_Number;
This_Seconds : Day_Duration;

Split(Date => Now, Year => This_Year, Month => This_Month,
      Day => This_Day, Seconds => This_Seconds);
Put("The current date is ");
Put(Item => This_Year, Width => 4);
Put(Item => This_Month, Width => 3);
Put(Item => This_Day, Width => 3);

The current date is 2002  2 25
------------------------------------------------------------------------------

-- @@PLEAC@@_3.2
------------------------------------------------------------------------------
-- Ada does not directly deal with minutes. Minutes are contained in the
-- Seconds value, which is the number of seconds into the day for the current
-- time. Seconds is a value from 0.0 through 86400.0
This_Year    : Year_Number;
This_Month   : Month_Number;
This_Day     : Day_Number;
This_Seconds : Day_Duration;
The_Time     : Time;

The_Time := Time_Of(Year => This_Year, Month => This_Month,
                    Day => This_Day, Seconds => This_Seconds);
-------------------------------
-- Year_Number is an integer from 1901 through 2099
-- Month_Number is an integer from 1 through 12
-- Day_Number is an integer from 1 through 31
-- Day_Duration is a Fixed Point type from 0.0 through 86400.0
------------------------------------------------------------------------------

-- @@PLEAC@@_3.3
------------------------------------------------------------------------------
The_Time     : Time;
The_Year     : Year_Number;
The_Month    : Month_Number;
The_Day      : Day_Number;
The_Seconds  : Day_Duration;
The_Minutes  : Integer;
The_Hour     : Integer;
Seconds      : Integer;

Split(Date => The_Time, Year => The_Year, Month => The_Month,
      Day => The_Day, Seconds => The_Seconds);
The_Hour := The_Seconds / 3600;
The_Minutes := (The_Seconds - (3600 * The_Hour)) / 60;
Seconds := The_Seconds mod 60;
------------------------------------------------------------------------------

-- @@PLEAC@@_3.4
------------------------------------------------------------------------------
-- Time addition involves adding a Duration to a Time.
-- A Duration is a total number of seconds.
Birthtime : Time;
Interval  : Duration;
Then      : Time;

Birthtime := Time_Of(Year => 1973, Month => 1, Day => 18,
             Seconds => ((3.0 * 3600.0) +  -- 3:45:50
                         (45.0 * 60.0) + 50.0));
Interval := 5.0 +               -- 5 seconds
            (17.0 * 60.0) +     -- 17 minutes
            (2.0 * 3600.0) +    -- 2 hours
            (55.0 * 86400.0);   -- 55 days
Then := Birthtime + Interval; --
-- or
Then := Interval + Birthtime;

Put("Then is ");
Put(Item => Month(Then), Width => 2);
Put(Item => Day(Then), Width => 3);
Put(Item => Year(Then), Width => 5);
Put(Item => Seconds(Then) / 3600, Width => 3);
Put(":");
Put(Item => (Seconds(Then) - (Seconds(Then) / 3600 * 3600)) / 60,
    Width => 2);
Put(":");
Put(Item => Seconds(Then) mod 60, Width => 2);
New_Line;

Then is 3 14 1973  6: 2:55
------------------------------------------------------------------------------

-- @@PLEAC@@_3.5
------------------------------------------------------------------------------
Seconds : Duration;
Recent  : Time;
Earlier : Time;

Seconds := Recent - Earlier;

Earlier := Recent - Seconds;
------------------------------------------------------------------------------

-- @@PLEAC@@_3.6
------------------------------------------------------------------------------
-- Ada provides no standard subprogram for calculation of week number, day of
-- week, Julian Date, or Month name.
-- Those calculations can be performed, with month names localized to the
-- current locale, by simple user defined functions.
------------------------------------------------------------------------------

-- @@PLEAC@@_3.7
------------------------------------------------------------------------------
date : String := "1998-06-03"; -- YYYY-MM-DD format
The_Year : Year_Number;
The_Month : Month_Number;
The_Day : Day_Number;
Index : Positive;

Get(From => date, Item => The_Year, Last => Index); -- Get YYYY as an integer
Get(From => date(Index + 2..date'Last),  -- Get MM skipping first "-"
    Item => The_Month, Last => Index);
Get(From => date(Index + 2..date'Last),  -- Get DD skipping second "-"
    Item => The_Day, Last => Index);
-- Calculate the time value at midnight of that date
The_Time : Time;

The_Time := Time_Of(Year => The_Year, Month => The_Month,
                    Day => The_Day, Seconds => 0.0);
------------------------------------------------------------------------------

-- @@PLEAC@@_3.8
------------------------------------------------------------------------------
function Month_Name(Month : Month_Number) return String is
   type Months is (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT,
                        NOV, DEC);
begin
   -- convert month number to enumeration value then convert enumeration
   -- value to a string
   return Months'Image(Months'Val(Month - 1));
end Month_Name;
-------------------------------
SEP 21 15:33:36 1997
-------------------------------
The_Time : Time;
The_Time := Time_Of(Year => 1997,
                    Month => 9,
                    Day   => 21,
                    Seconds => 36.0 +
                               (33.0 * 60.0) +
                               (15.0 * 3600.0));
Put(Month_Name(Month(The_Time)) & " ");
Put(Item => Day(The_Time), Width => 2);
Put(Item => Seconds(The_Time) / 3600, Width => 3);
Put(":");
Put(Item => (Seconds(The_Time) - (Seconds(The_Time) / 3600 * 3600)) / 60,
    Width => 2);
Put(":");
Put(Item => Seconds(The_Time) mod 60, Width => 2);
Put(Item => Year(The_Time), Width => 5);
New_Line;
------------------------------------------------------------------------------

-- @@PLEAC@@_3.9
------------------------------------------------------------------------------
-- High resolution time is provided by the Ada.Real_Time package.
-- High resolution time values shall be sufficient to uniquely represent the
-- range of real times from program start up to 50 years later.
-- Tick (the avearage time interval during which the clock value remains constant)
-- shall be no greater than 1 millisecond. Time_Unit (the smallest amount of real
-- time representable by the time type) shall be less than or equal to 20
-- microseconds.

with Ada.Real_Time; use Ada.Real_Time;

t0 : Time;
t1 : Time;

t0 := Clock; -- Get initial time stamp;
-- Perform some operation
t1 := Clock; -- Get ending time stamp;

elapsed : Time_Span;

elapsed := t1 - t0; -- Time in seconds i.e 0.0015
------------------------------------------------------------------------------

-- @@PLEAC@@_3.10
------------------------------------------------------------------------------
-- sleeps are achieved in Ada using the "delay" statement.
-- The parameter to the delay command is a Duration expressing a number of
-- seconds.

delay 0.0025;
------------------------------------------------------------------------------

-- @@PLEAC@@_3.11
------------------------------------------------------------------------------
-- To Be Done
------------------------------------------------------------------------------

-- @@PLEAC@@_7.0
------------------------------------------------------------------------------
-- Normal textual input and output is accomplished using the pacakge
-- Ada.Text_IO. The following I/O routines are taken from that package

file : File_Type;

begin
   Open(File => file, Name => "/usr/local/widgets/data", Mode => In_File);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open the file");
end;
-------------------------------
file : File_Type;
length : Natural;
line   : String(1..1024); -- some string large enough to hold any line in
                          -- the file
begin
   Open(File => file, Name => "/usr/local/widgets/data", Mode => In_File);
   while not End_Of_File(file) loop
      Get_Line(File => file, Item => line, Last => length);
      Put_Line(Item => line(1..length)); -- writes to stdout
   end loop;
   Close(file);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,  -- writes to stderr
          Item => "You do not have permission to open the file");
end;
-------------------------------
num : float;

while not End_Of_File loop
   begin
      Get(Item => num); -- read a float from stdin
      Put_Line(float'Image(num)); -- write to stdout
   exception
      when Data_Error =.
         Put(File => Standard_Error, -- write to stderr
             Item => "File contains non-numeric data");
         Skip_Line; -- skip the rest of the current input line
   end;
end loop;
-------------------------------
logfile : File_Type;

begin
   Open(File => logfile,
        Name => "/tmp/log",
        Mode => Out_File);  -- open for output
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,  -- writes to stderr
          Item => "You do not have permission to open the file");
end;
-------------------------------
oldfile : File_Type;

oldfile := Current_Output; -- save old file handle
Set_Output(logfile); -- switch standard output
Put_Line("Countdown initiated ...");
Set_Output(oldfile); -- return to original output
Put_Line("You have 30 seconds to reach maximum safety distance.");
------------------------------------------------------------------------------

--@@PLEAC@@_7.1
------------------------------------------------------------------------------
source : file_type;
sink   : file_type;

-- open file for reading
begin
   Open(File => source, Name => path, Mode => In_File);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open the file");
end;

-- open file for writing
begin
   Open(File => sink, Name => path, Mode => Out_File);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open the file");
end;

-- Open file for writing, creating a new file
begin
   Create(File => sink, Name => path);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open the file");
end;
-------------------------------
-- You must instantiate the package Ada.Direct_Io to perform both input
-- and output on a file.
package dir_text is new Ada.Direct_Io(Character);
use dir_text;

direct : File_Type;

begin
   Open(File => direct, Name => path, Mode => InOut_File);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open the file");
end;
-------------------------------
sink : File_Type;

begin
   Open(File => sink, Name => path, Mode => Append_File);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File name not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File already open.");
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open the file");
end;
------------------------------------------------------------------------------

-- @@PLEAC@@_7.2
------------------------------------------------------------------------------
-- Ada does not provide any rules about file names. Unusual file names are
-- opened the same as common file names.
------------------------------------------------------------------------------

-- @@PLEAC@@_7.3
------------------------------------------------------------------------------
-- Ada provides no facilities for expanding tildes in file names.
------------------------------------------------------------------------------

-- @@PLEAC@@_7.4
------------------------------------------------------------------------------
-- (The Ada equivalent)
sink : File_Type;

begin
   Open(File => sink, Name => path, Mode => Append_File);
exception
   when Name_Error =>
      Put(File => Standard_Error, Item => "File " & path " not found.");
   when Status_Error =>
      Put(File => Standard_Error, Item => "File " & path & " already open.");
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open file " & path);
end;
------------------------------------------------------------------------------

-- @@PLEAC@@_7.5
------------------------------------------------------------------------------
-- Passing in a null string for the file name creates a temporary file

begin
   Create(File => sink, Name => "");
exception
   when Use_Error =>
      Put(File => Standard_Error,
          Item => "You do not have permission to open the file");
end;
------------------------------------------------------------------------------

-- @@PLEAC@@_7.6
------------------------------------------------------------------------------
-- Not allowed in Ada.
------------------------------------------------------------------------------

-- @@PLEAC@@_7.7
------------------------------------------------------------------------------
line : String(1..256);
length : Natural;

while not End_Of_File loop
   Get_Line(Item => line, Last => length);
   -- ...  -- do something with the input line
end loop;
-------------------------------
-- The GNAT extension package Gnat.Command_Line provides some high level
-- command line parsing capabilities.
-- Simple command line parsing is provided by the standard package
-- Ada.Command_Line.

source : File_Type;
for argnum in 1..Argument_Count loop
   begin
      Open(File => Source, Name => Argument(argnum),
           Mode => In_File);
      while not End_Of_File(source) loop
         -- process the file from the command line
      end loop;
   exception
      when Name_Error =>
         Put(File => Standard_Error, Item => "File name not found.");
      when Status_Error =>
         Put(File => Standard_Error, Item => "File already open.");
      when Use_Error =>
         Put(File => Standard_Error,
             Item => "You do not have permission to open the file");
   end;
end loop;
-------------------------------
-- arg demo 1: Process optional -c flag
loop
   case GetOpt("-c") is
      when ASCII.NUL =>
         exit; -- exit loop
      when 'c' =>
         -- set whatever internal flag corresponds to the "-c" option
      when Others =>
         raise Program_Error; -- cannot occur!
   end case;
end loop;
-------------------------------
-- arg demo 2: Process optional -NUMBER flag
loop
   case GetOpt("1 2 3 4 5 6 7 8 9") is
      when ASCII.NUL =>
         exit;
      when '1' =>
         -- process 1
      when '2' =>
         -- process 2
      when '3' =>
         -- process 3
      when '4' =>
         -- process 4
      when '5' =>
         -- process 5
      when '6' =>
         -- process 6
      when '7' =>
         -- process 7
      when '8' =>
         -- process 8
      when '9' =>
         -- process 9
      when Others =>
         raise Program_Error;
   end case;
end loop;
-------------------------------
-- arg demo 3: Process clustering -a, -i, -n, or -u flags

loop
   case Get_Opt("a i n u") is
      when ASCII.NUL =>
         exit;
      when 'a' =>
         append := true;
      when 'i' =>
         ignore_ints := true;
      when 'n' =>
         nostdout := true;
      when 'u' =>
         unbuffer := true;
      when Others =>
         raise Program_Error;
   end case;
end loop;
------------------------------------------------------------------------------

-- @@PLEAC@@_7.8
------------------------------------------------------------------------------
-- Ada has no "rename" built in. It can, however, call existing programs
-- to achieve the same purpose.
with Interfaces.C; use Interfaces.C;
procedure Rename_File(From, To : String) is
   function system(Command : Char_Array) return Int;
   pragma Import(C, system, "system");
   rc : Int;
begin
   rc := system(To_C("mv " & From & " " & To));
end Rename_File;
-------------------------------
Open(File => Old, Name => old_file, Mode => In_File);
Create(File => New, Name => new_file);
while not End_Of_File(Old) loop
   Get_Line(File => Old, Item => Line, Last => Length);
   -- process the data in Line
   Put_Line(File => New, Item => Line(1..Length));
end loop;
Close(Old);
Close(New);
Rename_File(old_file, old_file & ".orig");
Rename_File(new_file, old_file);
------------------------------------------------------------------------------

-- @@PLEAC@@_10.0
------------------------------------------------------------------------------
-- Ada makes a distinction between functions and procedures.
-- Functions always return a value and never alter their parameters.
-- Procedures never return a value and may alter their parameters
procedure Hello is
begin
   Put_Line("hi there!");
end Hello;
-------------------------------
Hello; -- Calling Hello with no parameters
-------------------------------
function Sum(A, B : Integer) return Integer is
begin
   return A + B;
end Sum;
-------------------------------
C := Sum(X, Y); -- Calling a function
------------------------------------------------------------------------------

-- @@PLEAC@@_10.1
------------------------------------------------------------------------------
-- Ada subroutine parmameters must have one of three modes.
-- IN, OUT, IN OUT
-- IN is read-only inside the subroutine. All function parameters are IN
-- OUT is write-only inside a subroutine
-- IN OUT is read and write within the subroutine
function Hypotenuse(A, B : Float) return Float is
begin
   return sqrt((A**2) + (B**2));
end Hypotenuse;

diag := Hypotenuse(3.0, 4.0); -- diag is 5.0
-------------------------------
Ada.Float_Text_Io.Put(Hypotenuse(3.0, 4.0)); -- prints 5.0E00
------------------------------------------------------------------------------

--@@PLEAC@@_10.2
------------------------------------------------------------------------------
procedure Swap(A, B : in out Integer) is
   Temp := A; -- Temp is visible only within the Swap procedure
begin
   A := B;
   B := Temp;
end Swap;
------------------------------------------------------------------------------

-- @@PLEAC@@_10.3
------------------------------------------------------------------------------
-- Persistent private variables are declared inside a package body
package body Counter is
   type Numbers is mod 100; -- will only have a value from 0 through 99
   Count : Numbers := 0; -- Count is persistent across calls, and is
                         -- initialized to 0
   function Take_Number return Integer is
      Result : Integer := Integer(Count);
   begin
      Count := Count + 1; -- modular arithmetic. 99 + 1 => 0
      return Result;
   end Take_Number;
end Counter;
------------------------------------------------------------------------------

-- @@PLEAC@@_10.4
------------------------------------------------------------------------------
-- Ada provides no way to determine a function's name at run time
------------------------------------------------------------------------------

-- @@PLEAC@@_10.5
------------------------------------------------------------------------------
-- Ada parameter modes will always pass arrays by reference
-- The GNAT compiler provides a Hash as an extension, but the Ada language
-- does not define a Hash as part of its standard.
------------------------------------------------------------------------------

-- @@PLEAC@@_10.6
------------------------------------------------------------------------------
-- Ada return context is always determined at compile time.
-- There is no way to determine return context at run time.
------------------------------------------------------------------------------

-- @@PLEAC@@_10.7
------------------------------------------------------------------------------
Put(Item => Age, Width => 2);
Put(Item => Age);
Put(Width => 2, Item => Age);
Put(Age, Width => 2);
-------------------------------
function Put(Item : Integer; Width : Positive := 1) is
begin
 [...]
end Put;
------------------------------------------------------------------------------

-- @@PLEAC@@_10.8
------------------------------------------------------------------------------
-- Ada functions must return exactly one value. Skipping values does not
-- occur in Ada.
------------------------------------------------------------------------------

-- @@PLEAC@@_10.9
------------------------------------------------------------------------------
-- Ada allows you to return a single item from a function.
-- That item may be of a compound type, such as an array, an array of arrays,
-- or a record.
------------------------------------------------------------------------------

-- @@PLEAC@@_10.10
------------------------------------------------------------------------------
-- There is no standard Ada failure value. Raise an exception upon failure.
------------------------------------------------------------------------------

-- @@PLEAC@@_10.11
------------------------------------------------------------------------------
-- Functions and procedures are prototyped in a package specification
-- The prototype includes the exact signature of the function or procedure
function Take_Number return Integer;
procedure Swap(A, B : in out Integer);
------------------------------------------------------------------------------

--@@PLEAC@@_10.12
------------------------------------------------------------------------------
raise Buffer_Empty_Error; -- raise an exception
-------------------------------
begin
   [...]
exception
   when Buffer_Empty_Exception =>
       [...]
end;
-------------------------------
when Others=>
   Clean_Up;
   raise; -- reraise unknown error
------------------------------------------------------------------------------

-- @@PLEAC@@_10.13
------------------------------------------------------------------------------
-- Ada global variables are declared within a package specification
package Globals is
   age : Natural;
end Globals;

procedure SetAge(New_Age : Natural) is
begin
   age := New_Age;
end SetAge;
------------------------------------------------------------------------------

-- @@PLEAC@@_10.14
------------------------------------------------------------------------------
-- Ada does not allow redefinition of functions at run time.
-- Ada does allow functions and procedures to be overloaded.
-- Many functions or procedures can have the same name with a different list
-- of parameters, or a different return type.
type Real_Nums is array(1..100) of Float;
type Int_Nums is array(1..200) of Integer;
function Average(Nums : Real_Nums) return Float;
function Average(Nums : Real_Nums) return Integer;
function Average(Nums : Int_Nums) return Integer;
function Average(Nums : Int_Nums) return Float;
------------------------------------------------------------------------------

-- @@PLEAC@@_10.15
------------------------------------------------------------------------------
-- Undefined function and procedure calls are detected as errors by the
-- Ada compiler. No run time detection is possible.
------------------------------------------------------------------------------

-- @@PLEAC@@_10.16
------------------------------------------------------------------------------
function Outer(Num : Integer) return Integer is
   function Inner(Arg : Integer) return Integer is
   begin
      return Arg * 19;
   end Inner;
begin -- Outer
   return Num + Inner(Num);
end Outer;
------------------------------------------------------------------------------

-- @@PLEAC@@_10.17
-- @@SKIP@@ Program: Sorting Your Mail
------------------------------------------------------------------------------
-- To Be Done
------------------------------------------------------------------------------

-- @@PLEAC@@_11.0
------------------------------------------------------------------------------
type Int_Ptr is access all Integer;
ref : Int_Ptr;
num : aliased Integer := 5;
ref := num'Access;

Put(Item => ref.all); -- Print the value ref refers to
ref.all := 3;         -- Assign to ref's referant
-------------------------------
type Flt_Ptr is access all Float;
ref : Flt_Ptr;
pi : Float := 3.14159;
pi'Access := 4; -- Compile time error. The Access attribute cannot be used
                -- on the left hand side of an assignment.
                -- Also, "pi" is not defined as "aliased". This will also
                -- generate a compiler error
-------------------------------
-- There is no direct relationship between arrays and references in Ada.
-- An array is a collection of like objects. An array name is NOT an
-- access or reference to the first element of the array as in C.
-------------------------------
-- Ada provides two kinds of access types, general access types and
-- pool-specific acess types. Referants for pool-specific access types
-- must always be dynamically allocated. Referants for general access
-- types may be dynamically allocated, or may refer to aliased objects
-- generated on the stack.
type Pool_Ptr is access Float; -- Pool specific access type
type Gen_Ptr is access all Float; -- General access type
------------------------------------------------------------------------------

-- @@PLEAC@@_11.1
------------------------------------------------------------------------------
type Sales is array (Integer range 1..7) of Float;
type Sales_Access is access Sales; -- Pool specific example

Weekly_Sales : Sales_Access := new Sales; -- dynamically allocate
                                          -- the Sales array
                                          -- and assign its reference to
                                          -- Weekly_Sales
-------------------------------
last_index : Integer := Sales'Last;
first_index : Integer := Sales'First;
num_items : Integer := Sales'Length;
-------------------------------
-- Ada arrays are all fixed size. They cannot be enlarged after creation
-- Ada array types may be unconstrained in size, allowing the size of a
-- particular instance to be specified upon creation.

type String is array(Positive range <>) of Character;
s1 : String(1..10); -- s1 is a 10 element string
s2 : String(1..34); -- s2 is a 34 element string
s3 : String(11..20); -- s3 is a 10 element string with a
                     -- a starting index of 11
-------------------------------
type String_Ref is access String;
r1 : String_Ref;

r1 := new String(1..20); -- Dynamically allocate an array
Put(r1(N)); -- Print the character at index N
-------------------------------
r1(3..5)  -- Array slice
-------------------------------
r1(3..5) := r1(9..11);
-------------------------------
-- Ada provides no way to make a reference to a slice
-------------------------------
for index in r1'Range loop
  -- iterate through all elements in the array
  -- referenced by r1
end loop;
-------------------------------
for index in s1'Range loop
   -- iterate through all elements in array s1
end loop;
------------------------------------------------------------------------------

-- @@PLEAC@@_11.2
------------------------------------------------------------------------------
-- Ada provides no standard implementation of hashes
------------------------------------------------------------------------------

-- @@PLEAC@@_11.3
------------------------------------------------------------------------------
-- Ada provides no standard implemenation of hashes
------------------------------------------------------------------------------

-- @@PLEAC@@_11.4
------------------------------------------------------------------------------
-- Define an access type to a function taking two integer parameters and
-- returning a boolean
type func_ref is access function (A, B : Integer) return Boolean;

-- Define an access type to a procedure taking no parameters
type proc_ref is access procedure;

-- Define a function taking two integers and returning a boolean
function Less_Than(X, Y : Integer) return Boolean;

-- Define an instance of the func_ref access type
compare : func_ref;

compare := Less_Than'Access; -- Less than matches the signature defined in
                             -- the func_ref type declaration
-- Calling the function through the referenc
if compare(num1, num2) then
   ...  -- act on the comparison
end if;

-- Define a procedure taking no parameters
procedure Print_Header;

-- Define an instance of proc_ref access type

action : proc_ref := Print_Header'Access;

-- Call the parameter-less procedure
action.all;
-------------------------------
-- Ada does not provide closures, or functions with static data
-- The same effect can be produced using Ada tasks.
package counters is
   task type Counter_Maker is
      entry Get_Count(Num : out Integer);
   end Counter_Maker
   type Counter_Ref is access Counter_Maker;
end counters;

package body counters is
   task body Counter_Maker is
      start : Integer := 0;
   begin
      loop
         accept Get_Count(Num : out Integer)
         do
            Num := start;
         end Get_Count;
         start := start + 1;
      end loop;
   end Counter_Maker;
end counters;

counter1 : Counter_Ref := new Counter;
the_count : Integer;

for iter in 0..4 loop
   counter1.Get_Count(the_count);
   Put_Line(Integer'Image(the_count));
end loop;
-------------------------------
counter1 : Counter_Ref := new Counter;
counter2 : Counter_Ref := new Counter;
count1, count2 : Integer;

for iter in 0..4 loop
   counter1.Get_Count(count1);
   Put_Line(Integer'Image(count1));
end loop;
counter1.Get_Count(count1);
counter2.Get_Count(count2);
Put_Line(Integer'Image(count1) & " " &
         Integer'Image(count2));
0
1
2
3
4
5 0
-------------------------------
function Time_Diff(Base_Time : Time) return Duration is
   Now : Time := Clock;
begin
   return Now - Base_Time;
end Time_Diff;

early : Time;
later : Time;

early := Clock;
delay 20.0;
later := Clock;
delay 10.0;
Put_Line("It's been " & Duration'Image(Time_Diff(early)) &
         " seconds since early.");
Put_Line("It's been " & Duration'Image(Time_Diff(later)) &
         " seconds since later");
-- It's been  30.007172430 seconds since early.
-- It's been  10.003749644 seconds since later.
------------------------------------------------------------------------------

-- @@PLEAC@@_11.5
------------------------------------------------------------------------------
-- Ada does not provide special syntax for obtaining reference to scalars
-- compared to vectors.
------------------------------------------------------------------------------

-- @@PLEAC@@_11.6
------------------------------------------------------------------------------
type Num_Ref is access all Integer;
type Ref_Array is array (1..10) of Num_Ref;
-------------------------------
array_of_refs : Ref_Array;
array_of_refs := (Others => null); -- Set all values to null references

for index in Ref_Array'Range loop
   array_of_refs(index) := new Integer'(index);
end loop
-------------------------------
for index in Ref_Array'Range loop
   Put_Line(Integer'Image(array_of_refs(index).all));
end loop;
-------------------------------
for index in Ref_Array'Range loop
   array_of_refs(index).all := index * 10;
end loop;
------------------------------------------------------------------------------

-- @@PLEAC@@_11.7
------------------------------------------------------------------------------




-- @@PLEAC@@_11.15
------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Binary Tree Example
-------------------------------------------------------------------------------

package Binary_Trees is
   type Tree is private;
   procedure insert(Item : in Integer; Into : in out Tree);
   procedure in_order(Item : in Tree);
   procedure pre_order(Item : in Tree);
   procedure post_order(Item : in Tree);
   function search(Item : in Integer; From : in Tree) return boolean;
private
   type node;
   type Tree is access Node;
   type Node is record
      Value : Integer;
      Left  : Tree := Null;
      Right : Tree := Null;
   end record;
end Binary_Trees;

with Ada.Text_IO;
use Ada.Text_Io;

package body Binary_Trees is

   procedure insert(Item : in Integer; Into : in out Tree) is
   begin
     if Into /= null then
        if Into.Value > Item then
           insert(Item, Into.Left);
        elsif Into.Value < Item then
           insert(Item, Into.Right);
        end if; -- Do nothing if duplicate
     else
        Into := new Node;
        Into.Value := Item;
     end if;
        end insert;

   procedure in_order(Item : in Tree) is
   begin
     if Item = null then
        return;
     end if;
     in_order(Item.Left);
     Put(Integer'Image(Item.Value) & " ");
     in_order(Item.Right);
   end in_order;

   procedure pre_order(Item : in Tree) is
   begin
      if Item = null then
         return;
      end if;
      Put(Integer'Image(Item.Value) & " ");
      pre_order(Item.Left);
      pre_order(Item.Right);
   end pre_order;

   procedure post_order(Item : in Tree) is
   begin
      if Item = null then
         return;
      end if;
      post_order(Item.Left);
      post_order(Item.Right);
      Put(Integer'Image(Item.Value) & " ");
   end post_order;

   function search(Item : Integer; From : Tree) return Boolean is
      Result : Boolean;
   begin
      if From /= null then
         if From.Value = Item then
            Result := True;
         elsif From.Value > Item then
            Result := search(Item, From.Left);
         else
            Result := search(Item, From.Right);
         end if;
      else
         Result := False;
      end if;
      return Result;
   end search;
end Binary_Trees;

with Binary_Trees; use Binary_Trees;
with Ada.Numerics.Discrete_Random;
with Ada.Text_Io; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;

procedure Tree_Test is
   package Rand_Num is new Ada.Numerics.Discrete_Random(Integer);
   use Rand_Num;
   Seed : Generator;
   Val  : Integer;
   The_Tree : Tree;
begin
   Reset(Seed);
   for num in 1..20 loop
      Val := Random(Seed);
      insert(Item => Val, Into => The_Tree);
   end loop;
        -- now dump out the tree all three ways
   Put("Pre Order: ");
   pre_order(The_Tree);
   New_Line;
   Put("In order: ");
   in_order(The_Tree);
   New_Line;
   Put("Post_Order: ");
   post_order(The_Tree);
   New_Line;

        -- prompt until EOF
   loop
      Put("Search? ");
      exit when Ada.Text_IO.End_Of_File;
      Get(Val); -- read input as an integer
      if search(Item => Val, From => The_Tree) then
         Put_Line("Found " & Integer'Image(Val));
      else
         Put_Line("No " & Integer'Image(Val) & " in tree");
      end if;
   end loop;
end Tree_Test;



-- @@PLEAC@@_12.0
------------------------------------------------------------------------------
-- Ada packages provide both namespace and encapsulation. As such they act
-- both as perl packages and perl modules.

package Alpha is
   name : String := "First";
end Alpha;

package Omega is
   name : String := "Last";
end Omega;

With Ada.Text_Io; use Ada.Text_IO;
with Alpha, Omega;
with Ada.Text_Io;

procedure main is
begin
   Put_Line("Alpha is " & Alpha.name & " ," &
            "Omega is " & Omega.name);
end main;
----------------------
with Cards; -- Make the Cards package visible
use Cards;  -- Place the Cards package within the current scope
-- Both operations happen only at compile time.
----------------------

package Cards is
   type Card is private;
   -- Print the value of a card
   procedure Print(Item : in Card);
   type Deck is private;

   -- Create an initial deck (open a new deck of cards)
   function Fill_Deck return Deck;

   -- Print all the cards remaining in a deck
   procedure Print(Item : in Deck);

   -- Shuffle the deck (randomize the order of the cards in the deck)
   procedure Shuffle(The_Deck : in out Deck);

   -- Deal the next card from the deck
   procedure Deal(The_Card : out Card; From : in out Deck);

   -- Return the number of cards left in the deck
     function Cards_Left(In_Deck : Deck) return Natural;

   -- Deck_Empty exception raised when trying to deal from an empty deck.
   Deck_Empty : Exception;

private
   -- Define the face values of the cards
   type Pips is (Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
                 Jack, Queen, King, Ace);
   -- Define the card suits
   type Suits is (Hearts, Spades, Clubs, Diamonds);

   -- A card is defined by its combination of face value
   -- and suit.
   type Card is record
     Pip  : Pips;
     Suit : Suits;
   end record;

   -- Define the number of cards in a standard deck.
   subtype Deck_Index is integer range 1..52;
   -- Cards in the deck are accessed through an order list.
   -- The values in the order list are sorted to create a
   -- shuffled deck.
   type Order_List is array(Deck_Index) of Deck_Index;

   -- A deck is an order list, an index into the order list
   -- indicating the next card to deal, and a count of the
   -- number of cards left (not yeat dealt) in the deck.
   type Deck is record
      This_Order : Order_List;
      Deal_Next  : Deck_Index := Deck_Index'First;
      Num_Left   : Natural := 0;
   end record;
end Cards;
-------------------------

-- @@PLEAC@@_12.1
----------------------------------------------------------------------
-- Cards.ads
-- Package implementing a standard deck of playing cards
----------------------------------------------------------------------
package Cards is
   type Card is private;
   -- Print the value of a card
   procedure Print(Item : in Card);
   type Deck is private;
   -- Create an initial deck (open a new deck of cards)
   function Fill_Deck return Deck;

   -- Print all the cards remaining in a deck
   procedure Print(Item : in Deck);

   -- Shuffle the deck (randomize the order of the cards in the deck)
   procedure Shuffle(The_Deck : in out Deck);

   -- Deal the next card from the deck
   procedure Deal(The_Card : out Card; From : in out Deck);

   -- Return the number of cards left in the deck

   function Cards_Left(In_Deck : Deck) return Natural;

   -- Deck_Empty exception raised when trying to deal from an empty deck.
   Deck_Empty : Exception;
private
   -- Define the face values of the cards
   type Pips is (Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
                 Jack, Queen, King, Ace);
   -- Define the card suits
   type Suits is (Hearts, Spades, Clubs, Diamonds);

   -- A card is defined by its combination of face value
   -- and suit.
   type Card is record
     Pip  : Pips;
     Suit : Suits;
   end record;

   -- Define the number of cards in a standard deck.
   subtype Deck_Index is integer range 1..52;

   -- Cards in the deck are accessed through an order list.
   -- The values in the order list are sorted to create a
   -- shuffled deck.

   type Order_List is array(Deck_Index) of Deck_Index;

   -- A deck is an order list, an index into the order list
   -- indicating the next card to deal, and a count of the
   -- number of cards left (not yeat dealt) in the deck.
   type Deck is record
      This_Order : Order_List;
      Deal_Next  : Deck_Index := Deck_Index'First;
      Num_Left   : Natural := 0;
   end record;
end Cards;
--------------------

-- @@PLEAC@@_12.2
--------------------
-- No error trapping required in Ada, since all errors are detected
-- at compile time.
--------------------

-- @@PLEAC@@_12.3
--------------------
-- Not possible in Ada
--------------------

-- @@PLEAC@@_12.4
-------------------
-- Place the variables in the package implementation, or "body"


with Ada.Numerics.Float_Random;
use Ada.Numerics.Float_Random;
with Ada.Text_Io; use Ada.Text_Io;

package body Cards is
   type Card_Deck is array(Deck_Index) of Card;
-------------
-- Internal Function: Initialize
-- Purpose: Initialize the value of the common Card_Deck
-------------
   function Initialize return Card_Deck is
      Result : Card_Deck;
      Temp_Index : Integer := Deck_Index'First;
   begin
      for The_Suit in Suits loop
         for The_Pip in Pips loop
            Result(Temp_Index) := (The_Pip, The_Suit);
            Temp_Index := Temp_Index + 1;
         end loop;
      end loop;
      return Result;
   end Initialize;

   All_Decks : constant Card_Deck := Initialize;
-- private to the Cards package
----------
-- Procedure: Print
-- Purpose: Print the value of a card on standard output
----------
   procedure Print(Item : in Card) is
      package Pip_Io is new Ada.Text_Io.Enumeration_IO(Pips);
      package Suit_Io is new Ada.Text_Io.Enumeration_Io(Suits);
   begin
      Pip_Io.Put(Item => Item.Pip);
      Put(Item => " of ");
      Suit_Io.Put(Item => Item.Suit);
      New_Line;
   end Print;
----------------
-- Function: Fill_Deck
-- Purpose: Create a new card deck with all cards in order
---------------
   function Fill_Deck return Deck is
      Result : Deck;
      Temp_Index : Integer := Deck_Index'First;
   begin
      for Temp_Index in Deck_Index'Range loop
          Result.This_Order(Temp_Index) := Temp_Index;
      end loop;
      Result.Num_Left := Deck_Index'Last;
      return Result;
   end Fill_Deck;
--------
-- Procedure: Print
-- Purpose: Print all the cards remaining in the deck
--------
   procedure Print(Item : in Deck) is
   begin
      if Item.Num_Left > 0 then
         for Temp_Index in Item.Deal_Next..Deck_Index'Last loop
            Print(All_Decks(Item.This_Order(Temp_Index)));
         end loop;
      else
         Put_Line("The deck is empty.");
      end if;
end Print;

-----------
-- Procedure Swap
-- Exchange two Deck_Index values
-------------
   procedure Swap(Left, Right : in out Deck_Index) is
      Temp : Deck_Index := Left;
   begin
      Left := Right;
      Right := Temp;
   end Swap;
------------
-- Procedure: Shuffle
-- Purpose: Randomize the This_Order array for a deck to force
-- random access to the deck of cards
--  This algorithm is order O(n) and will work with any discrete
--  index type.
--  The Ada.Numerics.Float_Random routine is used so that the
--  random number generator is reset only once per shuffle. This
--  produces more random results than can be achieved by
--  resetting the generator for each iteration as would be needed
--  if the Ada.Numerics.Discrete_Random package had been used.
-----------
   procedure Shuffle(The_Deck : in out Deck) is
      use Ada.Numerics.Float_Random;
      Seed       : Generator;
      Max_Search : Deck_Index := Deck_Index'Pred(Deck_Index'Last);
      Difference : Integer;
      Rand_Value : Integer;
      Swap_Val   : Deck_Index;
   begin
      Reset(Seed);
      The_Deck.Deal_Next := Deck_Index'First;
      The_Deck.Num_Left  := Deck_Index'Last;
      for Index in Deck_Index'First .. Max_Search loop
         Difference := Deck_Index'Pos(Deck_Index'Last) -
                       Deck_Index'Pos(Index);
         Rand_Value := Integer( Random(Seed) * Float(Difference)) +
                       Deck_Index'Pos(Index);
         Swap_Val := Deck_Index'Val(Rand_Value);
         Swap(The_Deck.This_Order(Index), The_Deck.This_Order(Swap_Val));
      end loop;
      The_Deck.Num_Left := Deck_Index'Last;
      The_Deck.Deal_Next := Deck_Index'First;
   end Shuffle;

   procedure Deal(The_Card : out Card; From : in out Deck) is
   begin
      if From.Num_Left > 0 then
         The_Card := All_Decks(From.This_Order(From.Deal_Next));
         From.Num_Left := From.Num_Left - 1;
         if From.Deal_Next < Deck_Index'Last then
            From.Deal_Next := From.Deal_Next + 1;
         end if;
      else
         raise Deck_Empty;
      end if;
   end Deal;

   function Cards_Left(In_Deck : Deck) return Natural is
   begin
      return In_Deck.Num_Left;
   end Cards_Left;
end Cards;
------------------

-- @@PLEAC@@_12.5
------------------
-- Not possible in Ada. All such determinations are done at compile time
------------------


-- @@PLEAC@@_13.0
------------------------------------------------------------------------------
-- Ada separates aspects of classes into packages, for encapsulation, and
-- tagged types, for type inheritance and type expansion.
-- Ada does not have constructors or destructors.

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

package People is
   type Person is tagged private;
   procedure SetAge(Subject : out Person; Age : in Natural);
   function GetAge(Subject : Person) return Natural;
   procedure SetName(Subject : out Person; Name : in String);
   function Get_Name(Subject : Person) return String;
private
   type Person is tagged record
      Age : Natural := 0;
      Name : Unbounded_String := Null_Unbounded_String;
   end record;
end People;

package body People is
   procedure SetAge(Subject : out Person; Age : in Natural) is
   begin
      Subject.Age := Age;
   end SetAge;

   function GetAge(Subject : Person) return Natural is
   begin
      return Subject.Age;
   end GetAge;

   procedure SetName(Subject : out Person; Name : in String) is
   begin
      Subject.name := To_Unbounded_String(Name);
   end SetName;

   procedure GetName(Subject : Person) return String is
   begin
      return To_String(Subject.GetName);
   end GetName;
end People;
-------------------------------
-- Creating an instance of a class

Charley : Person;

-- Using a class

SetName(Charley, "Charles");
SetAge(Charley, 15);

Put_Line(GetName(Charley) & " is " & Integer'Image(GetAge(Charley)) &
         " years old.");
-- Charles is 15 years old.
------------------------------------------------------------------------------

-- @@PLEAC@@_13.1
------------------------------------------------------------------------------
-- Objects can be constructed on the stack, or dynamically

-- Constructing an object on the stack
Student : Person;

-- Constructing an object dynamically
type Person_Ptr is access Person;

Student_P : Person_Ptr := new Person;
------------------------------------------------------------------------------

-- @@PLEAC@@_13.2
------------------------------------------------------------------------------
-- Only dynamically allocated objects need to be destroyed.
-- Instantiate an instance of Unchecked_Deallocation for the type

procedure free is new Ada.Unchecked_Deallocation(Person, Person_Ptr);

Student_P : Person_Ptr := new Person;

free(Student_p);
------------------------------------------------------------------------------

-- @@PLEAC@@_13.3
------------------------------------------------------------------------------
-- Public instance data can be accessed directly
-- Private instance data can only be accessed directly by subprograms defined
-- in the same package as the type definition.

package People is
   type Person is tagged private;
   procedure SetAge(Subject : out Person; Age : in Natural);
   function GetAge(Subject : Person) return Natural;
   procedure SetName(Subject : out Person; Name : in String);
   function Get_Name(Subject : Person) return String;
private
   type Person is tagged record
      Age : Natural := 0;
      Name : Unbounded_String := Null_Unbounded_String;
   end record;
end People;

package body People is
   procedure SetAge(Subject : out Person; Age : in Natural) is
   begin
      Subject.Age := Age;
   end SetAge;

   function GetAge(Subject : Person) return Natural is
   begin
      return Subject.Age;
   end GetAge;

   procedure SetName(Subject : out Person; Name : in String) is
   begin
      Subject.name := To_Unbounded_String(Name);
   end SetName;

   procedure GetName(Subject : Person) return String is
   begin
      return To_String(Subject.GetName);
   end GetName;
end People;
------------------------------------------------------------------------------

-- @@PLEAC@@_13.4
------------------------------------------------------------------------------
-- Class data is defined in the package body, and is not accessable by any
-- subprograms outside the package body.
package body Counters is
   type Modular_Counter is mod 100;
   Internal_Counter : Modular_Counter := 0;
   --....
end Counters;
------------------------------------------------------------------------------


-- @@PLEAC@@_13.5
------------------------------------------------------------------------------
-- Ada allows public definition of records without tags.
-- These approximate structs in C.

package Complex_Nums is
   type Complex_Number is record
      Real : Float := 0.0;
      Imaginary : Float := 0.0;
   end record;
end Complex_Nums;
------------------------------------------------------------------------------

-- @@PLEAC@@_13.6
------------------------------------------------------------------------------
-- cloning dynamically allocated objects
P1 : new Person_Ptr;
P2 : new Person_Ptr;
P2.all := P1.all;

-- cloning stack based objects
P1 : Person;
P2 : Person := P1;
------------------------------------------------------------------------------

-- @@PLEAC@@_13.7


-- @@PLEAC@@_13.8
------------------------------------------------------------------------------
type Person is tagged private;
type Employee is new Person with private;
type Salaried_Employee is new Employee with private;
type Person_Pointer is access Person'Class;

P : Person_Pointer;

if P in Employee then
   -- P points to a member of Employee or Salaried_Employee
end if;
------------------------------------------------------------------------------

-- @@PLEAC@@_13.9
------------------------------------------------------------------------------
package People is
   type Person is tagged private;
   procedure SetAge(Subject : out Person; Age : in Natural);
   function GetAge(Subject : Person) return Natural;
   procedure SetName(Subject : out Person; Name : in String);
   function Get_Name(Subject : Person) return String;
private
   type Person is tagged record
      Age : Natural := 0;
      Name : Unbounded_String := Null_Unbounded_String;
   end record;
end People;

package body People is
   procedure SetAge(Subject : out Person; Age : in Natural) is
   begin
      Subject.Age := Age;
   end SetAge;

   function GetAge(Subject : Person) return Natural is
   begin
      return Subject.Age;
   end GetAge;

   procedure SetName(Subject : out Person; Name : in String) is
   begin
      Subject.name := To_Unbounded_String(Name);
   end SetName;

   procedure GetName(Subject : Person) return String is
   begin
      return To_String(Subject.GetName);
   end GetName;
end People;
-------------------------------
Dude : Person;
SetName(Dude, "Jason");
SetAge(Dude, 23);
-------------------------------
-- inheriting from Person
type Employee is new Person with record
   Salary : Float := 0.0;
end record;
-------------------------------
emp1 : Employee;
-------------------------------
SetName(emp1, "Jason"); -- call the inherited procedures
SetAge(emp1, 23);       -- to access the inherited private fields
emp1.Salary := 200.0; -- access the public salary field
Put_Line(GetName(emp1) & " is age " & Integer'Image(GetAge(emp1)) &
         ", the salary is " & Float'Image(emp1.Salary));
-------------------------------
-- Ada does not provide any built in tagged types
------------------------------------------------------------------------------

-- @@PLEAC@@_13.10
------------------------------------------------------------------------------
package Employees is
   subtype Title_String is string(1..20);
   type Employee is new Person with record
      Salary : Float := 0;
      Title  : Title_String;
   end record;

   function GetName(Subject : Employee) return String;
end Employees;

package body Employees is
   function GetName(Subject : Employee) return String is
   begin
      -- call the inherited GetName function:
      return GetName(Person(Subject)) & " : " & Title;
   end GetName;
end Employees;
------------------------------------------------------------------------------

-- @@PLEAC@@_13.14
------------------------------------------------------------------------------
type Nums_Array is array(Positive range 1..10) of Integer;

-- Overload the "+" operator to allow addition of integers and items of
-- type Num_Array

function "+" (left : Num_Array; Right : Integer) return Num_Array;
function "+" (left : Integer; Right : Num_Array) return Num_Array;
------------------------------------------------------------------------------