C
C Copyright (c) 1992 Carnegie Mellon University 
C                    SCAL project: Guy Blelloch, Siddhartha Chatterjee,
C                                  Jonathan Hardwick, Jay Sipelstein,
C                                  Marco Zagha
C All Rights Reserved.
C
C Permission to use, copy, modify and distribute this software and its
C documentation is hereby granted, provided that both the copyright
C notice and this permission notice appear in all copies of the
C software, derivative works or modified versions, and any portions
C thereof, and that both notices appear in supporting documentation.
C
C CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
C CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
C ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
C
C The SCAL project requests users of this software to return to 
C
C  Marco Zagha and Guy Blelloch         marco.zagha@cs.cmu.edu
C  School of Computer Science           guy.blelloch@cs.cmu.edu
C  Carnegie Mellon University
C  5000 Forbes Ave.
C  Pittsburgh PA 15213-3890
C
C any improvements or extensions that they make and grant Carnegie Mellon
C the rights to redistribute these changes.
C
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C
C Pure fortran segmented-scan based sparse-matrix vector multiply
C Marco Zagha (marcoz@cs.cmu.edu)
C
C  2/16/93: initial version
C  2/17/93: made an unrolled version
C  2/18/93: packed flags and 4-way unrolling
C  2/24/93: cleaned up loop structure for unrolled loops
C  2/25/93: some tuning
C  2/25/93: cleaned up interface, added some comments
C
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C
C  Format of this file:
C
C    Exported interfaces:
C       Cxxxx:  Clean fortran version
C       C2xxxx: Clean fortran version with 2-way unrolling & directives
C       Fxxxx:  Fast fortran version with packed flags and unrolling
C
C    Common code used by all versions
C
C    Code for Cxxxx
C    Code for C2xxxx
C    Code for Fxxxx
C
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C
C   Various constants and arrays are stuffed into the descriptor array
C   in order to simplify the interface.  Not all fields are used by
C   all implementations, but the same format is used for all implementations.
C   Note: if porting to a machine with a smaller hardware vector length
C   there is no need to change the format below.
C
C   Descriptor format:               offset   comments
C      LAST:     128                 1        index of last flag in column
C      PRESENT:  128                 129      true if flag present in column
C      L:        1                   257      max vector length used
C      S:        1                   258      stride
C      PS:       1                   259      stride on packed flags
C      R:        1                   260      # of full vector length rows
C      [flag field]                  261
C
C The flag field is either PFLAG for the fast implementation or
C FLAG for the clean implementations.  FLAG takes significantly
C more space than PFLAG.
C
C      PFLAG:    N/64 + 3000         261      packed flags (64 per word)
C                     (overkill)
C      FLAG:     N                   261      unpacked flags 
C
C Total descriptor size:
C   Fast implementation:    N/64 + 4000
C   Clean implementations:  N + 1000
C
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
      
      
      SUBROUTINE CMAKEDESC(DESC, PNTR, NROWS, SCRATCH)
      INTEGER DESC(*), PNTR(*), NROWS, SCRATCH(*)
      INTEGER N
      
      N = PNTR(NROWS+1) - 1
      CALL CLEANSETUP(DESC(261), DESC(129), DESC(1), PNTR, 
     $     NROWS, N, DESC(257), DESC(258), DESC(260))
      
      RETURN
      END

      SUBROUTINE C2MAKEDESC(DESC, PNTR, NROWS, SCRATCH)
      INTEGER DESC(*), PNTR(*), NROWS, SCRATCH(*)
      INTEGER N
      
      N = PNTR(NROWS+1) - 1
      CALL CLEANSETUP(DESC(261), DESC(129), DESC(1), PNTR, 
     $     NROWS, N, DESC(257), DESC(258), DESC(260))
      
      RETURN
      END
      
      SUBROUTINE FMAKEDESC(DESC, PNTR, NROWS, SCRATCH)
      INTEGER DESC(*), PNTR(*), NROWS, SCRATCH(*)
      INTEGER N
      
      N = PNTR(NROWS+1) - 1
      CALL FASTSETUP(DESC(261), SCRATCH, DESC(129), DESC(1), PNTR, 
     $     NROWS, N, DESC(257), DESC(258), DESC(259), DESC(260))
      
      RETURN
      END

CCCCCCC
      
      SUBROUTINE CMVMULT(RESULT, MATRIX, VECTOR, INDX, PNTR,
     $     DESC, NROWS, SCRATCH)
      INTEGER NROWS
      REAL RESULT(NROWS), MATRIX(*), VECTOR(NROWS)
      INTEGER INDX(*), PNTR(*), DESC(*), SCRATCH(*)
      INTEGER N
      
      N = PNTR(NROWS+1) - 1
      CALL CLEANMVMULT(RESULT, MATRIX, VECTOR, INDX, PNTR,
     $     DESC(261), DESC(1), DESC(129), NROWS, N,
     $     DESC(257), DESC(258), DESC(260),
     $     SCRATCH)
      
      RETURN
      END

      SUBROUTINE C2MVMULT(RESULT, MATRIX, VECTOR, INDX, PNTR,
     $     DESC, NROWS, SCRATCH)
      INTEGER NROWS
      REAL RESULT(NROWS), MATRIX(*), VECTOR(NROWS)
      INTEGER INDX(*), PNTR(*), DESC(*), SCRATCH(*)
      INTEGER N
      
      N = PNTR(NROWS+1) - 1
      CALL CLEAN2MVMULT(RESULT, MATRIX, VECTOR, INDX, PNTR,
     $     DESC(261), DESC(1), DESC(129), NROWS, N,
     $     DESC(257), DESC(258), DESC(260),
     $     SCRATCH)
      
      RETURN
      END
      
      SUBROUTINE FMVMULT(RESULT, MATRIX, VECTOR, INDX, PNTR,
     $     DESC, NROWS, SCRATCH)
      INTEGER NROWS
      REAL RESULT(NROWS), MATRIX(*), VECTOR(NROWS)
      INTEGER INDX(*), PNTR(*), DESC(*), SCRATCH(*)
      INTEGER N
      
      N = PNTR(NROWS+1) - 1
      CALL FASTMVMULT(RESULT, MATRIX, VECTOR, INDX, PNTR,
     $     DESC(261), DESC(1), DESC(129), NROWS, N,
     $     DESC(257), DESC(258), DESC(259), DESC(260),
     $     SCRATCH)
      
      RETURN
      END

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C
C                               VLEN (L)
C                    ----------------------------
C                    |                           |
C                    |                           |
C                    |   2-dimensional view of   |  REM (R)
C                    |   the 1-d arrays          |
C         STRIDE (S) |                           |
C                    |                           |
C                    |                         |-|
C                    |                         |
C                    |                         | S-R
C                    |                         |
C                    --------------------------      
C                               L-1
 
      SUBROUTINE SHAPE(LEN, VLEN, STRIDE, PSTRIDE, REM)
      INTEGER LEN, VLEN, STRIDE, PSTRIDE, REM
      INTEGER LEN1, S, DIV, L, MAXVL
      parameter (MAXVL=128)
C                         adjust MAXVL for each machine (64 on YMP)

      IF (LEN .LT. 1200)  THEN
         L = MAXVL / 4
      ELSE IF (LEN .LT. 4000)  THEN
         L = MAXVL / 2
      ELSE 
         L = MAXVL
      ENDIF

      LEN1 = LEN - 1
      S = LEN1 / L + 1
C                        correct for bad strides
      IF (MOD(S,4) .EQ. 0) S = S + 1
      STRIDE = S
      PSTRIDE = STRIDE / 64 + 3
      DIV = LEN1 / S
      VLEN = DIV + 1
      REM = LEN1 - DIV * S + 1
      RETURN
      END

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

      SUBROUTINE LASTSETUP(FLAG, LAST, PRESENT, L, S, R)
      INTEGER L, S, R
      LOGICAL FLAG(S,L), PRESENT(L)
      INTEGER LAST(L)
      INTEGER I, J, VL, LOW, HIGH
      DO VL = L,L-1,-1
         IF (VL .EQ. L-1) THEN
            HIGH = S
            LOW = R+1
         ELSE
            HIGH = R
            LOW = 1
         ENDIF
         DO I = LOW,HIGH
CDIR$SHORTLOOP128
            DO J = 1,VL
               IF (FLAG(I,J)) THEN
                  LAST(J) = I
                  PRESENT(J) = .TRUE.
               ENDIF
            END DO
         END DO
      END DO
      RETURN
      END
      

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
      
      SUBROUTINE FLAGSETUP(FLAG, PNTR, N, M)
      INTEGER N, M
      LOGICAL FLAG(N)
      INTEGER PNTR(M)
      INTEGER I, J
      DO J = 1, N
         FLAG(J) = .FALSE.
      END DO
      DO I = 1, M
         FLAG(PNTR(I)) = .TRUE.
      END DO
      RETURN
      END

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

C   This is a separate subroutine because some of the routines
C   using "2-d" arrays need to do a gather on 1-d arrays.
      
      SUBROUTINE GATHR(DEST, SRC, INDEX, M, N)
      INTEGER M, N
      REAL DEST(M), SRC(N)
      INTEGER INDEX(M)
      INTEGER K
      DO K = 1,M
         DEST(K) = SRC(INDEX(K))
      END DO
      RETURN
      END

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

      SUBROUTINE CLEANMVMULT(Y, VAL, X, INDX, PNTR, FLAG,
     $     LAST, PRESENT, M, N, L, S, R, SCANVAL)
      INTEGER M, N, L, S, R
      REAL Y(M), VAL(S,L), X(M), SCANVAL(S,L)
      INTEGER INDX(S,L), PNTR(M), LAST(L)
      LOGICAL PRESENT(L), FLAG(S,L)
      REAL SUM(128), SCANSUM(128), OLDSUM(128), NEWSUM(128)
      SAVE SUM, SCANSUM, OLDSUM, NEWSUM
      INTEGER I, J, VL, LOW, HIGH
      REAL SV, SS
      
      DO J = 1,L-1
         SCANVAL(S,J) = VAL(S,J) * X(INDX(S,J))
      END DO
      SCANVAL(R,L) = VAL(R,L) * X(INDX(R,L))

C     the important loop nest

      DO VL=L-1,L
         IF (VL .EQ. L-1) THEN
            HIGH = S
            LOW = R+1
         ELSE
            HIGH = R
            LOW = 2
         ENDIF
         DO I = HIGH,LOW,-1
            DO J = 1,VL
               SV = SCANVAL(I,J)
               IF (FLAG(I,J)) THEN
                  SS = 0.0
               ELSE 
                  SS = SV
               ENDIF
               SCANVAL(I-1,J) = VAL(I-1,J) * X(INDX(I-1,J)) + SS
            END DO
         END DO
      END DO

C     Now the fixup steps for spanning segments
      
      DO J = 1,L
         IF (FLAG(1,J)) THEN
            SUM(J) = 0.0
         ELSE 
            SUM(J) = SCANVAL(1,J)
         ENDIF
      END DO

      SCANSUM(L) = 0.0
      DO J = L-1,1,-1
         IF (PRESENT(J+1)) THEN
            SCANSUM(J) = SUM(J+1)
         ELSE
            SCANSUM(J) = SUM(J+1) + SCANSUM(J+1)
         ENDIF
      END DO
      
      DO J = 1,L
         IF (PRESENT(J)) THEN
            OLDSUM(J) = SCANVAL(LAST(J),J)
            NEWSUM(J) = OLDSUM(J) + SCANSUM(J)
            SCANVAL(LAST(J),J) = NEWSUM(J)
         ENDIF
      END DO

C     Gather the result
      
      CALL GATHR(Y, SCANVAL, PNTR, M, N)
      
      RETURN 
      END
      
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C
C   2-way unrolled, a few compiler directives added
C
      SUBROUTINE CLEAN2MVMULT(Y, VAL, X, INDX, PNTR, FLAG,
     $     LAST, PRESENT, M, N, L, S, R, SCANVAL)
      INTEGER M, N, L, S, R
      REAL Y(M), VAL(S,L), X(M), SCANVAL(S,L)
      INTEGER INDX(S,L), PNTR(M), LAST(L)
      LOGICAL PRESENT(L), FLAG(S,L)
      REAL SUM(128), SCANSUM(128)
      SAVE SUM, SCANSUM
      INTEGER I, J, VL, LOW, HIGH
      REAL SV, SS, XX
      
CDIR$SHORTLOOP128
      DO J = 1,L-1
         SCANVAL(S,J) = VAL(S,J) * X(INDX(S,J))
      END DO
      SCANVAL(R,L) = VAL(R,L) * X(INDX(R,L))

C     the important loop nest
      DO VL=L-1,L
         IF (VL .EQ. L-1) THEN
            HIGH = S
            LOW = R+1
         ELSE
            HIGH = R
            LOW = 2
         ENDIF
         DO I = HIGH,LOW+1,-2
CDIR$SHORTLOOP128
            DO J = 1,VL
               SV = SCANVAL(I,J)
               IF (FLAG(I,J)) THEN
                  SS = 0.0
               ELSE 
                  SS = SV
               ENDIF
               SCANVAL(I-1,J) = VAL(I-1,J) * X(INDX(I-1,J)) + SS

               XX = X(INDX(I-2,J))
               SV = SCANVAL(I-1,J)
               IF (FLAG(I-1,J)) THEN
                  SS = 0.0
               ELSE 
                  SS = SV
               ENDIF
               SCANVAL(I-2,J) = VAL(I-2,J) * XX + SS
            END DO
         END DO
         DO I = LOW+MOD(HIGH-LOW+1,2)-1,LOW,-1
CDIR$SHORTLOOP128
            DO J = 1,VL
               SV = SCANVAL(I,J)
               IF (FLAG(I,J)) THEN
                  SS = 0.0
               ELSE 
                  SS = SV
               ENDIF
               SCANVAL(I-1,J) = VAL(I-1,J) * X(INDX(I-1,J)) + SS
            END DO
         END DO
      END DO
      
CDIR$SHORTLOOP128
      DO J = 1,L
         IF (FLAG(1,J)) THEN
            SUM(J) = 0.0
         ELSE 
            SUM(J) = SCANVAL(1,J)
         ENDIF
      END DO

      SCANSUM(L) = 0.0
      DO J = L-1,1,-1
         IF (PRESENT(J+1)) THEN
            SCANSUM(J) = SUM(J+1)
         ELSE
            SCANSUM(J) = SUM(J+1) + SCANSUM(J+1)
         ENDIF
      END DO
      
CDIR$SHORTLOOP128
CDIR$IVDEP
      DO J = 1,L
         IF (PRESENT(J)) THEN
            SCANVAL(LAST(J),J) = SCANVAL(LAST(J),J) + SCANSUM(J)
         ENDIF
      END DO
      
      CALL GATHR(Y, SCANVAL, PNTR, M, N)
      
      RETURN 
      END
      

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

      SUBROUTINE CLEANSETUP(FLAG, PRESENT, LAST, PNTR,
     $     M, N, L, S, R)
      INTEGER M, N, L, S, R
      INTEGER FLAG(N), PRESENT(*), PNTR(M)
      LOGICAL LAST(*)
      INTEGER IGNORE

      CALL SHAPE(N, L, S, IGNORE, R)
      CALL FLAGSETUP(FLAG, PNTR, N, M)
      CALL LASTSETUP(FLAG, LAST, PRESENT, L, S, R)

      RETURN
      END

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C
C   packed flags, 4-way unrolled, a few compiler directives added
C


      SUBROUTINE FASTMVMULT(Y, VAL, X, INDX, PNTR, PFLAG,
     $     LAST, PRESENT, M, N, L, S, PS, R, SCANVAL)
      INTEGER M, N, L, S, PS, R
      REAL Y(M), VAL(S,L), X(M), SCANVAL(S,L)
      INTEGER INDX(S,L), PNTR(M), LAST(L)
      LOGICAL PRESENT(L)
      INTEGER PFLAG(PS,L)
      REAL SUM(128), SCANSUM(128)
      SAVE SUM, SCANSUM
      INTEGER I, J, VL, LOW, HIGH
      INTEGER II, IX, B, BMAX, BASE, BB, PF
      REAL SV, SS, XX

CDIR$SHORTLOOP128
      DO J = 1,L-1
         SCANVAL(S,J) = VAL(S,J) * X(INDX(S,J))
      END DO
      SCANVAL(R,L) = VAL(R,L) * X(INDX(R,L))
      
      II = PS
      DO VL=L-1,L
         IF (VL .EQ. L-1) THEN
            HIGH = S
            LOW = R+1
         ELSE
            HIGH = R
            LOW = 2
         ENDIF
         DO I = HIGH, LOW, -64
            BMAX = MIN(64, I-LOW+1)
            BASE = I - BMAX + 1
            DO B = BMAX-1, 3, -4
               BB = BASE+B
CDIR$SHORTLOOP128
               DO J = 1,VL
                  PF = PFLAG(II,J)
                  
                  IX = INDX(BB-1,J)
                  SV = SCANVAL(BB,J)
                  IF (BTEST(PF,B)) THEN
                     SS = 0.0
                  ELSE 
                     SS = SV
                  ENDIF
                  XX = X(IX)
                  SCANVAL(BB-1,J) = VAL(BB-1,J) * XX + SS
                  
                  IX = INDX(BB-2,J)
                  SV = SCANVAL(BB-1,J)
                  IF (BTEST(PF,B-1)) THEN
                     SS = 0.0
                  ELSE 
                     SS = SV
                  ENDIF
                  XX = X(IX)
                  SCANVAL(BB-2,J) = VAL(BB-2,J) * XX + SS
                  
                  IX = INDX(BB-3,J)
                  SV = SCANVAL(BB-2,J)
                  IF (BTEST(PF,B-2)) THEN
                     SS = 0.0
                  ELSE 
                     SS = SV
                  ENDIF
                  XX = X(IX)
                  SCANVAL(BB-3,J) = VAL(BB-3,J) * XX + SS
                  
                  IX = INDX(BB-4,J)
                  SV = SCANVAL(BB-3,J)
                  IF (BTEST(PF,B-3)) THEN
                     SS = 0.0
                  ELSE 
                     SS = SV
                  ENDIF
                  XX = X(IX)
                  SCANVAL(BB-4,J) = VAL(BB-4,J) * XX + SS
               END DO
            END DO
            DO B = MOD(BMAX,4)-1,0,-1
               BB = BASE+B
CDIR$SHORTLOOP128
               DO J = 1,VL
                  IX = INDX(BB-1,J)
                  SV = SCANVAL(BB,J)
                  IF (BTEST(PFLAG(II,J),B)) THEN
                     SS = 0.0
                  ELSE 
                     SS = SV
                  ENDIF
                  XX = X(IX)
                  SCANVAL(BB-1,J) = 
     $                 VAL(BB-1,J) * XX + SS
               END DO
            END DO
            II = II - 1
         END DO
      END DO
      
CDIR$SHORTLOOP128
      DO J = 1,L
         IF (BTEST(PFLAG(1,J),0)) THEN
            SUM(J) = 0.0
         ELSE 
            SUM(J) = SCANVAL(1,J)
         ENDIF
      END DO
      
      SS = 0.0
      DO J = L-1,1,-1
        SV = SUM(J+1)
        IF (PRESENT(J+1)) THEN
           SS = SV
        ELSE
           SS = SV + SS
        ENDIF
        SCANSUM(J) = SS
      END DO

CDIR$SHORTLOOP128
CDIR$IVDEP
      DO J = 1,L-1
         IF (PRESENT(J)) THEN
            SCANVAL(LAST(J),J) = SCANVAL(LAST(J),J) + SCANSUM(J)
         ENDIF
      END DO

      CALL GATHR(Y, SCANVAL, PNTR, M, N)
      
      RETURN 
      END

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

      SUBROUTINE PACKFLAGS(PFLAG, FLAG, L, S, PS, R)
      INTEGER L, S, PS, R
      LOGICAL FLAG(S,L)
      INTEGER PFLAG(PS,L)
      INTEGER B, BIT, MASK, BMAX, VL, LOW, HIGH
      INTEGER II, I, J, BASE
      
      DO II = 1, PS
CDIR$SHORTLOOP128
         DO J = 1,L
            PFLAG(II,J) = 0
         END DO
      END DO
      
      II = PS
      DO VL=L-1,L
         IF (VL .EQ. L-1) THEN
            HIGH = S
            LOW = R+1
         ELSE
            HIGH = R
            LOW = 2
         ENDIF
         DO I = HIGH, LOW, -64
            BMAX = MIN(64, I-LOW+1)
            BASE = I - BMAX + 1
            DO B = BMAX-1, 0, -1
               BIT = ISHFT(1,B)
CDIR$SHORTLOOP128
               DO J = 1,VL
                  IF (FLAG(BASE+B,J)) THEN
                     MASK = BIT
                  ELSE
                     MASK = 0
                  ENDIF
                  PFLAG(II,J) = IOR(PFLAG(II,J), MASK)
               END DO
            END DO
            II = II - 1
         END DO
      END DO
CDIR$SHORTLOOP128
      DO J = 1,L
         IF (FLAG(1,J)) THEN
            PFLAG(1,J) = 1
         ELSE
            PFLAG(1,J) = 0
         ENDIF
      END DO
      
      RETURN 
      END

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

      SUBROUTINE FASTSETUP(PFLAG, FLAG, PRESENT, LAST, PNTR,
     $     M, N, L, S, PS, R)
      INTEGER M, N, L, S, R, PS
      INTEGER FLAG(N), PFLAG(*), PRESENT(*), PNTR(M)
      LOGICAL LAST(*)

      CALL SHAPE(N, L, S, PS, R)
      CALL FLAGSETUP(FLAG, PNTR, N, M)
      CALL LASTSETUP(FLAG, LAST, PRESENT, L, S, R)
      CALL PACKFLAGS(PFLAG, FLAG, L, S, PS, R)

      RETURN
      END
