CONSTRAINED AND UNCONSTRAINED ARRAYS


1. OVERVIEW

A constrained array is an array where the index is specified (and hence the number of components is specified), we say that the bounds are static, hence constrained arrays are sometimes referred to as static arrays. So far we have only looked at such constrained arrays. Many imperative languages (including Ada) support the concept of unconstrained arrays. When an unconstrained array is declared the index type is a specified but there is no need to state the limit of the index. Ada makes use of the symbol <> to indicate an unconstrained array. The format for declaring unconstrained array types in Ada is as follows:

type TYPE_NAME is array (INDEX_TYPE range <>) of <ELEMENT_TYPE>

Where INDEX_TYPE is a discrete type (e.g. integer, character or an enumeration type). For example:

type LIST1 is array (INTEGER range <>) of FLOAT;

When a data item of an unconstrained array type is declared the index constraints must be specified. Example:

ITEM : LIST1(1..10);

2. PASSING ARRAYS AS PARAMETERS

It is usefull to make procedures/functions as general as possible by making use of unconstrained array types for formal parameters. Consider the following piece of code:

with TEXT_IO;
use TEXT_IO;

procedure NUM_ARRAY_EXAMPLE1 is
    package FLOAT_INOUT is new FLOAT_IO(FLOAT);
    use FLOAT_INOUT;
    type NUM_ARRAY is array (INTEGER range <>) of FLOAT;
    FL_ARRAY_1 : NUM_ARRAY(1..6) := (1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
    FL_ARRAY_2 : NUM_ARRAY(4..7) := (5.0, 6.0, 7.0, 8.0);
    FL_ARRAY_3 : NUM_ARRAY(1..8) := (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);

    --------------------------------------
    procedure PUT_FL_ARRAY(FL_ARRAY : NUM_ARRAY) is
    begin
        for LOOP_PARAMETER in FL_ARRAY'FIRST..FL_ARRAY'LAST loop
                PUT(FL_ARRAY(LOOP_PARAMETER),AFT=>1,EXP=>0);
        end loop;
        NEW_LINE;
    end PUT_FL_ARRAY;
    --------------------------------------
begin
    PUT_FL_ARRAY(FL_ARRAY_1);
    PUT_FL_ARRAY(FL_ARRAY_2);
    PUT_FL_ARRAY(FL_ARRAY_3);
end NUM_ARRAY_EXAMPLE1;

This uses an unconstraind array type for the formal parameter to the procedure PUT_FL_ARRAY. Consequently this procedure will operate successfully with floating point number arrays of any length. The alternative to the above (using constrained arrays) would be as follows:

with TEXT_IO;
use TEXT_IO;

procedure NUM_ARRAY_EXAMPLE2 is
    package FLOAT_INOUT is new FLOAT_IO(FLOAT);
    use FLOAT_INOUT;
    type NUM_ARRAY1 is array (INTEGER range 1..6) of FLOAT;
    type NUM_ARRAY2 is array (INTEGER range 1..4) of FLOAT;
    type NUM_ARRAY3 is array (INTEGER range 1..8) of FLOAT;
    FL_ARRAY_1 : NUM_ARRAY1 := (1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
    FL_ARRAY_2 : NUM_ARRAY2 := (5.0, 6.0, 7.0, 8.0);
    FL_ARRAY_3 : NUM_ARRAY3 := (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);

    --------------------------------------
    procedure PUT_FL_ARRAY1(FL_ARRAY : NUM_ARRAY1) is
    begin
        for LOOP_PARAMETER in FL_ARRAY'FIRST..FL_ARRAY'LAST loop
                PUT(FL_ARRAY(LOOP_PARAMETER),AFT=>1,EXP=>0);
        end loop;
        NEW_LINE;
    end PUT_FL_ARRAY1;
    --------------------------------------
    procedure PUT_FL_ARRAY2(FL_ARRAY : NUM_ARRAY2) is
    begin
        for LOOP_PARAMETER in FL_ARRAY'FIRST..FL_ARRAY'LAST loop
                PUT(FL_ARRAY(LOOP_PARAMETER),AFT=>1,EXP=>0);
        end loop;
        NEW_LINE;
    end PUT_FL_ARRAY2;
    --------------------------------------
    procedure PUT_FL_ARRAY3(FL_ARRAY : NUM_ARRAY3) is
    begin
        for LOOP_PARAMETER in FL_ARRAY'FIRST..FL_ARRAY'LAST loop
                PUT(FL_ARRAY(LOOP_PARAMETER),AFT=>1,EXP=>0);
        end loop;
        NEW_LINE;
    end PUT_FL_ARRAY3;
    --------------------------------------
begin
    PUT_FL_ARRAY1(FL_ARRAY_1);
    PUT_FL_ARRAY2(FL_ARRAY_2);
    PUT_FL_ARRAY3(FL_ARRAY_3);
end NUM_ARRAY_EXAMPLE2;

Note that, without using unconstrained arrays, we have had to define three distinct array types and an output procedure for each of these types.


3. EXAMPLE PROBLEM PASCAL'S TRIANGLE


3.1. Requirements

To develop an Ada program that expands expressions of the form (1+x)^n, for any value of n ranging from 1 to 32, using Pascal's triangle to determine the appropriate coefficients. Recall that Pascal's triangle is of the form:

            1   1
          1   2   1
        1   3   3   1
      1   4   6   4   1
    1   5  10  10   5   1
  1   6  15  20  15   6   1
1   7  21  35  35  21   7   1
Etc.

where the start and end coefficient in each line are 1 (unity) and each of the other coefficients is the sum of the corresponding coefficient and the preceding coefficient in the line above. Examples:

                          (1 + x) = 1 + x
       (1 + x)^2 = (1 + x)(1 + x) = 1 + 2x + x^2
(1 + x)^3 = (1 + x)(1 + x)(1 + x) = 1 + 3x + 3x^2 + x^3
     (1 + x)^4 = (1 + x)(1 = x)^3 = 1 + 4x + 6x^2 + 4x^3 + x^4
     (1 + x)^5 = (1 + x)(1 = x)^4 = 1 + 5x + 10x^2 + 10x^3 + 5x^4 + x^4

and so on.


3.2. Design

A top-down analysis of the proposed problem is given below.

TOP DOWN ANALYSIS

We will store the current line in the Triangle in an unconstrained array and use this to build the following line. The start line array will comprise two elements: 1 and 1. We will use a recursive function to iterate through the levels in the triangle until level N is reached. Once we have established line N in the triangle we will use the resulting array of coefficients to expand the expression (1-x)^N for the appropriate value of N.

  1. PASCALS_TRIANGLE (top level procedure): Input power N, check for power of 1 in which case direct output ((1+x)). Otherwise call PASC_TRIANG.
    NAMEUSAGETYPERANGE
    POWER_NLocal variableINTEGER1.32
  2. PASC_TRIANG (Level 2 procedure): Initiate expansion. Calls to CALCULATE_COEF and EXPANSION. Include type definition for LINE_ARRAY and initialisation of start line ({1, 1}).
    NAMEUSAGETYPERANGE
    POWERFormal parameterINTEGERDefault
    START_LINELocal constant (array)LINE_ARRAY{1, 1}
    COEFFICIENTSLocal variable (array)LINE_ARRAYn.a.
  3. CALCULATE_COEF (level 2 function): Recur till appropriate line in triangle is arrived at. Call LINE_IN_TRIANGLE and self.
    NAMEUSAGETYPERANGE
    CURRENT_LINEFormal parameter (array)LINE_ARRAYn.a.
    LINE_LENGTHFormal parameterINTEGERDefault
    LINE_NUMBERFormal parameterINTEGERDefault
    NEXT_LINELocal variable (array)LINE_ARRAYDefault
  4. EXPANSION (Level 2 procedure): Using identified line in triangle output expansion of expressions
    NAMEUSAGETYPERANGE
    LINE_NFormal parameter (array)LINE_ARRAYn.a.
    POWERFormal parameterINTEGERDefault
  5. LINE_IN_TRIANGLE (Level 3 function): Calculate next line in triangle form current line and return next line.
    NAMEUSAGETYPERANGE
    OLD_LINEFormal parameter (array)LINE_ARRAYn.a.
    INDEXLocal variableINTEGERDefault
    NEW_LINELocal variable (array) LINE_ARRAYn.a.

Complete set of Nassi-Shneiderman charts for the above are given below:

NASSI_SHNEIDERMAN CHART

NASSI_SHNEIDERMAN CHART

A control flow chart indicating the flow of control through this sequence of procedures/functions is as follows.

CONTROL FLOW CHART

3.3. Implementation

-- PASCALS TRIANGLE
-- 25 SEPTEMBER 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO;
use TEXT_IO;

procedure PASCALS_TRIANGLE is
    package INTEGER_INOUT is new INTEGER_IO(INTEGER);
    use INTEGER_INOUT;
    POWER_N : INTEGER range 1..32;

    --------------------------------------------------------------------------
    -- CALCULATE LINE OF COEFFICIENTS IN TRIANGLE, recursive function to
    -- determine appropriate line in triangle
    function CALCULATE_COEF(CURRENT_LINE : LINE_ARRAY; LINE_LENGTH,
                LINE_NUMBER : INTEGER) return LINE_ARRAY is
        NEXT_LINE : LINE_ARRAY(1..LINE_LENGTH+1);
        ----------------------------------------------------------------------
        -- CALCULATE NEXT LINE IN TRIANGLE
        function LINE_IN_TRIANGLE(OLD_LINE : LINE_ARRAY) return
                    LINE_ARRAY is
            INDEX    : INTEGER := 1;
            NEW_LINE : LINE_ARRAY(1..OLD_LINE'LENGTH+1) ;
        begin
            NEW_LINE(INDEX) := 1;
        -- Loop through previous line in triangle
            loop
                INDEX := INDEX + 1;
                NEW_LINE(INDEX) := OLD_LINE(INDEX-1) + OLD_LINE(INDEX);
                exit when OLD_LINE(INDEX) = 1;
            end loop;
            NEW_LINE(INDEX+1) := 1;
            RETURN(NEW_LINE);
        end LINE_IN_TRIANGLE ;
        ----------------------------------------------------------------------
    begin
        -- Base case
        if LINE_NUMBER = 1 then
        RETURN(CURRENT_LINE);
        -- Recursive step
        else
        NEXT_LINE := LINE_IN_TRIANGLE(CURRENT_LINE);
        RETURN(CALCULATE_COEF(NEXT_LINE,LINE_LENGTH+1,LINE_NUMBER-1));
        end if;
    end CALCULATE_COEF;
    --------------------------------------------------------------------------

    --------------------------------------------------------------------------
    -- EXPANSION, output
    procedure EXPANSION(LINE_N: LINE_ARRAY; POWER: INTEGER) is
    begin
        PUT("1 + ");
        PUT(LINE_N(2), WIDTH=>1);
        PUT("x");
    -- Loop through coefficients in line in triangle
        for LOOP_PARAMETER in 2..POWER-1 loop
            PUT(" + ");
            PUT(LINE_N(LOOP_PARAMETER+1), WIDTH=>1);
            PUT("x^");
            PUT(LOOP_PARAMETER, WIDTH=>1);
        end loop;
        PUT(" + x^");
        PUT(POWER,WIDTH=>1);
        NEW_LINE;
    end EXPANSION;
    ------------------------------------------------------------------------------

    ------------------------------------------------------------------------------
    -- PASC. TRIANG. - Commence expansion
    procedure PASC_TRIANG(POWER_N : INTEGER) is
        type LINE_ARRAY is array (INTEGER range <>)
                of INTEGER;
        START_LINE   : constant LINE_ARRAY(1..2) := (1, 1);
        COEFFICIENTS : LINE_ARRAY(1..POWER_N+1);
    begin
        COEFFICIENTS :=CALCULATE_COEF(START_LINE,2,POWER_N);
        EXPANSION(COEFFICIENTS,POWER_N);
    end PASC_TRIANG;
    ------------------------------------------------------------------------------

begin
-- Get input
    PUT_LINE("Input power (integer 1..32): ");
    GET(POWER_N);
-- If power 1 direct output
    if POWER_N = 1 then
    PUT_LINE("1 + x");
-- Otherwise determine coefficients and then output expansion
    else
    PASC_TRIANG(POWER_N);
    end if;
end PASCALS_TRIANGLE;

3.4. Testing

TEST CASEEXPECTED RESULT
PWER_NOUTPUT
0CONSTRAINT_ERROR
11 + x
21 + 2x + x^2
16Expansion
31Expansion
32Expansion
33CONSTRAINT_ERROR

2.4.1. Black Box Testing

BVA, Limit and Arithmetic Testing Test input variable POWER_N using BVA analysis and limit testing. Test operation of arithmetic expression using (in this case) a positive sample value. A suitable set of test cases given in the table to the right.

2.4.2. White Box Testing

Path Testing: We should test each path through the code. The flow chart presented previously indicates 3 paths through the code (if we include the recursion). An appropriate pair of test cases is given in the following table, both of which have already been run as part of black box testing discussed above.

TEST CASEEXPECTED RESULT
POWER_NOUTPUT
11 + x
21 + 2x + x^2

Loop Testing: We have three loops in the code (including the recursion). Loop testing theory dictates the following (where M is the maximum number of allowable passes):

  1. Skip loop entirely
  2. Only one pass through the loop
  3. Two passes through the loop
  4. M passes through the loop where M < N
  5. N-1, N, N+1 passes through the loop

Test cases for the recursion, LINE_IN_TRIANGLE loop and EXPANSION loops are given in the following tables. Note that with respect to the recursion and the LINE_IN_TRIANGLE loop the loop is always exercised at least once.

TEST CASEDESCRIPTIONEXPECTED RESULT
POWER_N.OUTPUT
2Only one pass1 + 2x + x^2
3Two passes through the loop1 + 3x + 3x^2 + x^3
10M passes through the loop where M < NExpansion
31N-1 passes through the loopExpansion
32N passes through the loopExpansion
33N+1 passes through the loopCONSTRAINT_ERROR

TEST CASEDESCRIPTIONEXPECTED RESULT
POWER_N.OUTPUT
2Only one pass1 + 2x + x^2
3Two passes through the loop1 + 3x + 3x^2 + x^3
10M passes through the loop where M < NExpansion
30N-1 passes through the loopExpansion
31N passes through the loopExpansion
32N+1 passes through the loopExpansion

TEST CASEDESCRIPTIONEXPECTED RESULT
POWER_N.OUTPUT
2Only one pass1 + 2x + x^2
3Two passes through the loop1 + 3x + 3x^2 + x^3
4Two passes through the loop1 + 4x + 6x^2 + 4x^3 + x^4
10M passes through the loop where M < NExpansion
30N-1, passes through the loopExpansion/TD>
31N, passes through the loopExpansion
32N+1 passes through the loopExpansion

Example Problem Pascal's Triangle.




Created and maintained by Frans Coenen. Last updated 11 October 1999