Thursday, November 22, 2018

Ada Loop Tutorial


The Ada programming language provides four different looping constructs; the “for” loop, the “while” loop, the simple loop and the container iterator loop.

The For loop

The Ada “for” loop iterates over a range of discrete values. Ada discrete values include signed integer values, modular (unsigned) integer values, and enumeration values. Ada ranges are always specified as lowest_value..highest value. Every “for” loop has a loop variable which is NOT declared before it is used. The loop variable has the type of the loop range. The loop variable cannot be altered within the body of the loop.

Ranges are always specified as lowest_value..highest_value, even if one wants to iterate through the range in reverse order. Any range where the first value is greater than the last value is considered an empty range. To iterate through a range in reverse order simply add the “reverse” reserved word

For Num in reverse 1..10 loop

Example For loop

with Ada.Text_Io; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure For_Loop is
   C     : Positive;
   start : time;
   Stop  : Time;
begin
   Start := Clock;
   for I in 1..10**9 loop
      C := I;
   end loop;
   Stop := Clock;
   Put_Line("counted " & C'Image & " numbers in " & Duration'Image(Stop - Start)
            & " seconds");
end For_Loop;

The loop variable in this example is named “I”. The first iteration of the loop variable I contains the value 1. The second iteration I contains 2. The final iteration of the loop I contains 10**9 (1000000000).

The output of the program is

counted  1000000000 numbers in  0.577727576 seconds

The While loop

The Ada while loop iterates while the condition specified at the top of the loop is true. The condition is always evaluated before the start of an iteration. The condition must evaluate to a Boolean value (False or True). Ada Boolean values are an enumeration type, not a numeric type. Ada does NOT provide any implicit conversion between zero and False or between not-zero and True as is done in the C language.

Example While loop

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure While_Loop is
   Start : Time;
   Stop  : Time;
   C     : Positive := 1;
begin
   Start := Clock;
   while C < 10**9 loop
      C := C + 1;
   end loop;
   Stop := Clock;
   Put_Line("counted " & C'Image & " numbers in " & Duration'Image(Stop - Start)
              & " seconds");
end While_Loop;

Unlike C, C++ or Java, the Boolean expression in Ada does not need to be enclosed in parentheses “()”.

The output of the program is

counted  1000000000 numbers in  0.559296318 seconds

The Simple loop

The Ada simple loop continues to iterate until an exit is executed within the body of the loop. If the body of the loop does not contain an exit command the loop behaves as an infinite loop. The exit command is typically found within a conditional expression such as

if C = 10**9 then
   exit;
end if;

The expression above can be abbreviated to

exit when C = 10**9;

Example Simple loop

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure Simple_Loop is
   Start : Time;
   Stop  : Time;
   C     : Positive := 1;
begin
   Start := Clock;
   loop
      C := C + 1;
      exit when C = 10**9;
   end loop;
   Stop := Clock;
   Put_Line("counted " & C'Image & " numbers in " & Duration'Image(Stop - Start)
              & " seconds");
end Simple_Loop;

The output of the program is

counted  1000000000 numbers in  0.576855340 seconds

The Iterator loop

The iterator loop is used to iterate through containers, and may be used with arrays. The iterator loop traverses the values of the container object or array object allowing reading or manipulation of each value in sequence.

The iterator loop strongly resembles the “for” loop with some subtle differences. The “loop variable” is actually a member of the container or array. No range is specified. The iteration proceeds through each data element in the container or array.

Example Iterator loop

The following example dynamically allocates an array of 10**9 elements. Dynamic memory allocation is used because such an array is too large to fit on the program stack.

with Ada.Text_Io; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure Iterator_Loop is
   type Nums_Array is Array(1..10**9) of Integer;
   type Nums_Access is access Nums_Array;
   Nums : Nums_Access := new Nums_Array;
   Start, Stop : Time;
   Counter : Integer := 1;
begin
   Start := Clock;
   for Value of Nums.all loop
      Value := Counter;
      Counter := Counter + 1;
   end loop;
   Stop := Clock;
   Put_Line("counted " & Nums(Nums'Last)'Image & " numbers in " &
            Duration'Image(Stop - Start) & " seconds");
end Iterator_Loop;

The variable Nums is defined to be an access type which references an instance of Nums_Array. The syntax Nums.all evaluates to the array referenced by Nums.

The output of the program is

counted  1000000000 numbers in  3.208108244 seconds

As you can see, iterator loops are much slower than the other loop constructs.

Monday, November 19, 2018

Comparison of Simple Matrix Code in C and Ada


Summary
A matrix is a two dimensional array of elements. Matrices are commonly used to represent spread sheets or tables of information. A square matrix is a matrix with the same number of rows and columns. A square matrix is said to be symmetric if the transpose of the matrix is equal to the original matrix. Only square matrices can be symmetric.

The website https://www.studytonight.com/c/programs/array/check-square-matrix-is-symmetric-or-not provides an example of a C program to determine if a square matrix input by the user is symmetric or not. The source code for the C program can be viewed through the link shown above.

Remarks concerning the C code

The code works very well within limits. The limits of its proper behavior are defined by the declaration of the arrays found on line 7 of the C source code.

int c, d, a[10][10], b[10][10], n, temp;

Two matrices are declared. Both matrices have a dimension of 10. While 10 may be a useful arbitrary dimension for an example, this approach exposes the program to possible buffer overflow. The obvious way to avoid such buffer overflow in C is to dynamically allocate the two matrices after inputting the matrix dimension from the user. The downside of using dynamically allocated matrices is the need to also explicitly use pointers. Specifically, the line quoted above would need to be changed to the following.

int c, d, **a, **b, n, temp;

This section of the StudyTonight web site is trying to concentrate on arrays without exposing their relationship to pointers in C.
Similarly, I suspect the example does not define a function to display the matrices because of the need to pass the matrix as an int **.

Functionally comparable Ada code

Following is Ada code which performs the same actions as the C code while avoiding any exposure to buffer overflow and also avoiding complicated pointer notation.
Ada arrays are first class types. This is true no matter how many dimensions an array contains.

-----------------------------------------------------------------------
-- Square Matrix Symmetry
-----------------------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_Io;

procedure Symmetric_Matrix is

   type Matrix is array(Positive range <>, Positive range <>) of Integer;
  
   procedure Print(M : Matrix) is
   begin
      for Row in M'Range(1) loop
         for Col in M'Range(2) loop
            Put(M(Row, Col)'Image & " ");
         end loop;
         New_Line;
      end loop;
   end Print;
  
   Dimension : Positive;
begin
   Put_Line("Enter the dimension of the matrix: ");
   Get(Dimension);
   declare
      A : Matrix(1..Dimension, 1..Dimension);
      B : Matrix(1..Dimension, 1..Dimension);
   begin
      Put_Line("Enter the" & Positive'Image(Dimension * Dimension) &
                 " elements of the matrix:");
      for Value of A loop
         Get(Value);
      end loop;
     
      -- find the transpose of A and store it in B
     
      for Row in A'Range(1) loop
         for Col in A'Range(2) loop
            B(Col, Row) := A(Row, Col);
         end loop;
      end loop;
     
      -- print matrix A
      New_Line;
      Put_Line("The original matrix is:");
     
      Print(A);
     
      -- print the transpose of A
      New_Line;
      Put_Line("The transpose matrix is:");
      Print(B);
     
      -- Checking if the original matrix is the same as its transpose
     
      if A = B then
         Put_Line("The matrix is symmetric.");
      else
         Put_Line("The matrix is not symmetric.");
      end if;
   end;
end Symmetric_Matrix;

Remarks concerning the Ada code

A Matrix type is defined as an unconstrained two dimensional array with each index of the subtype Positive. Each element of type Matrix is an Integer. Use of an unconstrained array type allows the program to create instances of Matrix containing exactly the number of elements specified by the user input.

Every Ada array has several attributes which are always available to the programmer. This program uses the ‘Range attribute which evaluates to the range of index values for the array. Since Matrix is a two-dimensional array there are two range attributes. ‘Range(1) evaluates to the index values for the first dimension of the array. ‘Range(2) evaluates to the index values for the second dimension of the array. The procedure Print has a single parameter M, which is an instance of type Matrix. Since Matrix is an unconstrained type the parameter M may correspond to any instance of Matrix, no matter what the sizes of the dimensions may be.

The “declare” block inside the Ada program allows the definition of Matrix A and Matrix B according to the user input all allocated from the program stack rather than from the heap, thus avoiding the use of pointers or their Ada analogs called access types.
The input loop used to fill Matrix A is an iterator loop. The loop parameter Value references each value in Matrix A in sequence.

Ada implicitly provides equality testing for array instances. In this program we simply compare Matrix A with Matrix B. If they are equal then Matrix A is symmetric, otherwise Matrix A is not symmetric.

The output of a representative execution of this program is:

Enter the dimension of the matrix:
3
Enter the 9 elements of the matrix:
1 7 3 7 5 -5 3 -5 6

The original matrix is:
 1  7  3
 7  5 -5
 3 -5  6

The transpose matrix is:
 1  7  3
 7  5 -5
 3 -5  6
The matrix is symmetric.

The Ada solution is somewhat simpler than the C solution while also being more secure. If the user chooses to specify a matrix with a dimension greater than 10 using the C version the variable ‘n’ will be overwritten with possibly catastrophic effects on the correctness of the program. The Ada program suffers no such security flaws.

Saturday, October 20, 2018

Ada Pre and Post Conditions


Ada preconditions and post-conditions are implemented using aspect clauses. While aspect clauses can include many other terms used to specify program behavior, this posting will focus on preconditions and post-conditions.


A thorough discussion of preconditions and post-conditions can be found at http://www.ada-auth.org/standards/12rat/html/Rat12-2-3.html

Since its first official version in 1983 the Ada language has always allowed the programmer to define data types and subtypes with specific ranges. For example:

type Byte is range -2**7..2**7 – 1;         -- signed integer type
type Unsigned_Byte is mod 2**8;             -- modular type
type Normalized is digits 5 range 0.0..1.0; -- floating point type
type Money is digits 10 delta 0.01;         -- decimal fixed point type
subtype Uppers is Character range ‘A’..’Z’; -- character subtype
subtype Positive is Integer range 1..Integer’Last;
type Days is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);

When a defined type or subtype is used as a function or procedure parameter that type or subtype acts as a pre-condition on the call of the function or procedure.

procedure Swap(Left, Right : in out Positive);

The procedure example above defines a procedure named  Swap. The procedure takes two parameters of the subtype Positive. Even though subtype Positive is a subtype of Integer and every instance of Positive is also an instance of Integer, not every instance of Integer is an instance of Positive.  The Integer type can represent values less than 1, while the subtype Positive cannot. The Ada compiler writes run-time checks to ensure that the values passed to Swap are integers within the range of subtype Positive, creating a precondition for the procedure that all values passed to it must be Integer values greater than 0.

This use of strong typing in parameter calls provides some very limited precondition capability. A more robust precondition capability combined with a post-condition capability was introduced in the Ada 2012 standard.

Preconditions provide a guarantee to the writer of the function or procedure that a condition will be true when the program is called. Post-conditions provide a guarantee to the caller of the function or procedure that a condition will be true when the function or procedure call returns.

The precondition becomes a contract between the caller and the called subprogram when the subprogram is called. The post-condition becomes a contract between the caller and the called subprogram when the called subprogram returns. These contracts are direct implementations of the requirements for the called subprogram.

Stack Example

The following example shows a stack implementation which includes definition of some preconditions and some post-conditions.

-----------------------------------------------------------------------
-- Package implementing a generic bounded stack
-----------------------------------------------------------------------
Generic

   type Element_Type is private;

   with function Image(E : Element_Type) return String;

package Bounded_Stack is

   type Stack(Size : Positive) is tagged private;

   function Is_Full(S : in Stack) return Boolean;

   function Is_Empty(S : in Stack) return Boolean;

   procedure Push(S : in out Stack; Item : in Element_Type) with
     Pre => not S.Is_Full,
     Post => not S.Is_Empty;

   procedure Pop(S : in out Stack; Item : out Element_Type) with
     Pre => not S.Is_Empty,
     Post => not S.Is_Full;

   procedure Display(S : in Stack);

private

   type Buf is array(Positive range <>) of Element_Type;

   type Stack(Size : Positive) is tagged record
      Stk   : Buf(1..Size);
      Top   : Positive := 1;
      Count : Natural := 0;
   end record;

end Bounded_Stack;

This package specification defines the public interface and the private data definitions for a generic bounded stack ADT.  A bounded stack is created with a fixed maximum size.

The procedure Push pushes an item onto the stack. The precondition for Push requires the Stack parameter S to not be full (not S.Is_Full). The post-condition requires that after the successful Push operation the stack will not be empty (not S.Is_Empty). The Pop procedure has inverse requirements. One can only Pop a value from the stack if the stack is not empty before the procedure Pop is called (not S.Is_Empty). After a successful Pop operation the stack will not be full (not S.Is_Full).

The precondition and the post-condition seem nice enough, but how do they help the programmer develop correct code? Let’s look first at the implementation of the subprograms for the generic bounded stack and then at the “main” procedure used to test this stack ADT.

with Ada.Text_IO; use Ada.Text_IO;

package body Bounded_Stack is

   -------------
   -- Is_Full --
   -------------

   function Is_Full (S : in Stack) return Boolean is
   begin
      return S.Count = S.Size;
   end Is_Full;

   --------------
   -- Is_Empty --
   --------------

   function Is_Empty (S : in Stack) return Boolean is
   begin
      return S.Count = 0;
   end Is_Empty;

   ----------
   -- Push --
   ----------

   procedure Push
     (S : in out Stack; Item : in Element_Type) is
   begin
      S.Stk(S.Top) := Item;
      S.Top := S.Top + 1;
      S.Count := S.Count + 1;
   end Push;

   ---------
   -- Pop --
   ---------

   procedure Pop
     (S : in out Stack; Item : out Element_Type) is
   begin
      S.Top := S.Top - 1;
      Item := S.Stk(S.Top);
      S.Count := S.Count - 1;
   end Pop;

   -------------
   -- Display --
   -------------

   procedure Display (S : in Stack) is
   begin
      if S.Is_Empty then
         Put_Line("Stack is empty.");
      else
         for index in reverse 1..S.Top - 1 loop
            Put_Line(Image(S.Stk(Index)));
         end loop;
      end if;
      New_Line;
   end Display;

end Bounded_Stack;

Now, let’s focus on the Push and Pop procedures, since their specifications include preconditions and post-conditions.

   ----------
   -- Push --
   ----------

   procedure Push
     (S : in out Stack; Item : in Element_Type) is
   begin
      S.Stk(S.Top) := Item;
      S.Top := S.Top + 1;
      S.Count := S.Count + 1;
   end Push;

   ---------
   -- Pop --
   ---------

   procedure Pop
     (S : in out Stack; Item : out Element_Type) is
   begin
      S.Top := S.Top - 1;
      Item := S.Stk(S.Top);
      S.Count := S.Count - 1;
   end Pop;

Since the precondition for Pop guarantees that the stack is not full when this procedure is called there is no need to check for a stack-full condition within the procedure. Similarly there is no need for the Pop procedure to check if the stack is empty. The precondition for Pop guarantees that the stack is not empty when Pop is successfully called.

The programmer can simply assume the preconditions are satisfied while writing the code for a subprogram with preconditions.

Now, let’s look at the “main” procedure used to test this ADT:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with bounded_Stack;


procedure Main is
   type Options is (Push, Pop, Display, Quit);

   package Int_Stack is new bounded_Stack(Integer, Integer'Image);
   use Int_Stack;

   S : Stack(5);

   function Menu return Options is
      package Opts_Io is new Ada.Text_IO.Enumeration_IO(Options);
      use Opts_Io;
      Value : Options;
   begin
      Put_Line("-----------------------------------");
      Put_Line("    Push");
      Put_Line("    Pop");
      Put_Line("    Display");
      Put_Line("    Quit");
      Put_Line("-----------------------------------");
      Put_Line("Enter your choice");
      Get(Value);
      return Value;
   end Menu;

   Choice : Options;
   New_Value : Integer;
   Popped_Value : Integer;

begin
   loop
      Choice := Menu;
      case Choice is
         when Push =>
            Put("Enter the new value to push on the stack: ");
            Get(New_Value);
            S.Push(New_Value);
         when Pop =>
            S.Pop(Popped_Value);
            Put_Line("Popped " & Popped_VAlue'Image);
         when Display =>
            Put_Line("Stack contents:");
            S.Display;
         when Quit =>
            exit;
      end case;
   end loop;
end Main;

This test makes an instance of the Bounded_Stack package passing in the type Integer and the function Integer’Image. This creates a stack package containing Integer elements. The variable S is defined to be an instance of Stack from that package. This instance is set to contain a capacity of 5 elements.

S : Stack(5);

A function is defined to display and manipulate a text menu for interacting with the stack.  The function returns the value of type Options input by the user. The executable part of the Main procedure simply loops through calling the Menu function and handling the return value of that function until the Quit option is chosen.

The following output shows what happens when the first option chosen is to Pop a value from the stack. In this case the stack is still empty because no value has first been pushed onto the stack.

-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
Pop

raised SYSTEM.ASSERTIONS.ASSERT_FAILURE : failed precondition from bounded_stack.ads:16 instantiated at main.adb:7
[2018-10-20 09:23:58] process exited with status 1, elapsed time: 06.38s

Notice that the program immediately terminated due to the exception SYSTEM.ASSERTIONS.ASSERT_FAILURE. Furthermore, the exception was raised because the precondition stated in the file bounded_stack.ads, line 16 was violated for the instance of Bounded_Stack instantiated at line 7 of the file main.adb.

Line 16 of bounded_stack.ads is the line containing the precondition for the Pop operation.
Now let’s look at the behavior of pushing too many items onto the stack:

-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
push
Enter the new value to push on the stack: 1
-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
push
Enter the new value to push on the stack: 2
-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
push
Enter the new value to push on the stack: 3
-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
push
Enter the new value to push on the stack: 4
-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
push
Enter the new value to push on the stack: 5
-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
display
Stack contents:
 5
 4
 3
 2
 1

-----------------------------------
    Push
    Pop
    Display
    Quit
-----------------------------------
Enter your choice
push
Enter the new value to push on the stack: 6


raised SYSTEM.ASSERTIONS.ASSERT_FAILURE : failed precondition from bounded_stack.ads:13 instantiated at main.adb:7
[2018-10-20 09:44:39] process exited with status 1, elapsed time: 38.56s

Again, the exception SYSTEM.ASSERTIONS.ASSERT_FAILURE was raised, this time the precondition for the Push operation was violated.

In both cases the program was terminated because the precondition for a procedure call was violated. The preconditions prevented buffer overflow errors while ensuring the requirements for the Push and Pop procedures.

Comparison of three algorithms for summing an array of integers

This article shares a comparison of the time taken to sum an array of integers. This article tests three algorithms for summing the array. •...