Subject: Intrinsics

From: Rob Schreiber <[email-masked]>
Date: Mon, 30 Nov 92 14:57:33 PST

Dave,

I dont object to any of your edits.
I cannot make the last table fit the page.   So to hell with the 
"Overfull hbox".

I have added text to deal with inquiry about an unallocated array
(copied from the F90 SIZE intrinsic).  I also clarified the type and shape
of the results returned by the mapping inquiries HPF_ALIGNMENT, etc..

%
%intrinsics.tex

%I have made a number of small edits in the intrinsics chapter,
%primarily to eliminate many messages of the form:
%
%Overfull \hbox (13.32715pt too wide) in paragraph at lines 1438--1441 . . . .
%
%Could you please use this edited version as your base for future edits.
% If you disagree with any of the edits I made, please fix them, while
%attempting to not introduce new LaTeX messages.  I can't think of a
%good way to make the next to the last table fit.  Any ideas?
%
%-David
%
%====================================================================
%Version of May 29, 1992 --- Guy Steele, Thinking Machines Corporation
%and David Loveman, Digital Equipment Corporation --- Robert Schreiber,
RIACS (Editor)

\chapter{Intrinsic and Library Functions}
\label{intrinsics}

\footnote{Version of October 27, 1992 ---
Guy Steele, Thinking Machines Corporation, David Loveman, Digital
Equipment Corporation, and
Robert Schreiber, Research Institute for Advanced Computer Science
}
This section extends Section 13 of the Fortran 90 standard.

HPF retains Fortran 90's intrinsic functions.  It also adds a number of
new intrinsics in three categories: system inquiry intrinsics, mapping
inquiry intrinsics, and computational intrinsics.

The definitions of two Fortran 90 intrinsics, MAXLOC and MINLOC,
are extended by the addition of an optional DIM argument.

In addition to the new intrinsics, HPF defines an HPF library that must be
provided by vendors of any full HPF implementation.

\section{System Inquiry Intrinsic Functions}

\footnote{Version of
October 27, 1992 --- David Loveman, Digital Equipment Corporation.}
In addition to the Fortran 90 intrinsic functions, High Performance
Fortran has two system inquiry  intrinsic functions:
NUMBER_OF_PROCESSORS and PROCESSORS_SHAPE.  Their values remain
constant for (at least) the duration of one program execution.
Accordingly, NUMBER_OF_PROCESSORS and PROCESSORS_SHAPE have values that
are restricted expressions and may be used wherever any other Fortran
90 restricted expression may be used.
%    If the system configuration is committed to at compile time,
%    NUMBER_OF_PROCESSORS and PROCESSORS_SHAPE have values that are constant
%    expressions and may be used wherever any other Fortran 90 constant
%    expression may be used.  
In particular, NUMBER_OF_PROCESSORS may be
used in a specification expression.
%    and, if a constant expression, may
%    be used in an initialization expression.  
None of the categories of
intrinsic functions listed in Chapter 13 of the Fortran 90 standard
seem quite apt to describe the nature of this new intrinsic function,
so HPF adds a new category of ``system inquiry functions'' and place
NUMBER_OF_PROCESSORS and PROCESSORS_SHAPE in that category.

%    Note that treating these intrinsics as constant expressions does not
%    force a compiler to bind the number of processors at compile time
%    (although that is one possible implementation) -- with the right linker
%    or code-generation technology the choice could be deferred until run
%    time, possibly at some performance cost.

\subsection{Formal Definition}

%[this subsection contains formal definition material, phrased as additions
%or modifications to the Fortran 90 specification, with non-formal
%commentary in square brackets]

\par\smallskip
13.8a System inquiry functions

In a multi-processor implementation, the processors may be arranged in
an im\-ple\-men\-ta\-tion-de\-pen\-dent n-dimensional processor array.
The system inquiry functions return values related to this underlying
machine and processor configuration, including the size and shape of
the underlying processor array.  NUMBER_OF_PROCESSORS returns the total
number of processors available to the program or the number of
processors available to the program along a specified dimension of the
processor array.  PROCESSORS_SHAPE returns the shape of the processor
array.

\par\smallskip
13.10.21 System inquiry functions

\begin{verbatim}
   NUMBER_OF_PROCESSORS(DIM)      Total number of processors in
                                    the processor array.
   PROCESSORS_SHAPE()             Shape of the processor array
\end{verbatim}

\par\smallskip
13.13.xx  NUMBER_OF_PROCESSORS(DIM)

Optional Argument.  DIM

Description.  Returns the total number of processors available to the
program or the number of processors available to the program along a
specified dimension of the processor array.

Class.  System inquiry function.

Arguments.
DIM (optional)  must be scalar and of type integer with a value in the
range  (\(1 \leq DIM \leq n\)) where n is the rank of the processor array.)

Result Type, Type Parameter, and Shape.  Default integer scalar.

Result Value.  The result has a value equal to the extent of dimension
DIM (\(1 \leq DIM \leq n\)), where n is the rank of the processor array) of the
processor-dependent hardware processor array or, if DIM is absent, the
total number of elements, equal to or greater than one, of the
processor-dependent hardware processor array.

\par\smallskip\noindent
{\bf Examples:} For a DECmpp 12000/Sx Model 200 with 8192 processors,
the value of NUMBER_OF_PROCESSORS( ) is 8192, the value of
NUMBER_OF_PROCESSORS(DIM=1) is 128, and the value of
NUMBER_OF_PROCESSORS(DIM=2) is 64.  For a single processor DEC 3000 AXP
workstation, the value of NUMBER_OF_PROCESSORS( ) is 1, and the value
of NUMBER_OF_PROCESSORS(DIM=1) is 1.

\par\smallskip
13.13.yy PROCESSORS_SHAPE()

Description.  Returns the shape of the implementation-dependent processor array.

Class.  System inquiry function.

Arguments.  None

Result Type, Type Parameter, and Shape.  The result is a default
integer array of rank one whose size is equal to the rank of the
implementation-dependent processor array.

Result Value.  The value of the result is the shape of the
implementation-dependent processor array.

\par\smallskip\noindent
{\bf Example:} For a DECmpp 12000/Sx Model 200 with 8192 processors,
the value of PROCESSORS_SHAPE() is (/ 128, 64 /).  For a Connection
Machine CM-2 with 8192 processors, the value of PROCESSORS_SHAPE()
might be (/ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 /).  For a Connection
Machine CM-5 with 8192 processors, the value of PROCESSORS_SHAPE()
might be (/ 8192 /).  For a single processor DEC 3000 AXP workstation,
the value of PROCESSORS_SHAPE() is (/ 1 /).

%[The list of alternatives for Fortran 90 constant expressions and
%restricted expression are expanded to include NUMBER_OF_PROCESSORS and
%PROCESSORS_SHAPE.]

%    7.1.6.1 Constant expression
%
%    A constant expression is . . . . .
%
%    (6a)  A system inquiry function reference where each argument is a
%    constant expression and the compiler has been informed of the
%    appropriate system configuration.  
%
%    . . . . .
%
7.1.6.2 Specification expression

A restricted expression is . . . . .

(9a)  A system inquiry function reference where each argument is a
restricted expression.  

. . . . .

\subsection{Discussion and Pragmatic Usage --- Consequences of the Formal Proposal}

%    The shape of the processor array may be treated as a constant known at
%    compile time if the compiler is informed about the system
%    configuration.  This will allow the values of system inquiry functions
%    to be used in initialization expressions
%    even if the system configuration is not
%    committed to at compile time.
The values of system inquiry
functions are always restricted expressions;  thus they may be used in
specification expressions.
They may not, however, occur in initialization expressions, because they
may not be assumed to be constants.   In particular, HPF programs may
be compiled to run on machines whose configurations are not known at
compile time.

Note that the system inquiry functions query the physical machine, and
have nothing to do with any PROCESSORS directive that may occur.

References to system inquiry functions may occur in HPF directives, as in:
                                                                  \CODE
!HPF$ TEMPLATE T(100, 3*NUMBER_OF_PROCESSORS())
                                                                  \EDOC

The definition of NUMBER_OF_PROCESSORS is modeled on the definition of
the SIZE intrinsic function.

The definition of PROCESSORS_SHAPE is modeled on the definition of the
SHAPE intrinsic function.

The rank of the processor array is returned by
								\CODE
SIZE(PROCESSORS_SHAPE())
								\EDOC
an expression that may occur in any specification expression.
%and that is constant whenever PROCESSORS_SHAPE() is.

%  PART DELETED AS INCORRECT
%    As a result of being a constant expression, if the system configuration
%    is committed to at compile time, suitably constrained references to
%    system inquiry functions may occur in initialization expressions as,
%    for example, initialization values in type-declaration-statements or in
%    parameter-statements, as in:
%
%                                                                      \CODE
%    PARAMETER (N_PROCS=NUMBER_OF_PROCESSORS(),        &
%               NXPROCS=NUMBER_OF_PROCESSORS(DIM=1),   &
%               NYPROCS=NUMBER_OF_PROCESSORS(DIM=2))
%                                                                      \EDOC
%    
As a result of being a restricted expression, suitably constrained
references to system inquiry functions may occur in specification
expressions as, for example, lower or upper bounds of an
explicit-shape-spec of an array-spec in type-declaration-statements, as in:

                                                                 \CODE
INTEGER, DIMENSION(SIZE(PROCESSORS_SHAPE())) :: PS
PS = PROCESSORS_SHAPE()
! PS(2) = NUMBER_OF_PROCESSORS(DIM=2)
                                                                 \EDOC

\section{Computational Intrinsic Functions}

\footnote{Version of
October 27, 1992 --- Guy Steele, Thinking Machines Corporation.}
This section extends the set of Fortran intrinsic functions.

\subsection{Extension to MINLOC and MAXLOC}

The MAXLOC and MINLOC intrinsics are redefined to have an optional DIM
argument that works exactly as does the DIM argument of MAXVAL.  If
such an argument is present, then the shape of the result equals the
shape of the first argument with one dimension (the one indicated by
the DIM argument) deleted; it is as if a series of one-dimensional
MAXLOC or MINLOC operations were performed.  The rank of the result is
one less than the rank of the first argument.  If the smallest (MINLOC)
or largest (MAXLOC) element along a given dimension is not unique, then
the location of the first one is returned.   The declared lower bounds
of the input array play no role in determining the output.   The
optional MASK argument is retained and may be used together with the
DIM argument.

Note that the behavior of MAXLOC and MINLOC without the DIM argument is
quite different.   In this case, a one-dimensional integer array of
size equal to the rank of ARRAY is returned, giving the ssubscripts of
the first element in array element order with the smallest (MINLOC) or
largest (MAXLOC) value.

Thus, if A has DIMENSION(4,3), then 
\begin{verbatim}
      SHAPE(MAXLOC(A))       has the value (/ 2 /)
      SHAPE(MAXLOC(A,DIM=1)) has the value (/ 3 /)
      SHAPE(MAXLOC(A,DIM=2)) has the value (/ 4 /).
\end{verbatim}
\par\smallskip\noindent
{\bf Example:} If A has the value

\begin{verbatim}
        [  0  -5   8  -3  ]
        [  3   4  -1   2  ]
        [  0   4   6  -4  ]

then	

        MINLOC(A)        has the value (/ 1, 2 /)
        MAXLOC(A)        has the value (/ 1, 3 /)
        MINLOC(A, DIM=1) has the value (/ 1, 1, 2, 3 /)
        MAXLOC(A, DIM=1) has the value (/ 2, 2, 1, 2 /)
        MINLOC(A, DIM=2) has the value (/ 2, 3, 4 /)
        MAXLOC(A, DIM=2) has the value (/ 3, 2, 3 /).
\end{verbatim}

\subsection{ILEN}

An elemental integer-length intrinsic.  Its action on a scalar is:

                                                                \CODE
  ILEN(X) = ceiling(log2( IF x < 0 THEN -x ELSE x+1 ))
                                                                \EDOC

ILEN(x) is the number of bits
required to store a 2's-complement signed integer x.  As examples of
its use,  2**ILEN(N-1)  rounds N up to a power of 2 (for \(N > 0\)),
whereas  2**(ILEN(N)-1)  rounds N down to a power of 2.

Note that a given integer value will always produce the same result
from ILEN, independent on the number of bits in the representation of
the integer.  That is because bits are counted from the right (the
least significant bit).

{\bf Argument:}:  X must be integer.  It may be scalar or array valued.

{\bf Result shape and type:}  Same as X.

As an elemental, integer-valued intrinsic, ILEN may appear in a
specification expression.

\section{Computational Library Functions}

\footnote{Version of
September 14, 1992 --- Guy Steele, Thinking Machines Corporation
Robert Schreiber, Research Institute for Advanced Computer Science,
and Rex Page, Amoco Corporation.}
This section consists of five groups of computational library
functions, to be available in the standard HPF module.   Use of these
functions must be accompanied by an appropriate USE statement in each
scoping unit in which they are used.   They are not intrinsic.   Thus,
they are not allowed in specification expressions.

\subsection{New Reduction Functions}

Just as Fortran 90 has the correpondences:

\begin{verbatim}
          operator/intrinsic        reduction intrinsic

                +                       SUM, COUNT
                *                       PRODUCT
                .AND.                   ALL
                .OR.                    ANY
                MAX                     MAXVAL
                MIN                     MINVAL
\end{verbatim}
\noindent
it is useful to have reduction versions of certain other operators and
intrinsics in the language that happen to be associative and
commutative.   Therefore the new functions AND, OR, EOR, and PARITY
are defined.

\begin{verbatim}
          operator/intrinsic        reduction function

                IAND                    AND
                IOR                     OR
                IEOR                    EOR
               .NEQV.                   PARITY

Thus

        AND( (/ 7,3,10 /) )  yields 2
         OR( (/ 7,3,10 /) )  yields 15
        EOR( (/ 7,3,10 /) )  yields 14

      LOGICAL T,F
      PARAMETER (T = .TRUE., F = .FALSE. )      !just for conciseness

      PARITY( (/ T,F,F,T,T,F,F,F,T,T /) )  yields .TRUE.
      PARITY( (/ T,F,F,T,T,F,F,F,T,F /) )  yields .FALSE.
\end{verbatim}

Some of these are particularly valuable when used with the corresponding
parallel-prefix functions, Section~\ref{parallel-prefix}.

The identity element for the reduction PARITY is .FALSE., for the 
reductions OR and EOR is zero, and for the 
reduction AND is -1.  COUNT does not have an identity, as it maps
logicals to integers and returns zero if there are no true values to be counted.
The identities for the other reductions are defined in the Fortran 90 standard.

\subsection{Combining-Scatter Functions}

%[Note the addition of COPY_SCATTER, by analogy with COPY_PREFIX and
%COPY_SUFFIX, to achieve what the Connection Machine calls
%send-with-overwrite.]

%Combining-Send section revised to make these functions
%rather than subroutines --- Rex Page 26Aug92

%Changed to SCATTER, removed restrictions on order of operations, and
%added optional final MASK argument
%RSS, Sept 14.

For every reduction operation XXX in the language, introduce a new
function:

                                                                \CODE
   XXX_SCATTER(SOURCE,BASE,IDX1,..., IDXn, MASK)
                                                                \EDOC

The IDX arguments are integer arrays.
The number of IDX arguments must equal the rank of BASE.  
The SOURCE and all the IDX arguments must be conformable.  
The result delivered by the function is conformable with BASE.   
The types of SOURCE and BASE must be the
same (exception: COUNT), and the result has the type of BASE.   
The allowed types are:

\begin{verbatim}
                XXX                     Allowed Types

                SUM                     Real, Complex, Integer
                COUNT                   BASE = Integer, SOURCE = Logical
                PRODUCT                 Real, Complex, Integer
                MAXVAL                  Real, Integer
                MINVAL                  Real, Integer
                AND                     Integer
                OR                      Integer
                EOR                     Integer
                ALL                     Logical
                ANY                     Logical
                PARITY                  Logical

\end{verbatim}

Since SOURCE and all the IDX arrays are conformable, for every
element s in SOURCE there is a corresponding element in each of
the IDX arrays.
Let i1 be the value of the element of
IDX1 that is indexed by the same subscripts as element s of SOURCE.
More generally, for each j=1,2,...,n,
let ij be the value of the element of IDXj that corresponds to
element s in SOURCE, where n is the rank of BASE. 
The integers ij, j=1,...,n, form a subscript selecting an element
of BASE:  BASE(i1,i2,...,in).

Thus SOURCE and the IDX arrays establish a mapping from all the
elements of SOURCE onto selected elements of BASE.
Viewed in the other direction, this mapping associates
with each element b of BASE a set S of elements from SOURCE.

Since BASE and the result of XXX_SCATTER are conformable,
there is a corresponding element of the result for each 
element of BASE.

If S is empty, then the element of the result corresponding to the
element  b  of BASE has the same value as  b.

If S is non-empty, then the elements of  S  will be combined with
element  b to produce an element of the result.
Let the elements of  S  be  s1, ..., sm.
Let @ denote an infix form of operation XXX.
The element of the result corresponding to the
element  b  of BASE is the result of evaluating
                                                                \CODE
       s1 @ s2 @ ... @ sm @ b
                                                                \EDOC
\noindent
or any mathematically equivalent expression (as defined in Section
7.1.7.3 of the Fortran 90 standard [ISO/IEC 1539:1991(E)]).

Thus the order of operations is arbitrary, and may differ on two
otherwise identical runs of the same HPF program.  This matters when
the combining operation is not both associative and commutative, for
example floating-point addition.  In fact, because machine arithmetic
is not associative (not even  fixed-point, because of overflow) the
programmer must be sure that the nondeterministic order of evaluation
of the result will not produce undesirable effects.

If the optional argument MASK is present, then only the elements of
SOURCE in positions for which MASK is true participate in the operation.
All other elements of SOURCE and of the IDX arrays are ignored.

Thus the result of the expression
                                                                \CODE
      SUM_SCATTER(SOURCE,BASE,IDX1,IDX2,...,IDXn,MASK)
                                                                \EDOC
\noindent
{\em could} be computed as

                                                                \CODE
      result = BASE
      DO J1=LBOUND(SOURCE,1),UBOUND(SOURCE,1)
        DO J2=LBOUND(SOURCE,2),UBOUND(SOURCE,2)
          ...
            DO Jk=LBOUND(SOURCE,k),UBOUND(SOURCE,k)
              IF (MASK(J1,J2,...,Jk))
     &           result(IDX1(J1,J2,...,Jk),
     &                  IDX2(J1,J2,...,Jk),
     &                  ...
     &                  IDXn(J1,J2,...,Jk)) =
     &           result(IDX1(J1,J2,...,Jk),
     &                  IDX2(J1,J2,...,Jk),
     &                  ...
     &                  IDXn(J1,J2,...,Jk)) + SOURCE(J1,J2,...,Jk)
            END DO
          ...
        END DO
      END DO
                                                                \EDOC

\noindent
where k is the rank of SOURCE.  (However, this nest of DO loops makes a
greater commitment to the particular order in which the combining
operations are carried out than the order---namely, none!--- guaranteed
by the XXX_SCATTER function.)

In addition, COPY_SCATTER is the combining-send
function generated by the (noncommutative) binary operator

                                                                \CODE
      COPY_operation(x,y) = x
                                                                \EDOC
\noindent
Thus an element of the result delivered by
COPY_SCATTER(SOURCE,BASE,IDX1,..., IDXn) corresponding with an element
of BASE that is associated with a non-empty set from SOURCE has
the same value as {\em some} SOURCE element from that set.
So if multiple elements of SOURCE are sent to the same result
element, some one of them will be assigned and the rest, as well as
the corresponding element of BASE, will be effectively discarded.
\par\smallskip\noindent
{\bf Example:} 

                                                                \CODE
      A = (/ 10., 20., 30., 40., -10./)
      X = (/ 1.,  2.,  3.,  4./)
      V = (/ 3,   2,   2,   1,   1/)
      X = SUM_SCATTER(A,X,V, MASK=(A > 0) )
                                                                \EDOC
\noindent
yields the result X = (/41., 52., 13., 4./).

If all elements of V were distinct, one could write this in
Fortran 90 as

                                                                \CODE
      X(V) = X(V) + MERGE(A, 0., A > 0.)
                                                                \EDOC

The proposed function SUM_SCATTER ``works'' even if V contains
duplicate values.  Note that the two-dimensional case

                                                                \CODE
      X(V,W) = X(V,W) + B
                                                                \EDOC

\noindent
must be rendered using SPREAD:

                                                                \CODE
      X = SUM_SCATTER(B,X,SPREAD(V,DIM=2,NCOPIES=SIZE(X,2)),
     &                      SPREAD(W,DIM=1,NCOPIES=SIZE(X,1)))
                                                                \EDOC

\noindent
in order to duplicate the cross-product effect of ordinary array
subscripting.  
(This definition of XXX_SCATTER does {\em not} perform such a cross
product of indices because it is more general and in practice more
useful without the cross-product effect built in.)

When scatter along one or more axes of a multidimensional array is required,
use a surrounding FORALL.  For example, the idiom used 
to SUM_SCATTER the (j,k) planes
of an (i,j,k)-indexed three-dimensional array, using the one-dimensional
index vector V is

								\CODE
  REAL, ARRAY(NI, NJ, NK) :: SRC, DEST
  LOGICAL MASK(NI, NJ, NK)
  INTEGER V(NI)
  FORALL (J = 1:NJ, K = 1:NK)
&      DEST(:, J, K) = SUM_SCATTER( SRC(:,J,K), DEST(:,J,K), 
&           V, MASK(:, J, K))
								\EDOC
which has the same effect as
								\CODE
  DO I = 1, NI
     WHERE (MASK(I, :, :)
&       DEST(V(I), :, :) = DEST(V(I), :, :) + SRC(I, :, :)
  ENDDO
								\EDOC

\noindent
but may be more efficient, and 
makes no guarantees as to the order of evaluation.

\subsection{Parallel Prefix Instrinsics}
\label{parallel-prefix}

For every reduction operation XXX in the language, introduce the two new
functions XXX_PREFIX and XXX_SUFFIX.  They take the same arguments
as the corresponding reduction intrinsic, 
(an array of appropriate type, an optional scalar integer DIM argument,
an optional, LOGICAL array argument MASK conformable with ARRAY)
plus two additional optional arguments:

                                                                \CODE
XXX_PREFIX(ARRAY, DIM, MASK, SEGMENT, EXCLUSIVE)
XXX_SUFFIX(ARRAY, DIM, MASK, SEGMENT, EXCLUSIVE)
                                                                \EDOC
Each element of the result is the reduction under the operator XXX of a
(possibly empty) set of elements of ARRAY.

\par\smallskip\noindent
{\bf Example:}
                                                                \CODE
     MAXVAL_PREFIX((/ 3, 2, 4, 1, 6/))
is
                   (/ 3, 3, 4, 4, 6/).

     MAXVAL_SUFFIX((/ 3, 2, 4, 1, 6/))
is
                   (/ 6, 6, 6, 6, 6/).
                                                                \EDOC

The value of these functions is conformable with ARRAY.

The result has the same type as ARRAY, except
COUNT_PREFIX and COUNT_SUF\-FIX, which take a LOGICAL
array argument and return an integer array result.
The allowed operations and the corresponding allowed types for ARRAY are
given in the table below.
\begin{verbatim}
                XXX                     Allowed Types

                SUM                     Real, Complex, Integer
                COUNT                   Result = Integer, ARRAY = Logical
                PRODUCT                 Real, Complex, Integer
                MAXVAL                  Real, Integer
                MINVAL                  Real, Integer
                AND                     Integer
                OR                      Integer
                EOR                     Integer
                ALL                     Logical
                ANY                     Logical
                PARITY                  Logical
\end{verbatim}

If the DIM argument is omitted, then the arrays are processed in array
element order (``column-major''), as if temporarily regarded as
one-dimensional.  If it is present, then it must be an integer scalar
between one and the rank of ARRAY.   In this case, completely
independent prefix or suffix operations occur along the selected
dimension of ARRAY.

\par\smallskip\noindent
{\bf Example:} If A has the value

\begin{verbatim}

        [  0  -5   8  -3  ]
        [  3   4  -1   2  ]
        [  0   4   6  -4  ]

then SUM_PREFIX(A) has the value 

        [  0  -2  14  16  ]
        [  3   2  13  18  ]
        [  3   6  19  14  ]

SUM_PREFIX(A, DIM=1) has the value

        [  0  -5   8  -3  ]
        [  3  -1   7  -1  ]
        [  3   3  13  -5  ]

SUM_PREFIX(A, DIM=2) has the value

        [  0  -5   3   0  ]
        [  3   7   6   8  ]
        [  0   4  10   6  ]

\end{verbatim}

Array elements corresponding to positions where the MASK is false
do not contribute to the running accumulation.  However, the result
is still defined for corresponding positions in the result.
\par\smallskip\noindent
{\bf Example:}
                                                                \CODE
     MAXVAL_PREFIX(    (/ 3, 2, 4, 5, 6/), 
   &           MASK  = (/ T, F, F, T, F/))
is                                                  
                       (/ 3, 3, 3, 5, 5/).

                                                                \EDOC
In actual practice, results may not be required in those positions;
in such cases the programmer may be able to use the WHERE statement
to inform the compiler:

                                                                \CODE
      WHERE (FOO) A=SUM_PREFIX(B,MASK=FOO)
                                                                \EDOC

The first additional optional argument is called SEGMENT, which is of
type logical and conformable with the ARRAY argument.  If present, the
array is divided into pieces corresponding to contiguous sequences of
true or false elements of SEGMENT.  The beginning of a piece is a place
where the running accumulation is to be reset before processing the
corresponding array element).

\par\smallskip\noindent
{\bf Example:}
                                                                \CODE
     LOGICAL T,F
     PARAMETER (T = .TRUE., F = .FALSE. )

     MAXVAL_PREFIX((/ 3, 2, 4, 1, 6/), 
   &       SEGMENT=(/ T, T, T, F, F/))  yields  (/ 3, 3, 4, 1, 6/).
                      -------  ----                -------  ----
                   two input segments         two independent results
                                                                \EDOC

The second additional optional argument, a scalar logical, is called
EXCLUSIVE, default .FALSE., which determines whether the prefix or
suffix operation is inclusive (the default) or exclusive.  (The
inclusive sum-prefix of (/ 1,2,3,4 /) is (/ 1,3,6,10 /) whereas the
exclusive sum-prefix is (/ 0,1,3,6 /).)

In every case, every element of the result has a value equal to the
reduction of certain selected elements of ARRAY, or an identity value
(zero for SUM_PREFIX or SUM_SUFFIX, for example) if no elements of
ARRAY are selected for that result element.  The optional arguments
affect the selection of elements of ARRAY for each element of the
result; the selected elements of ARRAY are said to contribute to the
result element.

The identity element for the reduction PARITY is .FALSE., for the
reductions OR and EOR is zero, and for the reduction AND is -1.  COUNT
does not have an identity, as it maps logicals to integers and returns
zero if there are no true values to be counted.  The identities for the
other reductions are defined in the Fortran 90 standard.

For any given element R of the result, let A be the corresponding
element of ARRAY.  Every element of ARRAY contributes to R unless
disqualified by one of the following rules.

For xxx_PREFIX, no element that follows A in the array element ordering
of ARRAY contributes to R.  For xxx_SUFFIX, no element that precedes A
in the array element ordering of ARRAY contributes to R.  This rule
applies even when the DIM argument is present, since array element
order increases with an increase in any component of an array element
index.

If the DIM argument is provided, an element Z of ARRAY does not
contribute to R unless all its indices, excepting only the index for
dimension DIM, are the same as the corresponding indices of A.

If the MASK argument is provided, an element Z of ARRAY does not
contribute to R if the element of MASK corresponding to Z is false.

If the SEGMENT argument is provided, an element Z of ARRAY does not
contribute unless the elements B and Y of SEGMENT corresponding to A
and Z (respectively), and the intervening elements of SEGMENT as well,
all have the same value.  If the DIM argument is not present, then the
``intervening'' elements are all elements between them in array element
order; if the DIM argument is present, then the ``intervening''
elements are those having indices the same as those of both B and Y,
except the index for dimension DIM, which must be between (and possibly
equalling) the indices of B and Y for dimension DIM.  In other words,
the prefix or suffix operation is performed on groups of elements of
ARRAY, where a group corresponds to a maximal contiguous run of
like-valued elements of SEGMENT.

If the SEGMENT argument is omitted, then the result is computed using a
default SEGMENT all elements of which are true.   Thus, without the DIM
argument, there is exactly one group, while if DIM is present, there is
one group for each valid set of indices of ARRAY other than the index
selected by DIM.

If the EXCLUSIVE argument is provided and is true, then A itself does
not contribute to R.

In addition to all this, the operation COPY_PREFIX replicates the first
(lowest-indexed) element of each segment throughout the segment, and
the operation COPY_SUF\-FIX replicates the last (highest-indexed)
element of each segment throughout the segment.

\par\smallskip\noindent
{\bf Examples:}

                                                                \CODE
SUM_PREFIX( (/1,3,5,7/) ) yields (/1,4,9,16/)
SUM_SUFFIX( (/1,3,5,7/) ) yields (/16,15,12,7/)

LOGICAL T,F
PARAMETER (T = .TRUE., F = .FALSE. )

COUNT_PREFIX( (/T,F,F,T,T,T,F,T,F/) )              
                                 !yields (/1,1,1,2,3,4,4,5,5/)
COUNT_PREFIX( (/T,F,F,T,T,T,F,T,F/), EXCLUSIVE=T ) 
                                 !yields (/0,1,1,1,2,3,4,4,5/)

  SUM_PREFIX( (/1,2,3,4,5,6,7,8,9/),
&   SEGMENT=(/T,T,T,T,F,F,T,F,F/)) yields (/1,3,6,10,5,11,7,8,17/)
              ------- --- - ---             -------- ---  - ----
	     four input segments       four independent result segments

  COPY_PREFIX( (/1,2,3,4,5,6,7,8,9/),
&    SEGMENT=(/T,T,T,T,F,F,T,F,F/)) yields (/1,1,1,1,5,5,7,8,8/)
               ------- --- - ---             ------- --- - ---
	      four input segments       four independent result segments
                                                                \EDOC

A new segment begins at every
{\em transition} from false to true or true to false; thus a segment is
indicated by a maximal contiguous subsequence of like logical values:

                                                                \CODE
(/T,T,T,F,T,F,F,F,T,F,F,T/)
  ----- - - ----- - --- -    seven segments
                                                                \EDOC

Note: Connection Machine software delimits the segments by indicating
the {\em start} of each segment.  Cray MPP Fortran delimits the
segments by indicating the {\em stop} of each segment.  Each method has
its advantages.  There is also the question of whether this convention
should change when performing a suffix rather than a prefix.  HPF
adopts the symmetric representation above.  The main advantages of this
representation are:

(a) It is symmetrical, in that the same segment specifier may
    be meaningfully used for parallel prefix and parallel suffix
    without changing its interpretation (start versus stop).

(b) It seems to be equally inconvenient for every existing
    architecture!  However, it is not that hard to accommodate.

(c) The start-bit or stop-bit representation is easily converted
    to this form by using PARITY_PREFIX or PARITY_SUFFIX.

\par\smallskip\noindent
{\bf Examples:}

                                                                \CODE
SUM_PREFIX(FOO,SEGMENT=PARITY_PREFIX(START_BITS))
SUM_PREFIX(FOO,SEGMENT=PARITY_SUFFIX(STOP_BITS))
SUM_SUFFIX(FOO,SEGMENT=PARITY_SUFFIX(START_BITS))
SUM_SUFFIX(FOO,SEGMENT=PARITY_PREFIX(STOP_BITS))
                                                                \EDOC
\noindent
These might be standard idioms for a compiler to recognize.

\subsection{Sorting Functions}

This section introduces two sorting functions, GRADE_UP and GRADE_DOWN.
                                                                \CODE
GRADE_UP(ARRAY,DIM)
                                                                \EDOC

The array may be of type integer, real, or character.

If the optional DIM argument is present, then the result has the same
shape as the ARRAY.  Suppose DIM has the value k; then the result R
has the property that if one computes the array

                                                                \CODE
B(i1,i2,...,ik,...in)=ARRAY(i1,i2,...,R(i1,i2,...,ik,...,in),...,in)
                                                                \EDOC

\noindent
then for all i1,i2,...,(omit ik),...,in, the vector B(i1,i2,...,:,...,in) is
sorted in ascending order; moreover, R(i1,i2,...,:,...,in) is a permutation of
all the integers in the range 

                                                                \CODE
LBOUND(ARRAY,k):UBOUND(ARRAY,k). 
                                                                \EDOC

The sort is
stable; that is, if j \(\leq\) m and B(i1,i2,...,j,...,in) .EQ.
B(i1,i2,...,m,...,in),
then R(i1,i2,...,j,...,in) \(\leq\) R(i1,i2,...,m,...,in).

If the optional DIM argument is absent, then the result S is an
array of rank 2, with shape (/ SIZE(SHAPE(ARRAY)), PRODUCT(SHAPE(ARRAY)) /)
and the property that if one computes the rank-1 array

                                                                \CODE
B(k)=ARRAY(S(1,k),S(2,k),...,S(n,k))
                                                                \EDOC

\noindent
where n=SIZE(SHAPE(ARRAY)), then B is sorted in ascending order;
moreover, all of the columns of S are distinct, that is, if j \(\neq\) m then
ALL(S(:,j) .EQ. S(:,m)) will be false.  The sort is stable;
if j \(\leq\) m and B(j) .EQ. B(m), then ARRAY(S(1,j),S(2,j),...,S(n,j))
precedes ARRAY(S(1,m),S(2,m),...,S(n,m)) in the array element ordering
of ARRAY.

                                                                \CODE
GRADE_DOWN(ARRAY,DIM)
                                                                \EDOC

The array may be of type integer, real, or character.

If the optional DIM argument is present, then the result has the same
shape as the ARRAY.  Suppose DIM has the value k; then the result R
has the property that if one computes the array

                                                                \CODE
B(i1,i2,...,ik,...in)=ARRAY(i1,i2,...,R(i1,i2,...,ik,...,in),...,in)
                                                                \EDOC

\noindent
then for all i1,i2,...,(omit ik),...,in, the vector B(i1,i2,...,:,...,in) is
sorted in descending order; moreover, R(i1,i2,...,:,...,in) is a permutation of
all the integers in the range 

                                                                \CODE
LBOUND(ARRAY,k):UBOUND(ARRAY,k).  
                                                                \EDOC

The sort is
stable; that is, if j \(\leq\) m and B(i1,i2,...,j,...,in) .EQ.
B(i1,i2,...,m,...,in),
then R(i1,i2,...,j,...,in) \(\leq\) R(i1,i2,...,m,...,in).  (Yes, that
last ``\(\leq\)'' sign
really should be a ``\(\leq\)'', not a ``\(\geq\)''.)

If the optional DIM argument is absent, then the result S is an
array of rank 2, with shape (/ SIZE(SHAPE(ARRAY)), PRODUCT(SHAPE(ARRAY)) /)
and the property that if one computes the rank-1 array

                                                                \CODE
B(k)=ARRAY(S(1,k),S(2,k),...,S(n,k))
                                                                \EDOC

\noindent
where n=SIZE(SHAPE(ARRAY)), then B is sorted in descending order;
moreover, all of the 
columns of S are distinct, that is, if j \(\neq\) m then ALL(S(:,j)
.EQ. S(:,m)) will
be false.  The sort is stable; if j \(\leq\) m and B(j) .EQ. B(m), then
ARRAY(S(1,j),S(2,j),...,S(n,j)) precedes (yes, ``precedes'', not ``follows'')
ARRAY(S(1,m),S(2,m),...,S(n,m)) in the array element ordering of ARRAY.

Because of the stability requirement, GRADE_DOWN(A(1:N)) does not, in
general, equal GRADE_UP(A(N:1:-1)).  Indeed, these results are equal if
and only if A contains no duplicate values.

The stability requirement allows one to cascade grading operations in order to
sort on multiple fields.  For example, suppose one had the following derived
type (example taken from section 4.4.1 of the Fortran 90 standard):

                                                                \CODE
TYPE PERSON
  INTEGER AGE
  CHARACTER (LEN = 50) NAME
END TYPE PERSON
                                                                \EDOC

Now consider two arrays of persons:

                                                                \CODE
TYPE(PERSON), DIMENSION(100000) :: MEMBERS, ROSTER
                                                                \EDOC

Also assume a work vector for indices:

                                                                \CODE
INTEGER, DIMENSION(100000) :: V
                                                                \EDOC

Then the statements

                                                                \CODE
V = GRADE_UP(MEMBERS%AGE)
V = V(GRADE_UP(MEMBERS(V)%NAME))
ROSTER = MEMBERS(V)
                                                                \EDOC

\noindent
cause ROSTER to be a rearrangement of MEMBERS that is sorted
primarily by name and secondarily by age (that is, members with
the same name are grouped together in order of ascending age).
Note that the minor sort field is graded first, and that more
statements like the second one may be inserted to sort on additional
fields.

To list members with the same name in descending order of age,
change the first GRADE_UP to GRADE_DOWN:

                                                                \CODE
V = GRADE_DOWN(MEMBERS%AGE)
V = V(GRADE_UP(MEMBERS(V)%NAME))
ROSTER = MEMBERS(V)
                                                                \EDOC

The ideas and names here are inspired by APL.  The term ``grade''
rather than ``rank'' is used because the latter is already used in the
Fortran 90 standard to mean the size of the shape of an array (that is,
the number of dimensions).

\subsection{POPCNT, POPPAR, and LEADZ Functions}

\subsubsection{POPCNT}

An elemental population count function.  Its action on a scalar is:

                                                                \CODE
POPCNT(x) = COUNT( (/ (BTEST(x,J), J=0, BIT_SIZE(x)-1) /) )
                                                                \EDOC

The result is the number of 1-bits in the integer x, according to the
bit-manipulation model in section 13.5.7 of the Fortran 90 standard.

\subsubsection{POPPAR}

An elemental population-parity function.  Its action on a scalar is:

                                                                \CODE
POPPAR(x) = MERGE(1,0,BTEST(POPCNT(x),0))
                                                                \EDOC

The result is 1 if the number of 1-bits in the integer x is odd,
or 0 if the number of 1-bits in the integer x is even.

\subsubsection{LEADZ}

An elemental count-leading-zeros function.  Its action on a scalar is:

                                                                \CODE
LEADZ(x) = MINVAL( (/ (J, J=0,BIT_SIZE(x)) /),
     MASK=(/ (BTEST(x,J), J=BIT_SIZE(x)-1,0,-1), .TRUE. /) )
                                                                \EDOC

The result is a count of the number of leading 0-bits in the integer
x, according to the bit-manipulation model in section 13.5.7 of the
Fortran 90 standard.

Note that a given integer value may produce different results from
LEADZ, depending on the number of bits in the representation of the
integer.  That is because bits are counted from the left (the most
significant bit).

%(The intent is to define POPCNT, POPPAR, and LEADZ consistent with their
%use in Cray Fortran, but to limit them to integer arguments.  
%The
%definition of ILEN is equivalent to that of the built-in function
%integer-length in Common Lisp, which has proven to be quite useful.)

%
%    Draft of October 7, 1992, by Richard Shapiro;  edited by Rob Schreiber
%
\section{Mapping Inquiry Intrinsic Functions}

\footnote{Version of
October 14, 1992 --- Richard Shapiro.}
This section extends the set of Fortran intrinsic functions to add
inquiries regarding data distribution.

\subsection{Motivation}
HPF provides a rich set of data mapping directives. These directives
are advisory in nature. At some point, users will want to know to what
extent the compiler took their advice. This is especially important
when a user calls a non-HPF subroutine, since he may need to know the
exact mapping. For these reasons, HPF includes inquiry intrinsics which
describe how an array is actually mapped onto a machine.  To keep the
number of intrinsics small, the inquiry intrisics are structured as
intrinsic subroutines with optional arguments.

\def\varray{\verb+ARRAY+}
{\bf Result type and shape:}  Results are returned in optional arguments,
all of which have {\tt INTENT OUT}.   All logical results are
default logical scalars.   All integer results are default integer
scalars or rank one default integer arrays. 
Where a result is an integer array, it is a rank one,
default integer array of size equal to the rank of \varray\ except as
noted below.

\subsection{Alignment Inquiry Subroutine}

\CODE
CALL HPF_ALIGNMENT(ARRAY,LB,UB,STRIDE,AXIS_MAP,
	IDENTITY_MAP,DYNAMIC,NCOPIES)
INTEGER,DIMENSION(7) :: LB,UB,STRIDE,AXIS_MAP
INTEGER NCOPIES
LOGICAL IDENTITY_MAP,DYNAMIC
\EDOC	
\begin{description}
\item[Required] ARRAY 
\item[Optional] LB,UB,STRIDE,AXIS\_MAP,
		IDENTITY\_MAP,DYNAMIC,\\ NUMBER\_OF\_COPIES
\end{description}

The \verb+HPF_ALIGNMENT+ subroutine returns information regarding
the alignment of an array to its associated template.  \verb+ARRAY+ is the
only input argument; all the remaining arguments are optional output arguments.
\begin{description}
\item[ARRAY] The array about which alignment information is requested.
{\bf New In This Draft:}
ARRAY may not be a pointer that is disassociated or an allocatable array
that is not allocated.

\item[LB] An integer array containing the template coordinate
	of the first element of \varray\ along an axis.
\item[UB] An integer array containing the template coordinate
	of the last element of \varray\ along an axis.
\item[STRIDE] An integer array containing the stride used in 
        aligning the elements of \varray\ along an axis.
\item[AXIS\_MAP] An integer array containing the template axis associated 
        with an array axis.  If \verb+AXIS_MAP+ is 0, the axis 
        is a collapsed axis.
\item[IDENTITY\_MAP] A logical scalar which will be true if the template
	associated with \varray\  has a shape identical to \varray, the
	axes are mapped using the identity permutation, and the strides are all
        positive.
\item[DYNAMIC] A logical scalar which will be true if \varray\ has the 
	DYNAMIC attribute.
\item[NUMBER\_OF\_COPIES] An integer scalar equal to 
        the product of the extents of all template axes over
        which \varray\ has been replicated.
	For a non-replicated array, for
	example, this will be 1.
\end{description}

\subsection{Template Inquiry Subroutine}

\CODE
CALL HPF_TEMPLATE(ARRAY,TEMPLATE_RANK,LB,UB,AXIS_TYPE,
	AXIS_INFO,NUMBER_ALIGNED,DYNAMIC)
INTEGER,DIMENSION(MAX_TEMPLATE_RANK) :: LB,UB,AXIS_INFO
CHARACTER*(*) AXIS_TYPE(MAX_TEMPLATE_RANK)
INTEGER NUMBER_ALIGNED,TEMPLATE_RANK
LOGICAL DYNAMIC
\EDOC	

\begin{description}
\item[Required] ARRAY 
\item[Optional] LB,UB,AXIS\_TYPE,AXIS\_INFO,
		NUMBER\_ALIGNED,\\ TEMPLATE\_RANK,DYNAMIC
\end{description}
The \verb+HPF_TEMPLATE+ subroutine returns information regarding
the template associated with an array. The main difference between
\verb+HPF_TEMPLATE+ and \verb+HPF_ALIGNMENT+  is that the
former returns information concerning the array from the template's point
of view, while the latters returns information from the array's point of
view. \varray\ is the
only input argument; all the remaining arguments are optional output
arguments.   Array outputs are rnak one and of size equal to the rank of
the template, which is returned in TEMPLATE\_RANK.

\begin{description}
\item[ARRAY] The array about which template information is requested.
{\bf New In This Draft:}
ARRAY may not be a pointer that is disassociated or an allocatable array
that is not allocated.
\item[LB] An integer array containing the declared template lower bound 
	for each axis.
\item[UB] An integer array containing the declared template upper bound 
	for each axis.
\item[AXIS\_TYPE] A rank one array of type default character, of length
        at least 10,
        that returns information about each
	axis of the template.  The following values are defined by HPF
	(implementations may define other values):
	
	\begin{description}
	\item['NORMAL'] The axis has an axis of \varray\ aligned to to it.
		\verb+AXIS_INFO+ contains the axis of \varray\ aligned
		with the axis of the template.
	\item['SINGLE'] \varray\  is aligned with one coordinate of
		the template axis. \verb+AXIS_INFO+ contains the coordinate
		to which \varray\ is aligned.
	\item['REPLICATED'] \varray\  is replicated along this template axis.
		\verb+AXIS_INFO+ contains the number of copies of
		\varray\ along the axis. This is an implemtation-specific
		quantity.
	\end{description}

\item[AXIS\_INFO] See the desciption of \verb+AXIS_TYPE+ above.

         Example:
                               \CODE
       REAL A(4, 20)
CHPF$  TEMPLATE T(30, 150, 8, 200) 
CHPF$  ALIGN A(I,J,*) WITH T(J+5, 100, 10-2*I, *)
							\EDOC

         then 

							\CODE
AXIS_TYPE = (/'NORMAL', 'SINGLE', 'NORMAL', 'REPLICATED'/) 
							\EDOC
         and
							\CODE
AXIS_INFO = (/2, 100, 1, 200/)
							\EDOC

\item[NUMBER\_ALIGNED] An integer scalar giving the 
        total number of arrays aligned to the template. 
	This is the number of arrays which will be moved when the template
	is redistributed.
\item[DYNAMIC] A logical scalar which will be true if the
	template is redistributable.
\item[TEMPLATE\_RANK] An integer scalar giving the 
        number of axes in the template. This can be
	different than the number of \varray\  axes, due to collapsing and
	replicating.
\end{description}

\subsection{Distribution Inquiry Subroutine}
\CODE
CALL HPF_DISTRIBUTION (ARRAY,AXIS_TYPE,AXIS_INFO,
	PROCESSORS_SHAPE,PROCESSORS_RANK)
INTEGER AXIS_INFO(MAX_TEMPLATE_RANK),PROCESSORS_RANK
CHARACTER*(*) AXIS_TYPE(MAX_TEMPLATE_RANK)
INTEGER PROCESSORS_SHAPE(MAX_PROCESSORS_RANK)
\EDOC

\begin{description}
\item[Required] ARRAY 
\item[Optional] AXIS\_TYPE,AXIS\_INFO,
		PROCESSORS\_SHAPE,PROCESSORS\_RANK
\end{description}
The \verb+HPF_DISTRIBUTION+ subroutine returns information regarding
the distribution of the template associated with an array.
\varray\ is the only input argument; all the remaining arguments 
are optional output arguments.

\begin{description}
\item[ARRAY] The array about whose template distribution 
	information is requested.
{\bf New In This Draft:}
ARRAY may not be a pointer that is disassociated or an allocatable array
that is not allocated.
\item[AXIS\_TYPE] A rank one array of type default character and of
        length at least 9, and size equal to the rank of the template
        of \varray\ (which is returned by HPF_TEMPLATE in
        TEMPLATE\_RANK),
        that returns information about the 
	distribution of each axis of the template. 
	 The following values are defined by HPF
	(implementations may define other values):
	
	\begin{description}
	\item['BLOCK'] The axis is distributed BLOCK.
		\verb+AXIS_INFO+ contains the block size.
	\item['CYCLIC'] The axis is distributed CYCLIC.
		\verb+AXIS_INFO+ contains the block size.
	\item['COLLAPSED'] The axis is collapsed (distributed with the *
		specification)
	\end{description}	
\item[AXIS\_INFO] A rank one integer array.  
        See the desciption of \verb+AXIS_TYPE+ above.
\item[PROCESSORS\_RANK] An integer scalar giving 
        the rank of the processor arrangement onto which
        \varray\ is distributed.
	with the template.
\item[PROCESSORS\_SHAPE] An array of type default integer and of size
	equal to the value returned in PROCESSORS\_RANK, giving the
	shape of the processor arrangement onto which \varray\ is
	distributed.

\end{description}

\subsection{Examples}
Consider the declarations below:

\CODE
      DIMENSION A(10,10),B(20,30),C(20,40,10),D(40)
CHPF$ TEMPLATE T(40,20)
CHPF$ DYNAMIC A
CHPF$ ALIGN A(I,:) WITH T(1+3*I,2:20:2)
CHPF$ ALIGN C(I,*,J) WITH T(J,21-I)
CHPF$ ALIGN D(I) WITH T(I,4)
CHPF$ PROCESSORS PROCS(4,2)
CHPF$ DISTRIBUTE T(BLOCK,BLOCK) ONTO PROCS
CHPF$ DISTRIBUTE B(CYCLIC,BLOCK) ONTO PROCS
\EDOC	

The results of \verb+HPF_ALIGNMENT+ will be:
\begin{center}
\begin{tabular}{|l|c|c|c|}
\hline
                   & A         & B         & C      \\ \hline \hline
LB                 & 4,2,...   & 1,1,...   & 1,N/A,1,...   \\ \hline
UB                 & 31,20,... & 20,30,... & 20,N/A,10,... \\ \hline
STRIDE             & 3,2,...   & 1,1,...   & -1,N/A,1,...  \\ \hline
AXIS\_MAP          & 1,2,...   & 1,2,...   & 2,0,1,...     \\ \hline
IDENTITY\_MAP      & .FALSE.   & .TRUE.    & .FALSE.       \\ \hline
DYNAMIC            & .TRUE.    & .FALSE.   & .FALSE.       \\ \hline
NUMBER\_OF\_COPIES & 1         & 1         & 1             \\ \hline
\end{tabular}\end{center}

and the result of  \verb+HPF_TEMPLATE+ will be

\begin{center}
\begin{tabular}{|l|c|c|c|}
\hline
           & A         & C         & D          \\ \hline \hline
LB         & 1,1,...   & 1,1,...   & 1,1,...    \\ \hline
UB         & 40,20,... & 40,20,... & 40,20,...  \\ \hline
AXIS\_TYPE & 'NORMAL','NORMAL',... &
	 'NORMAL','NORMAL',... & 'NORMAL','SINGLE',... \\ \hline
AXIS\_INFO & 1,2,...   & 3,1,...   & 1,4,...    \\ \hline
NUM.AL.    & 3         & 3         & 3          \\ \hline
TEMP. RANK & 2         & 2         & 2          \\ \hline
DYNAMIC    & .FALSE.   & .FALSE.   & .FALSE.    \\ \hline
\end{tabular}\end{center}

Finally  \verb+HPF_DISTRIBUTION+ will produce
\begin{center}
\begin{tabular}{|l|c|c|}
\hline
                  & A         & B  \\ \hline \hline
AXIS\_TYPE        & 'BLOCK','BLOCK',... &
	 'CYCLIC','BLOCK',...            \\ \hline
AXIS\_INFO        & 10,10,... & 1,15,...  \\ \hline
PROCESSORS\_SHAPE & 4,2,...   & 4,2,...   \\ \hline
PROCESSORS\_RANK  & 2         & 2         \\ \hline
\end{tabular}\end{center}

Note that the values of the block sizes (in \verb+AXIS_INFO+) are not
specified by HPF, but may be implementation-dependent.

---

From: Richard Shapiro <[email-masked]>
Date: Tue, 1 Dec 92 10:13:04 EST

Date: Mon, 30 Nov 92 14:57:33 PST
   From: Rob Schreiber <[email-masked]>

   Dave,

In order to make the table fit try this:

   \small      % added to make table fit

   \begin{center}
   \small      % added to make table fit

   \small      % added to make table fit