Subject: Plan

From: Rob Schreiber <[email-masked]>
Date: Mon, 18 May 92 14:13:36 PDT

Dear committee,

The mail on intrinsics having died down, I suspect that we are
ready to agree on a few things.

1.  We can come up with a draft proposal on intrinsics via email.
2.  Guy's collection, with Dave's NPROCS as modified (with PROCESSOR_SHAPE)
    will be the basis of it.
3.  There seems to have been general agreement on the questions Guy raised.

 a.   Yes to all the new intrinsic groups proposed.   No floating-point versions
      of the POPCNT, POPPAR, ILEN, LEADZ intinsics.

 b.   Sort:  First option (GRADE_UP, GRADE_DOWN) instead of the second
             (GRADE, INDEX)
             Stability is required.

 c.   The "alternating sequence" method of defining segments in the XXX_PREFIX 
      and XXX_SUFFIX intrinsics.

4.  These proposals are in accord with Rex's advisory message about extensions to the
    language.

Let me know if you agree.   If there is agreement, I will suggest to Guy and
Dave that they formulate new drafts along these lines, and that we try to
reach a consensus by end of June.  We can have a pro forma meeting in Dallas
before the Forum meeting, attendance optional, if we succeed.

--- Rob

**** Here is the text of Guy and Dave's proposals:

**** Next 6 sections are Guy's:

1.  Proposal for HPF intrinsics POPCNT, POPPAR, LEADZ, and ILEN

Guy L. Steele Jr.
Thinking Machines Corporation
Version of May 6, 1992

(a) An elemental population count intrinsic.  Its action on a scalar is:

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

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.

(b) An elemental population-parity intrinsic.  Its action on a scalar is:

  POPPAR(x) = MERGE(1,0,BTEST(POPCNT(x),0))

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.

(c) An elemental count-leading-zeros intrinsic.  Its action on a scalar is:

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

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).

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

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

This is related to LEADZ but is often much more convenient for
the calculation of array dimensions, etc.  It 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).

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.

Issues: I hope I have defined POPCNT, POPPAR, and LEADZ consistent
with their use in Cray Fortran.  I believe that Cray Fortran allows
these intrinsics to be applied to data types other than integers;
should HPF allow this also?
From: Guy Steele <[email-masked]>
Date: Wed, 6 May 92 14:28:51 EDT
To: [email-masked]
Cc: [email-masked]
Subject:  extension to MINLOC and MAXLOC
Status: R

2.  Proposal for extension to MINLOC and MAXLOC for HPF

Guy L. Steele Jr.
Thinking Machines Corporation
Version of May 6, 1992

The MAXLOC and MINLOC instrinsics should have an optional DIM
argument.  If such an argument is present, then the shape of the
result equals the shape of the first argument with one dimension (that
indicated by the DIM argument) deleted; it is as if a series of
one-dimensional MAXLOC or MINLOC operations were performed.

Example: If A has the value

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

then	MINLOC(A, DIM=1) has the value [ 1, 1, 2, 3 ]
	MAXLOC(A, DIM=1) has the value [ 2, 3, 1, 2 ]
	MINLOC(A, DIM=2) has the value [ 2, 3, 4 ]
	MAXLOC(A, DIM=2) has the value [ 3, 2, 3 ].
From: Guy Steele <[email-masked]>
Date: Wed, 6 May 92 14:29:30 EDT
To: [email-masked]
Cc: [email-masked]
Subject:  reduction intrinsics
Status: R

3.  Proposal for reduction instrinsics for HPF

Guy L. Steele Jr.
Thinking Machines Corporation
Version of May 6, 1992

Just as we have the correpondences:

	operator/intrinsic	reduction intrinsic

		+			SUM
		*			PRODUCT
		.AND.			ALL
		.OR.			ANY
		MAX			MAXVAL
		MIN			MINVAL

it would be useful to have reduction versions of certain
other operators and intrinsics in the language that happen
to be associative and commutative:

				    proposed
	operator/intrinsic	reduction intrinsic

		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.

Some of these are particularly valuable if corresponding
parallel-prefix intrinsics are also defined (see separate proposal).
From: Guy Steele <[email-masked]>
Date: Wed, 6 May 92 14:44:58 EDT
To: [email-masked]
Cc: [email-masked]
Subject:  parallel prefix intrinsics
Status: R

4.  Proposal for parallel prefix instrinsics for HPF

Guy L. Steele Jr.
Thinking Machines Corporation
Version of May 6, 1992

For every reduction operation XXX in the language, introduce two new
intrinsics XXX_PREFIX and XXX_SUFFIX.  They take the same arguments
as the corresponding reduction intrinsic, plus two additional
optional arguments:

	XXX_PREFIX(ARRAY, DIM, MASK, SEGMENT, EXCLUSIVE)
	XXX_SUFFIX(ARRAY, DIM, MASK, SEGMENT, EXCLUSIVE)

The first additional optional argument is called SEGMENT, which is of
type logical and conformable with the ARRAY argument (a TRUE element
indicates the start of a new segment of the first argument, that is,
a place where the running accumulation is to be reset before
processing the corresponding array element).

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 /).)

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.
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 give the compiler a strong hint:

      WHERE (FOO) A=SUM_PREFIX(B,MASK=FOO)

If the DIM argument is omitted, then the arrays are processed in
array element order ("column-major"), as if temporarily regarded as
one-dimensional.

In all cases the result has the same shape as the first argument.

In addition, the operation COPY_PREFIX replicates the first
(lowest-indexed) element of each segment throughout the segment, and
the operation COPY_SUFFIX replicates the last (highest-indexed)
element of each segment throughout the segment.

Examples:

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. )      !just for conciseness

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,F,F,F,T,F,T,T,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,F,F,F,T,F,T,T,F/)) yields (/1,1,1,1,5,5,7,8,8/)
               ------- --- - ---             ------- --- - ---
	      four input segments       four independent result segments

Outstanding issues: This proposal delimits the segments by indicating
the *start* of each segment.  Cray MPP Fortran delimits the segments
by indicating the *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.

Another way to delimit segments is to use a logical vector and say
that a new segment begins at every *transition* from false to true or
true to false; thus a segment is indicated by a maximal contiguous
subsequence of like logical values:

	(/T,T,T,F,T,F,F,F,T,F,F,T/)
          ----- - - ----- - --- -    seven segments

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 a parallel XOR prefix or suffix.
    Of course, we would need to define one (see separate proposal
    for a PARITY reduction intrinsic).  Examples:

    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))

    These might be standard idioms for a compiler to recognize.
From: Guy Steele <[email-masked]>
Date: Wed, 6 May 92 15:44:06 EDT
To: [email-masked]
Cc: [email-masked]
Subject:  sorting intrinsics
Status: R

5.  Proposal for HPF sorting intrinsics

Guy L. Steele Jr.
Thinking Machines Corporation
Version of May 6, 1992

The ideas and names here are inspired by APL.  I have used the
term "grade" rather than "rank" 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).

GRADE_UP(ARRAY,DIM)

The array may be of type integer, real, or character.  [Alternate spec:
the array may be of any type for which the operator .LT. has been defined?]

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

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

then for all i1,i2,...,(omit ik),...,in, the vector B(i1,i2,...,:,...,in)
is sorted in ascending order.

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

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

where n=SIZE(SHAPE(ARRAY)), then B is sorted in ascending order.

Question: should stability be guaranteed?

GRADE_DOWN(ARRAY,DIM)

Same as GRADE_UP, with "ascending" replaced by "descending".

----------------------------------------------------------------

An alternate approach:

First define the utility intrinsic

INDEX(ARRAY,SUBS)

where SIZE(SUBS,DIM=1) = SIZE(SHAPE(ARRAY)).  If S = SHAPE(ARRAY),
then S(2:) is the shape of the result R, which has the property that

	R(i1,i2,...,in) = ARRAY(SUBS(1,i1,i2,...,in),
                                SUBS(2,i1,i2,...,in),
                                ...
                                SUBS(k,i1,i2,...,in))

where k = SIZE(SHAPE(ARRAY)) and n = SIZE(SHAPE(SUBS))-1.

GRADE(ARRAY1,ARRAY2,...)

Arguments ARRAY2,... are optional.  All arrays must be conformable.
The arrays must be of type integer, real, or character.  [Alternate spec:
the array may be of any type for which the operator .LT. has been defined?]
The arrays need not all be of the same type.

The result S is an array of rank 2, with shape [SIZE(SHAPE(ARRAY1)),
PRODUCT(SHAPE(ARRAY1))], and the property that if j < k then

ARRAY1(S(1,j),...,S(n,j)) .LT. ARRAY1(S(1,k),...,S(n,k))  or
( ARRAY1(S(1,j),...,S(n,j)) .EQ. ARRAY1(S(1,k),...,S(n,k))  and
  ( ARRAY2(S(1,j),...,S(n,j)) .LT. ARRAY2(S(1,k),...,S(n,k))  or
    ( ARRAY2(S(1,j),...,S(n,j)) .EQ. ARRAY2(S(1,k),...,S(n,k))  and
      ( ...

          ARRAYn(S(1,j),...,S(n,j)) .LT.  ARRAYn(S(1,k),...,S(n,k))  or
          ( ARRAYn(S(1,j),...,S(n,j)) .EQ. ARRAYn(S(1,k),...,S(n,k))  )
        ...
      )
    )
  )
)

which can also be written

INDEX(ARRAY1,S(:,j)) .LT. INDEX(ARRAY1,S(:,k))  or
( INDEX(ARRAY1,S(:,j)) .EQ. INDEX(ARRAY1,S(:,k))  and
  ( INDEX(ARRAY2,S(:,j)) .LT. INDEX(ARRAY2,S(:,k))  or
    ( INDEX(ARRAY2,S(:,j)) .EQ. INDEX(ARRAY2,S(:,k))  and
      ( ...

          INDEX(ARRAYn,S(:,j)) .LT. INDEX(ARRAYn,S(:,k))  or
          ( INDEX(ARRAYn,S(:,j)) .EQ. INDEX(ARRAYn,S(:,k))  )
        ...
      )
    )
  )
)

That is, the array arguments are treated as sort fields, with the first
argument most significant (major) and the last argument least significant
(minor).  The result gives a set of indices that can be used to
permute the arrays into a collectively sorted (ascending) order.

For example, suppose one had the following derived type (example
taken from section 4.4.1 of the Fortran 90 standard):

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

now consider two arrays of persons:

      TYPE(PERSON), DIMENSION(100000) :: MEMBERS, ROSTER

then the statement

      ROSTER = INDEX(MEMBERS,GRADE(MEMBERS%NAME,MEMBERS%AGE))

causes 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).
To list members with the same name in descending order of age,
the following trick more or less works:

      ROSTER = INDEX(MEMBERS,GRADE(MEMBERS%NAME,-MEMBERS%AGE))

though this is not completely general.
From: Guy Steele <[email-masked]>
Date: Wed, 6 May 92 18:02:56 EDT
To: [email-masked]
Cc: [email-masked]
Subject:  combining-send intrinsics
Status: R

6.  Proposal for HPF combining-send intrinsics

Guy L. Steele Jr.
Thinking Machines Corporation
Version of May 6, 1992

For every reduction operation XXX in the language, introduce a new
intrinsic subroutine XXX_SEND:

   XXX_SEND(SOURCE,DEST,IDX1,...)

Arguments IDX1,... are optional.  The number of IDX arguments
must equal the rank of DEST.  The SOURCE and all the IDX arguments
must be conformable.

For every element s in SOURCE, the corresponding elements ij of IDXj
are used to carry out the operation

	DEST(i1,i2,...,in) = XXX_operation(DEST(i1,i2,...,in), s)

and all such operations performed by a single call are done *as if
serially* in *some* (processor-dependent) order for each element s.
Thus the call

      CALL SUM_SEND(SOURCE,DEST,IDX1,IDX2,...,IDXn)

*could* be implemented as

      DO J1=LBOUND(SOURCE,1),UBOUND(SOURCE,1)
        DO J2=LBOUND(SOURCE,2),UBOUND(SOURCE,2)
          ...
            DO Jk=LBOUND(SOURCE,k),UBOUND(SOURCE,k)
              DEST(IDX1(J1,J2,...,Jk),
     &             IDX2(J1,J2,...,Jk),
     &             ...
     &             IDXn(J1,J2,...,Jk)) =
     &        DEST(IDX1(J1,J2,...,Jk),
     &             IDX2(J1,J2,...,Jk),
     &             ...
     &             IDXn(J1,J2,...,Jk)) + SOURCE(J1,J2,...,Jk)
            END DO
          ...
        END DO
      END DO

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_SEND intrinsic.  This matters when the
combining operation is not both associative and commutative,
for example floating-point addition.)

Example:  The C* operation

        x[v] += a;

where x, v, and a are all parallel arrays, and a and v conform,
may be rendered

      CALL SUM_SEND(A,X,V)

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

      X(V) = X(V) + A

The proposed intrinsic SUM_SEND "works" even if V contains
duplicate values.  Note that the two-dimensional case

      X(V,W) = X(V,W) + A

must be rendered using SPREAD:

      CALL SUM_SEND(A,X,SPREAD(V,DIM=2,NCOPIES=SIZE(X,2)),
     &                  SPREAD(W,DIM=1,NCOPIES=SIZE(X,1)))

in order to duplicate the cross-product effect of ordinary array
subscripting.  (I chose to propose a definition of XXX_SEND that does
*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.)

**** Here is the DEC NPROCS proposal:

New Intrinsic Function

In addition to the intrinsic functions of Fortran 90, High Performance
Fortran has a new intrinsic function, NUMBER_OF_PROCESSORS, which takes
no arguments and returns an integer value giving the number of
processors in the system. This number is expected to remain constant
for (at least) the duration of one program execution. Accordingly,
NUMBER_OF_PROCESSORS() is a constant expression and can be used
wherever any other Fortran 90 constant expression can be used. In
particular, NUMBER_OF_PROCESSORS can be used in an initialization
expression or in a specification 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 we add a new category of "system inquiry functions" and place
NUMBER_OF_PROCESSORS in that category.

Note that treating NUMBER_OF_PROCESSORS as a constant expression 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.

A definition of NUMBER_OF_PROCESSORS, in the style of Chapter 13 of the
Fortran 90 standard is:

13.13.77a  NUMBER_OF_PROCESSORS(DIM)

Optional Argument.  DIM

Description.  Returns the total number of processors available to the
program, the dimensionality of the processor array, 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 0<=DIM<=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 rank of the
processor array if the value of DIM is 0, the extent of dimension DIM
(1<=DIM<=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.

The value of NUMBER_OF_PROCESSORS() need not be a constant, if the
processor allows for a variable number of processors to execute the
program.  However, the value must not change during the execution of the program.

Example. For a DECmpp 12000 Model 8B with 8192 processors, the value of
NUMBER_OF_PROCESSORS( ) is 8192, the value of
NUMBER_OF_PROCESSORS(DIM=0) is 2, the value of
NUMBER_OF_PROCESSORS(DIM=1) is 128, and the value of NUMBER_OF_PROCE is 64.

The list of alternatives in a Fortran 90 restricted expression is
expanded to include NUMBER_OF_PROCESSORS, as follows:

A  *restricted expression* is an expression in which each operation is
intrinsic and each primary is:

1.   A constant or subobject of a constant,

2.   A variable that is a dummy argument that has neither the OPTIONAL
nor the INTENT (OUT) attribute, or a variable that is a subobject of such as dummy
argument,

3.   A variable that is in a common block or a variable that is a
subobject of a variable in a common block,

4.   A variable that is made accessible by use association or host
association or a variable that is a subobject of such a variable,

5.   An array constructor where each element and the bounds and strides
of each implied-DO are expressions whose primaries are either restricted expressions
or implied-DO variables, 

6.   A structure constructor where each component is a restricted expression,

7.   An elemental intrinsic function reference of type integer or
character where each argument is a restricted expression of type
integer or character,

8.   One of the transformational functions REPEAT, RESHAPE,
SELECTED_INT_KIND, SELECTED_REAL_KIND, TRANSFER, and TRIM, where each
argument is a restricted expression of type integer or character,

9.   A reference to an array inquiry function (13.10.15) other than
ALLOCATED, the bit inquiry function BIT_SIZE, the character inquiry
function LEN, the kind inquiry function KIND, or a numeric inquiry
function (13.10.8), where each argument is either a restricted
expression or a variable whose type parameters or bounds inquired about
are not assumed or defined by an ALLOCATE statement or a pointer
assignment, or

10.   A restricted expression enclosed in parentheses, or

11.   The HPF system inquiry function NUMBER_OF_PROCESSORS.

**** Here is Guy's suggested change to it.

[a] Using DIM=0 to convey rank information is one of those
clever-but-strange encoding tricks.  Inasmuch as there is no
precedent for it already in Fortrasn 90, I recommend considering a
separate intrinsic that would be analogous to something already in
Fortran 90, to wit, PROCESSORS_SHAPE(), returning a rank-1 vector.
Thus SIZE(PROCESSOR_SHAPE()) is the rank of the processor array.

Example.  For a DECmpp 12000 Model 8B with 8192 processors, the value of
PROCESSORS_SHAPE() is (/ 128, 64 /).

Example.  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 /).

Example.  For a Connection Machine CM-5 with 8192 processors, the value of
PROCESSORS_SHAPE() might be (/ 8192 /).

[b] Perhaps NUMBER_OF_PROCESSORS and PROCESSORS_SHAPE should take
another optional argument, the name of a processors arrangement
as declared in a PROCESSORS directive:

	NUMBER_OF_PROCESSORS(procs, dim)
	PROCESSORS_SHAPE(procs)

If "procs" is omitted, then the information returned concerns the
"natural" processors arrangement of the hardware; if included,
then it serves as an inquiry intrinsic for the declared processors
arrangement.  Perhaps this latter form should be restricted to use
within HPF directives (inasmuch as the referenced name is defined
by an HPF directive).

Example.

!HPF$ PROCESSORS FOO(40,80)
!HPF$ ... PROCESSORS_SHAPE(FOO) ...           ! (/ 40, 80 /)
!HPF$ ... NUMBER_OF_PROCESSORS(FOO) ...       ! 3200

---

From: Richard Shapiro <[email-masked]>
Date: Mon, 18 May 92 17:19:52 EDT

Date: Mon, 18 May 92 14:13:36 PDT
   From: Rob Schreiber <[email-masked]>

   Dear committee,

I am in agreement.
	Rich Shapiro