C @(#)pcacif.for	19.1 (ES0-DMD) 02/25/03 13:27:33
C===========================================================================
C Copyright (C) 1995 European Southern Observatory (ESO)
C
C This program is free software; you can redistribute it and/or 
C modify it under the terms of the GNU General Public License as 
C published by the Free Software Foundation; either version 2 of 
C the License, or (at your option) any later version.
C
C This program is distributed in the hope that it will be useful,
C but WITHOUT ANY WARRANTY; without even the implied warranty of
C MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
C GNU General Public License for more details.
C
C You should have received a copy of the GNU General Public 
C License along with this program; if not, write to the Free 
C Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
C MA 02139, USA.
C
C Corresponding concerning ESO-MIDAS should be addressed as follows:
C	Internet e-mail: midas@eso.org
C	Postal address: European Southern Observatory
C			Data Management Division 
C			Karl-Schwarzschild-Strasse 2
C			D 85748 Garching bei Muenchen 
C			GERMANY
C===========================================================================
C
C-----------------------------------------------------------------------
C
C.IDENTIFICATION
C
C  Program PCACIF
C
C.AUTHOR
C
C  F. Murtagh, ST-ECF, Garching.             Version 1.0  9 May 1986
C  F. Murtagh                                Version 2.0  Oct. 1988
C                                            (New std. ifs.)
C  M. Peron                                  Version 3.0  Sep 91
C.PURPOSE
C
C  Pass parameters to, and execute, PCA (Principal Components Analysis).
C  The columns, only, are studied (i.e. in the objects space).
C
C.INPUT/OUTPUT
C
C  P1 - P5 contain parameters; these are: input table name, output
C  table name, PCA option (3 = on correlations, 2 = on covariances, 1 =
C  on SSCPs), number of principal components wanted (optional, - default
C  = 3), and whether or not a second output table is required, to store
C  the eigenvectors.
C
C.ALGORITHM
C
C . uses Midas Table interface routines;
C . input table is assumed to contain entries in single precision;
C . no select or nulls;
C . all columns of the input table are sent to the PCA routine;
C . description of the PCA routine is in the corresponding program;
C . the output produced consists of one (or two) tables, and descriptors
C   to the first of these (eigenvalues): view with
C   READ/DESC outtable.TBL *
C . storage is limited only by the Midas Table system, and by the
C   overall system limitations;
C . assumed is: NROW > 7 or NCOLIN (this isn't a restrictive assumption).
C
C.MODIFICATIONS
C
C   New MIDAS interface calls, F. Murtagh August 1988.
C
C-----------------------------------------------------------------------
        PROGRAM PCACIF
C
        CHARACTER*60     NAMEIN, NAMEOUT, TABEVEC
        CHARACTER*6     FORM
        CHARACTER*16    UNIT, LABEL, LABEL2
        INTEGER         MADRID, KUN, KNUL, DTYPE
        INTEGER         NACTV,ISTAT,TID1,NCOLIN,NROW,NSORTC
        INTEGER         NAC,NAR,I,LEN,NSEL,IWPTRIN,IPTRIN
        INTEGER         NTOT,INULL,IACTV,IERR,TID,TID2
        INTEGER         IADD0,IADD1,IADD2,IADD3,IADD4
        INTEGER         IPTROUT,IWPTROUT,IWPTROUT2
        INTEGER         NNCOL,NNROW,NSC1,NAROUT,IEVEC
        INTEGER         NCOLOUT,IOPTION,IPTROUT2,NNAC
        INTEGER         NAROUT2 
        INCLUDE 'MID_INCLUDE:TABLES.INC/NOLIST'
        COMMON /VMR/MADRID(1)
        INCLUDE 'MID_INCLUDE:TABLED.INC/NOLIST'
C
C
C ...   Assuming this is our first action ...
C
        CALL STSPRO('PCACIF')
        DTYPE = D_R4_FORMAT
C
C ...   Get table name as a keyword passed in the command line.
C
        CALL STKRDC('P1',1,1,60,NACTV,NAMEIN,KUN,KNUL,ISTAT)
C
C ...   Read input table.
C
        CALL TBTOPN(NAMEIN,16,TID1,ISTAT)
        CALL TBIGET(TID1,NCOLIN,NROW,NSORTC,NAC,NAR,ISTAT)
C        CALL TBCMAP(TID1,0,IPTRIN,ISTAT)
C
C ...   Some error checking on input.
C
        IF (NROW.LT.1.OR.NCOLIN.LT.1) THEN
           CALL STTPUT(' Nos. of rows/columns are less than 1.',ISTAT)
           CALL STTPUT(' What sort of a table is this ??',ISTAT)
           CALL STTPUT(' Aborting.',ISTAT)
           GOTO 9000
        ENDIF
C
        DO I = 1, NCOLIN
           CALL TBFGET(TID1,I,FORM,LEN,DTYPE,ISTAT)
           IF (DTYPE.NE.D_R4_FORMAT) THEN
              CALL STTPUT(' Illegal format:',ISTAT)
              CALL STTPUT(' Only R*4 column type allowed.',ISTAT)
              CALL STTPUT(' Aborting.',ISTAT)
              GOTO 9000
           ENDIF
        ENDDO

        CALL CHSEL(TID1,NROW,NSEL)
        IF (NSEL.NE.NROW) THEN
           CALL STTPUT(' Not all rows are SELECTed. ',ISTAT)
           CALL STTPUT(' In current implementation, MUST select all.',
     X                                                      ISTAT)
          CALL STTPUT(' Aborting.',ISTAT)
          GOTO 9000
       ENDIF
C
        CALL TDMGET(4*NROW*NCOLIN,IWPTRIN,ISTAT)
        CALL TBCMAP(TID1,1,IPTRIN,ISTAT)
        CALL MAPSM(MADRID(IPTRIN),MADRID(IWPTRIN),
     1              NAR,NROW,NCOLIN) 
        NTOT = NROW*NCOLIN
        CALL CHNULL(MADRID(IWPTRIN),NTOT,INULL)
        IF (INULL.NE.0) THEN
           CALL STTPUT
     X     (' Null entries in the table are not allowed.',ISTAT)
           CALL STTPUT
     X     (' Use SELECT, and then construct another table.',
     X                                                    ISTAT)
          CALL STTPUT(' Aborting.',ISTAT)
          GOTO 9000
       ENDIF
C
C
C ...   Get the output table name and no. cols. via keywords.
C
        CALL STKRDC('P2',1,1,60,IACTV,NAMEOUT,KUN,KNUL,ISTAT)
        CALL STKRDI('INPUTI',1,1,IACTV,IOPTION,KUN,KNUL,ISTAT)
        CALL STKRDI('INPUTI',2,1,IACTV,NCOLOUT,KUN,KNUL,ISTAT)
C
C ...   Now create the output (projections) table.
C
        CALL TBTINI(NAMEOUT,F_TRANS,17,NCOLOUT,NCOLIN,TID,ISTAT)
        FORM = 'G14.6'
        UNIT = ' '
C       (The messing around with LABEL2 which follows is so that
C       leading zeros in a column label appear as such, and not as
C       blanks.)
        LABEL = 'NEW00'
        LABEL2 = '10001'
        DO I = 1, NCOLOUT
           WRITE (LABEL2(1:5),100) I+10000
           LABEL(4:5) = LABEL2(4:5)
           CALL TBCINI(TID,DTYPE,1,FORM,UNIT,LABEL,I,ISTAT)
        ENDDO
  100   FORMAT(I5)
C
        CALL TBIGET(TID,NNCOL,NNROW,NSC1,NAC,NAROUT,ISTAT)
        CALL TDMGET(4*NCOLOUT*NCOLIN,IWPTROUT,ISTAT)
        CALL TBCMAP(TID,1,IPTROUT,ISTAT)
        CALL MAPSM(MADRID(IPTROUT),MADRID(IWPTROUT),
     1              NAROUT,NCOLIN,NCOLOUT)
C
        IEVEC = 0
        CALL STKRDC('INPUTC',1,1,60,IACTV,TABEVEC,KUN,KNUL,ISTAT)
        IF (TABEVEC.NE.'NO') THEN
           IEVEC = 1
           IF (NROW.LT.NCOLIN) THEN
           CALL STTPUT(' Warning.',ISTAT)
           CALL STTPUT
     X     (' Number of rows is usually > number of columns;',ISTAT)
           CALL STTPUT
     X     (' Eigenvector coords. in NCOLIN-dimension space are filed.',
     X                                                          ISTAT)
           CALL STTPUT('  ',ISTAT)
           ENDIF
C
C ...      Create the output (eigenvectors) table.
C
           CALL TBTINI(TABEVEC,F_TRANS,17,NCOLOUT,NCOLIN,TID2,ISTAT)
C          (The messing around with LABEL2 which follows is so that
C          leading zeros in a column label appear as such, and not as
C          blanks.)
           LABEL = 'EVC00'
           LABEL2 = '10001'
           DO I = 1, NCOLOUT
              WRITE (LABEL2(1:5),100) I+10000
              LABEL(4:5) = LABEL2(4:5)
              CALL TBCINI(TID2,DTYPE,1,FORM,UNIT,LABEL,I,ISTAT)
           ENDDO
C
           CALL TBIGET(TID2,NNCOL,NNROW,NSC1,NNAC,NAROUT2,ISTAT)
           CALL TDMGET(4*NCOLOUT*NCOLIN,IWPTROUT2,ISTAT)
           CALL TBCMAP(TID2,1,IPTROUT2,ISTAT)
           CALL MAPSM(MADRID(IPTROUT2),MADRID(IWPTROUT2),
     1                NAROUT2,NCOLIN,NCOLOUT) 
        ENDIF
C

C
C ...   Allocate storage.
C
        CALL GETSTOR(NROW*NCOLIN,IADD0)
        CALL GETSTOR(NCOLIN*NCOLIN,IADD1)
        CALL GETSTOR(NCOLIN*NCOLIN,IADD2)
        CALL GETSTOR(NCOLIN,IADD3)
        CALL GETSTOR(NCOLIN,IADD4)
C
C ...   We now have all the storage we need; APPL will look after
C       a few further odds and ends - e.g. setting a few hard-wired
C       options, rearranging output, etc.  Then it will call the
C       application program.
C
        IERR = 0
        IF (IEVEC.EQ.0) THEN
        CALL APPL1(NROW,NCOLIN,NCOLOUT,MADRID(IWPTRIN),MADRID(IWPTROUT),
     X     MADRID(IADD0),MADRID(IADD1),MADRID(IADD2),MADRID(IADD3),
     X     MADRID(IADD4),IOPTION,IERR)
        CALL MAPBG(MADRID(IWPTROUT),MADRID(IPTROUT),
     1             NAROUT,NCOLIN,NCOLOUT)
        ELSE
        CALL APPL2(NROW,NCOLIN,NCOLOUT,MADRID(IWPTRIN),MADRID(IWPTROUT),
     X     MADRID(IADD0),MADRID(IADD1),MADRID(IADD2),MADRID(IADD3),
     X     MADRID(IADD4),MADRID(IWPTROUT2),IOPTION,IERR)
        CALL MAPBG(MADRID(IWPTROUT),MADRID(IPTROUT),
     1             NAROUT,NCOLIN,NCOLOUT)
        CALL MAPBG(MADRID(IWPTROUT2),MADRID(IPTROUT2),
     1             NAROUT2,NCOLIN,NCOLOUT)
        ENDIF
        IF (IERR.NE.0) THEN
           CALL STTPUT(' IERR not 0 on return from PCA.',ISTAT)
           CALL STTPUT(' Aborting.',ISTAT)
           GOTO 9000
        ENDIF
C
C ...   Write out descriptors with output table.
C
        CALL DSCROUT(NAMEOUT,NROW,NCOLIN,MADRID(IADD3),TID)
C
C ...   Finish up: write output file to disk, and release memory space.
C
        CALL RELSTOR(NROW*NCOLIN,IADD0)
        CALL RELSTOR(NCOLIN*NCOLIN,IADD1)
        CALL RELSTOR(NCOLIN*NCOLIN,IADD2)
        CALL RELSTOR(NCOLIN,IADD3)
        CALL RELSTOR(NCOLIN,IADD4)
C
        CALL TBTCLO(TID1,ISTAT)
C
        CALL TBIPUT(TID,NCOLOUT,NCOLIN,ISTAT)
        CALL TBSINI(TID,ISTAT)
        CALL TBTCLO(TID,ISTAT)
        IF (IEVEC.NE.0) THEN
           CALL TBIPUT(TID2,NCOLOUT,NCOLIN,ISTAT)
           CALL TBSINI(TID2,ISTAT)
           CALL TBTCLO(TID2,ISTAT)
        ENDIF
C
 9000   CONTINUE
        CALL STSEPI
C
        END
C----------------------------------------------------------------------
        SUBROUTINE APPL1(NR,NC1,NC2,AIN,AOUT,ACREA,A1,A2,W1,W2,
     X                  METHOD,IERR)
C
C ...   This is now the application routine - note the setting of
C       parameters, which in a future epoch may be passed if desired
C       on the command line.
C
        INTEGER NR,NC1,NC2,METHOD,IERR,IPRINT,I,J
        REAL*4  AIN(NR,NC1), AOUT(NC1,NC2), ACREA(NR,NC1),A1(NC1,NC1),
     X          A2(NC1,NC1),W1(NC1),W2(NC1)
C
        IPRINT = 3
C
        DO I = 1, NR
           DO J = 1, NC1
              ACREA(I,J) = AIN(I,J)
           ENDDO
        ENDDO
C
        CALL PCA(NR,NC1,ACREA,METHOD,IPRINT,A1,W1,W2,A2,IERR)
C
        IF (IERR.NE.0) RETURN
C
        DO I = 1, NC1
           DO J = 1, NC2
              AOUT(I,J) = A1(I,J)
           ENDDO
        ENDDO
C
        RETURN
        END
C----------------------------------------------------------------------
        SUBROUTINE APPL2(NR,NC1,NC2,AIN,AOUT,ACREA,A1,A2,W1,W2,
     X             EVEC,METHOD,IERR)
C
C ...   This is now the application routine - note the setting of
C       parameters, which in a future epoch may be passed if desired
C       on the command line.
C
        INTEGER NR,NC1,NC2,I,J,IERR,IPRINT,METHOD
        REAL*4  AIN(NR,NC1), AOUT(NC1,NC2), ACREA(NR,NC1),A1(NC1,NC1),
     X          A2(NC1,NC1),W1(NC1),W2(NC1),EVEC(NC1,NC2)
C
        IPRINT = 1
C
        DO I = 1, NR
           DO J = 1, NC1
              ACREA(I,J) = AIN(I,J)
           ENDDO
        ENDDO
C
        CALL PCA(NR,NC1,ACREA,METHOD,IPRINT,A1,W1,W2,A2,IERR)
C
        IF (IERR.NE.0) RETURN
C
        DO I = 1, NC1
           DO J = 1, NC2
              AOUT(I,J) = A1(I,J)
           ENDDO
        ENDDO
C
        DO I = 1, NC1
           DO J = 1, NC2
              EVEC(I,J) = A2(I,NC1-J+1)
           ENDDO
        ENDDO
C
        RETURN
        END
C-------------------------------------------------------------------------
        SUBROUTINE CHNULL(X,NT,NULL)
C ...   Check if null values are present.
        REAL*4  X(NT), TRNULL
        INTEGER TINULL,NULL,NT,I
        DOUBLE PRECISION TDNULL
        CALL TBMNUL(TINULL,TRNULL,TDNULL)
        NULL = 0
        DO I = 1, NT
           IF (X(I).EQ.TRNULL) NULL = NULL + 1
           IF (NULL.GT.0) RETURN
        ENDDO
        RETURN
        END
C-------------------------------------------------------------------------
        SUBROUTINE GETSTOR(NVALS,IPTR)
C ...   Allocate storage space for NVALS real values.
        INTEGER NVALS,IPTR,ISTAT
        CALL TDMGET(4*NVALS,IPTR,ISTAT)
        RETURN
        END
C-------------------------------------------------------------------------
        SUBROUTINE RELSTOR(NVALS,IPTR)
C ...   Release storage space (already allocated) for NVALS real values.
        INTEGER NVALS,IPTR,ISTAT
        CALL TDMFRE(4*NVALS,IPTR,ISTAT)
        RETURN
        END
C---------------------------------------------------------------------
        SUBROUTINE DSCROUT(TAB,N,M,VALS,TID2)
C ...   Output descriptors, with table.
        INTEGER N,M,TID2,I,INDX,MM
        INTEGER KUNIT,ISTAT
        REAL*4 VALS(M),V
        CHARACTER TAB*8, TABNAME*12
C
C ...   The following values are rearranged in decreasing rather
C       than increasing order.
        DO I = 1, M/2
           V = VALS(I)
           VALS(I) = VALS(M-I+1)
           VALS(M-I+1) = V
        ENDDO
C
        INDX = INDEX(TAB//' ',' ')-1
        TABNAME = TAB(1:INDX)//'.TBL'
C
        MM = MIN0(N,M)
        CALL STDWRR(TID2,'EIGENVALUES',VALS,1,MM,KUNIT,ISTAT)
C
        RETURN
        END
