C @(#)arthmy.for	19.2 (ESO-DMD) 05/20/03 09:41:53
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 Massachusetts Ave, Cambridge, 
C MA 02139, USA.
C
C Correspondence 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
      SUBROUTINE ARTHMY(PLADIR,ZDEFA,ZRESFR,ZLINE)
C
C++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
C
C.IDENTIFICATION
C  subroutine ARTHMY			version 1.00	950803
C  K. Banse            		      	ESO - Garching
C
C.KEYWORDS
C  arithmetic operations, bulk data frames
C
C.PURPOSE
C  evaluate an arithmetic expression involving frames, constants and functions
C  and store the result into a data frame or key OUTPUTR(1), if used as pocket calculator
C
C.ALGORITHM
C  "clean" the expression by replacing all frame names by F, all constants by C
C  and all functions by P, convert it to polish (postfix) notation and evaluate it piecewise
C  establish special condition handler for arithmetic traps...
C  copy all descriptors of first frame operand to result frame.
C
C.INPUT/OUTPUT
C  call as ARTHMY(PLADIR,ZDEFA,ZRESFR,ZLINE)
C  
C  input par:
C  PLADIR:	I*4		plane direction 
C				X_Z_PLANE, X_Y_PLANE or Z_Y_PLANE
C  ZDEFA:	char.		2 char. default string
C				(1) Y or N for result frame or not
C  ZRESFR:	char.		name of result frame
C  ZLINE:	char.		char.string with arithmetic expression
C	
C.VERSIONS
C 030519	last modif
C	
C-------------------------------------------------------------------------
C
      IMPLICIT NONE
C
C  important parameter: WORKLM = 45
C
      CHARACTER*(*)  ZDEFA,ZRESFR,ZLINE
      CHARACTER      INFRAM(23)*80                   !MAXFRAM = WORKLM/2 + 1
      CHARACTER      ATOM(23)*80,OPER(23)*5          !size = MAXFRAM
      CHARACTER      RESFRA*80,DSCFRA*80
      CHARACTER      CUNIT*80,IDENT*72
      CHARACTER      WORK*50,WORK1*50,OUTPU*80,WORKY*1
      CHARACTER      ERROR3*50,ERROR4*40
C      
      REAL         CONST,CUTS(4),USRNUL
      REAL         DATTA(23),RDUM(1)                 !size = MAXFRAM
C 
      DOUBLE PRECISION STEP(3),START(3),DDUM(1)
C      
      INTEGER      NULCNT,NNN
      INTEGER      LATOM(23),WORKLM                  !size = MAXFRAM
      INTEGER      APNTRS(48)                        !size = WORKLM + 3
      INTEGER*8    PNTR(27)                          !size = MAXFRAM + 4
      INTEGER*8    XPNTR,RPNTR
      INTEGER      IMNO(23),IPLANE(23)               !size = MAXFRAM
      INTEGER      DIM3(23),PIDX(23),PPP(23)         !size = MAXFRAM
      INTEGER      PLADIR,OPLANE,PLINDX
      INTEGER      FRACNT,NPIX(3),NPIXA(3),YPIX(2)
      INTEGER      COUNT,IAV,K,LL,DIM1ON
      INTEGER      N,NAXIS,NAXISA,TOTAL
      INTEGER      SIZE,INFO(5),DATFMT
      INTEGER      MAPSIZ,STAT
      INTEGER      NNULL,UNIT(1),INPU(1),OPCNT
      INTEGER      RIMNO,WIMNO,MADRID(1)
      INTEGER      UPDA,DSCORG,XIMNO
C 
      INCLUDE  'MID_INCLUDE:ST_DEF.INC'
C      
      COMMON      /NULCOM/ NULCNT,USRNUL
      COMMON      /IPOOL/  FRACNT
      COMMON      /VMR/ MADRID
C 
      DATA      WORKLM      /45/
      DATA      CUNIT       /' '/,  IDENT       /' '/
      DATA      ERROR3
     +          /'wrong syntax in arithmetic expression ...'/
      DATA      ERROR4      /'too many operands...'/
C      
      INCLUDE  'MID_INCLUDE:ST_DAT.INC'
C      
C  init variables
      TOTAL = 0
      MAPSIZ = 0
      DIM1ON = 0
      CUTS(1) = 0.
      CUTS(2) = 0.
      CUTS(3) = + 999999.9
      CUTS(4) = - 999999.9
C 
      DO 130, N=1,23
         DATTA(N) = 0.0
130   CONTINUE
      DO 140, N=1,48
         APNTRS(N) = -1
140   CONTINUE
      DO 150, N=1,3
         NPIX(N) = 1
         START(N) = 0.D0
         STEP(N) = 1.D0
150   CONTINUE
C 
C  reduce expression... (clean up + fill ATOM, LATOM)
200   SIZE = 80                                !length of ATOM(i) strings
      CALL EXPCLE(ZLINE,WORK,SIZE,COUNT,ATOM,LATOM)      
      IF (COUNT.EQ.-1) THEN
         CALL STETER(1,ERROR3)     
      ELSE IF (COUNT.EQ.-2) THEN
         CALL STETER(5,'arithmetic expression too long...')
C      
C  take care of operand without operation   (extend F or C to F+C or C+C)
      ELSE IF (COUNT.EQ.1) THEN    
         ATOM(2) = '0.'    
         LATOM(2) = 2
         WORK(2:3) = '+C'
         COUNT = 2
      ENDIF
C      
C  convert cleaned algebraic expression to polish postfix
      ZLINE(1:) = ' '
      CALL EXPPOL(WORK,ZLINE,STAT)                 
      IF (STAT.NE.0) CALL STETER(2,ERROR3)     
      LL = INDEX(ZLINE,' ') - 1
      IF (LL.GT.WORKLM) 
     +   CALL STETER(8,'Too many operands - reduce expression...')
C      
C  fill pointers for atoms
      DATFMT = D_R4_FORMAT                   !that's what we use currently
      K = 1
      DO 220, N=1,LL
         WORKY = ZLINE(N:N)
         IF ( (WORKY.EQ.'C').OR. (WORKY.EQ.'F').OR.
     +        (WORKY.EQ.'Q').OR. (WORKY.EQ.'P') ) THEN
            APNTRS(N) = K
            K = K + 1
         ENDIF
220   CONTINUE
C 
C  check for descriptor-origin-frame (other than 1. frame)
C  but be careful because of frame[.. , ..:.. , ..] syntax
C 
      DSCFRA(1:) = ' '
      LL = 0
      K = INDEX(ZRESFR,']')
      IF (K.GT.1) THEN                          !abc[...],descframe possible
         K = K + 1
         IF (ZRESFR(K:K).EQ.',') LL = K
      ELSE
         LL = INDEX(ZRESFR,',')                 !abc,descframe possible
      ENDIF
      IF (LL.GT.1) THEN
         OUTPU(1:) = ZRESFR(LL+1:)
         CALL CLNFRA(OUTPU,DSCFRA,0)
         ZRESFR(LL:) = ' '
      ENDIF
      CALL CLNFRA(ZRESFR,RESFRA,0)
C      
C  find frames + constants
      LL = 0                        !counter in ATOM
      K = 0
C 
      DO 250, N=1,WORKLM
         WORKY = WORK(N:N)
         IF (WORKY.EQ.' ') GOTO 300
C 
         IF (WORKY.EQ.'F') THEN
            K = K + 1                   !not more than 23 frames possible
            LL = LL + 1
            CALL CLNFRA(ATOM(LL),INFRAM(K),0)
            PIDX(LL) = K               !we'll use PIDX instead of ATOM later 
C      
         ELSE IF (WORKY.EQ.'C') THEN
            LL = LL + 1                !we'll use DATTA instead of ATOM later 
            CALL UPCAS(ATOM(LL),ATOM(LL))
            STAT = INDEX(ATOM(LL),'D')
            IF (STAT.GT.1) THEN
               DATFMT = D_R8_FORMAT     !explicit double constant...
               CALL GENCNV(ATOM(LL),4,1,INPU,RDUM,DDUM,STAT)
               DATTA(LL) = SNGL(DDUM(1))
            ELSE
               CALL GENCNV(ATOM(LL),2,1,INPU,RDUM,DDUM,STAT)
               DATTA(LL) = RDUM(1)
            ENDIF
            IF (STAT.LE.0) CALL STETER
     +         (10,'conversion error of ASCII -> number ...')
C
         ELSE IF ((WORKY.EQ.'P').OR.(WORKY.EQ.'Q')) THEN
            LL = LL + 1
         ENDIF
250   CONTINUE
C      
C  extract 1-arg, 2-arg operations and save them (with offsets)
300   WORK1(1:) = ZLINE(1:)
      LL = (WORKLM+1) / 2
      DO 350, N=1,999
         CALL EXPRDC(WORK1,WORK,OPER(N),PPP(N))
         IF (PPP(N).LT.1) CALL STETER(1,ERROR3)
         IF (WORK(2:2).NE.' ') THEN
            WORK1 = WORK
         ELSE
            IF (N.GT.LL) CALL STETER
     +         (11,'too many operations in expression - split it up...')
            OPCNT = N 
            GOTO 1000
         ENDIF
350   CONTINUE
C      
C  if no frames involved ... do it right now + skip the loop
C      
1000  IF (K.LE.0) THEN
         CALL COMPUA(PNTR,OPER,PPP,OPCNT,APNTRS,ATOM,DATTA,PIDX,
     +                     COUNT,NPIX(1),RPNTR,CUTS(3))
         CONST = CUTS(3)
         IF (ZDEFA(1:1).EQ.'N') THEN    !put resulting constant into OUTPUTR(1)
            CALL STKWRR('OUTPUTR',CONST,1,1,UNIT,STAT)
            WRITE(OUTPU,10000) CONST
            CALL STTPUT(OUTPU,STAT)
         ELSE
            CALL STIGET(RESFRA,D_R4_FORMAT,F_IO_MODE,F_IMA_TYPE,
     +                  3,NAXIS,NPIX,START,STEP,IDENT,
     +                  CUNIT,RPNTR,RIMNO,STAT)
            SIZE = NPIX(1) * NPIX(2) * NPIX(3)
            CALL OPFC('FC=',0.0,CONST,MADRID(RPNTR),SIZE,
     +                USRNUL,NNN)                  !copy constant to frame
            CUTS(4) = CONST
         ENDIF
         GOTO 9000
      ENDIF
C      
C  at least one frame involved ...
C      
      FRACNT = K
      CALL ISSUBF(RESFRA,K)
      IF (K.GT.0) THEN
         UPDA = 1
      ELSE
         UPDA = 0
      ENDIF
      IF (DSCFRA(1:1).NE.' ') THEN
         DSCORG = 0
      ELSE
         DSCORG = 1
      ENDIF
C 
      DO 1200, N=1,FRACNT
         CALL STFINF(INFRAM(N),2,INFO,STAT)
         IF (INFO(2).EQ.D_R8_FORMAT) DATFMT = D_R8_FORMAT
         CALL STFOPN(INFRAM(N),D_R4_FORMAT,0,F_IMA_TYPE,IMNO(N),STAT)
         IF (UPDA.EQ.0) THEN
            CALL GENEQF(INFRAM(N),RESFRA,UPDA)       !test, if frames are equal
            IF (UPDA.NE.0) UPDA = N                  !Yes. Save the index
         ENDIF
         IF (DSCORG.EQ.0)  THEN
            CALL GENEQF(INFRAM(N),DSCFRA,LL)
            IF (LL.NE.0) DSCORG = N
         ENDIF
1200  CONTINUE
C 
      IF (PLADIR.EQ.X_Y_PLANE) THEN
         PLINDX = 3                              !index of `looping' plane 
      ELSE IF (PLADIR.EQ.X_Z_PLANE) THEN
         PLINDX = 2
      ELSE
         PLINDX = 1
      ENDIF
C 
C  we use R*4 data currently
      IF (DATFMT.EQ.D_R8_FORMAT) THEN
         CUNIT(1:) = 
     +   'Warning: double operand found - result will be single! '
         CALL STTPUT(CUNIT,STAT)
         DATFMT = D_R4_FORMAT
      ENDIF
C 
C  work on first file operand 
      CALL STDRDI(IMNO(1),'NAXIS',1,1,IAV,INPU,UNIT,NNULL,STAT)
      NAXIS = INPU(1)
      CALL STDRDI(IMNO(1),'NPIX',1,NAXIS,IAV,NPIX,UNIT,NNULL,STAT)
      IF (NAXIS.LT.2) THEN
         DIM3(1) = -1
         DIM1ON = 1                        !so we know later on...
      ELSE IF (NAXIS.EQ.2) THEN
         IPLANE(1) = X_Y_PLANE
         TOTAL = 1
         DIM3(1) = 1
         MAPSIZ = NPIX(1) * NPIX(2)
      ELSE
         IPLANE(1) = PLADIR
         TOTAL = NPIX(PLINDX)
         DIM3(1) = TOTAL
         SIZE = NPIX(1) * NPIX(2) * NPIX(3)
         MAPSIZ = SIZE / NPIX(PLINDX)
         CALL STDRDD(IMNO(1),'START',1,NAXIS,IAV,START,UNIT,NNULL,STAT)
         CALL STDRDD(IMNO(1),'STEP',1,NAXIS,IAV,STEP,UNIT,NNULL,STAT)
         CALL STDRDC(IMNO(1),'IDENT',1,1,72,IAV,IDENT,UNIT,NNULL,STAT)
         CALL STDRDC(IMNO(1),'CUNIT',1,1,64,IAV,CUNIT,UNIT,NNULL,STAT)
         IF (DSCORG.EQ.0) DSCORG = 1
      ENDIF
C 
C  loop over all file operands
      IF (FRACNT.GT.1) THEN
         DO 1500, N=2,FRACNT
            CALL STDRDI(IMNO(N),'NAXIS',1,1,IAV,NAXISA,UNIT,NNULL,STAT)
            CALL 
     +      STDRDI(IMNO(N),'NPIX',1,NAXISA,IAV,NPIXA,UNIT,NNULL,STAT)
            IF (NAXISA.LT.2) THEN
               DIM3(N) = -1
               DIM1ON = 1                        !so we know later on...
               GOTO 1500                         !avoid size check
            ELSE IF (NAXISA.EQ.2) THEN
               IPLANE(N) = X_Y_PLANE
               DIM3(N) = 1
               K = NPIXA(1) * NPIXA(2)
            ELSE
               IPLANE(N) = PLADIR
               DIM3(N) = NPIXA(PLINDX)
               IF (TOTAL.LE.1) THEN
                  TOTAL = DIM3(N)
                  NPIX(1) = NPIXA(1)
                  NPIX(2) = NPIXA(2)
                  NPIX(3) = NPIXA(3)
                  SIZE = NPIX(1) * NPIX(2) * NPIX(3)
                  CALL STDRDD(IMNO(N),'STEP',1,NAXIS,IAV,START,UNIT,
     +                        NNULL,STAT)
                  CALL STDRDD(IMNO(N),'STEP',1,NAXIS,IAV,STEP,UNIT,
     +                        NNULL,STAT)
                  CALL STDRDC(IMNO(N),'IDENT',1,1,72,IAV,IDENT,UNIT,
     +                        NNULL,STAT)
                  CALL STDRDC(IMNO(N),'CUNIT',1,1,64,IAV,CUNIT,UNIT,
     +                        NNULL,STAT)
                  IF (DSCORG.EQ.0) DSCORG = N
               ELSE IF (DIM3(N).NE.TOTAL) THEN
                  CALL STETER (67,
     +            'No. of planes must be the same for all frames')
               ENDIF
               K = (NPIXA(1) * NPIXA(2) * NPIXA(3)) / NPIXA(PLINDX)
            ENDIF
            IF (MAPSIZ.EQ.0) THEN
               MAPSIZ = K
            ELSE
               IF (K.NE.MAPSIZ) THEN
                  CALL STETER(66,
     +            'All relevant planes must have equal size')
               ENDIF
            ENDIF
1500     CONTINUE
      ENDIF
C      
      YPIX(1) = NPIX(1)                  !maybe we need it 
      YPIX(2) = NPIX(2)
      IF (TOTAL.EQ.0) THEN               !only 1-dim frames involved
         CALL STETER(68,
     +   'Only 1-dim files involved - use COMPUTE/IMAGE instead.')
      ENDIF
      IF (TOTAL.EQ.1) THEN               !only 2-dim frames involved
         SIZE = NPIX(1) * NPIX(2)
         OPLANE = X_Y_PLANE
         NAXIS = 2
      ELSE
         OPLANE = PLADIR
         NAXIS = 3
         IF (DIM1ON.EQ.1) THEN           !we have 1-dime operands
            IF (OPLANE.EQ.X_Z_PLANE) THEN
               YPIX(2) = NPIX(3)
            ELSE IF (OPLANE.EQ.Z_Y_PLANE) THEN
               YPIX(1) = NPIX(3)
               YPIX(2) = NPIX(2)
            ENDIF                        !YPIX for X_Y_PLANE already set
         ENDIF 
      ENDIF
C 
C  for 1-dim operands we need one more dimension check
      IF (DIM1ON.EQ.1) THEN
         DO 2000, N=1,FRACNT
            IF (DIM3(N).EQ.-1) THEN
               CALL STDRDI(IMNO(N),'NPIX',1,1,IAV,NPIXA,UNIT,
     +                     NNULL,STAT)
               IF (NPIXA(1).NE.YPIX(1)) THEN
                  CALL STETER(69,
     +       '1-dim frames must have same size as first plane dim.')
               ENDIF
            ENDIF
2000     CONTINUE
C 
         CALL STFCRE('Work1',D_R4_FORMAT,F_X_MODE,F_IMA_TYPE,
     +               YPIX(1),XIMNO,STAT)
         CALL STFMAP(XIMNO,F_X_MODE,1,YPIX(1),IAV,XPNTR,STAT)
      ENDIF
C      
C  if no frame is equal to result frame, we create it
      IF (UPDA.EQ.0) THEN
         CALL STFCRE(RESFRA,DATFMT,F_O_MODE,F_IMA_TYPE,
     +               SIZE,RIMNO,STAT)
         INPU(1) = NAXIS
         CALL STDWRI(RIMNO,'NAXIS',INPU,1,1,UNIT,STAT)
         CALL STDWRI(RIMNO,'NPIX',NPIX,1,NAXIS,UNIT,STAT)
         CALL STDWRD(RIMNO,'START',START,1,NAXIS,UNIT,STAT)
         CALL STDWRD(RIMNO,'STEP',STEP,1,NAXIS,UNIT,STAT)
         CALL STDWRC(RIMNO,'IDENT',1,IDENT,1,72,UNIT,STAT)
         N = (NAXIS+1)*16
         CALL STDWRC(RIMNO,'CUNIT',1,CUNIT,1,N,UNIT,STAT)
      ELSE
         IF (IPLANE(UPDA).NE.OPLANE) THEN
            CALL STETER(69,
     +  'We cannot update result frame with different plane directions')
         ENDIF
         CALL STFOPN(RESFRA,DATFMT,0,F_IMA_TYPE,RIMNO,STAT)
         CALL STFINF(RESFRA,2,INFO,STAT)
         IF ( (INFO(2).NE.D_R8_FORMAT) .AND.
     +        (INFO(2).NE.D_R4_FORMAT) ) THEN
            CUNIT(1:) = 
     +      'Warning: Result frame is not real - '//
     +      'you may get truncation errors... '
            CALL STTPUT(CUNIT,STAT)
         ENDIF
      ENDIF
C 
C  map virtual memory for input files + result frame
      K = (FRACNT + 5) * MAPSIZ
      CALL STFCRE('Work2',D_R4_FORMAT,F_X_MODE,F_IMA_TYPE,
     +            K,WIMNO,STAT)
      CALL STFMAP(WIMNO,F_X_MODE,1,K,IAV,PNTR(1),STAT)
      IF (FRACNT.GT.1) THEN
         DO 5200, N=2,FRACNT                  !no more pointers,
            PNTR(N) = PNTR(N-1) + MAPSIZ      !but indices of MADRID ...
5200     CONTINUE
      ENDIF
C 
      RPNTR = PNTR(FRACNT) + MAPSIZ            !result pointer
C 
C  get pointers for 4 working chunks
      DO 5250, N=1,4
         PNTR(N+23) = RPNTR + (N*MAPSIZ)
5250  CONTINUE
C      
C  loop over all planes
C
      DO 5800, N=1,TOTAL
         DO 5600, K=1,FRACNT
            IF (DIM3(K).GE.N) THEN
               CALL STPGET(IMNO(K),IPLANE(K),N,MADRID(PNTR(K)),STAT)
            ELSE IF (DIM3(K).EQ.-1) THEN           !build 2-dim frame
               CALL STFGET(IMNO(K),1,YPIX(1),IAV,MADRID(XPNTR),STAT)
               CALL GROWIT('L',YPIX,MADRID(XPNTR),YPIX,
     +                     MADRID(PNTR(K)),1)
               DIM3(K) = 1
            ENDIF
5600     CONTINUE
C      
         CALL COMPUA(PNTR,OPER,PPP,OPCNT,APNTRS,ATOM,DATTA,PIDX,
     +               COUNT,MAPSIZ,RPNTR,CUTS(3))
C      
         CALL STPPUT(RIMNO,OPLANE,N,MADRID(RPNTR),STAT)
5800  CONTINUE
C  
C  ***
C  we're done - set dynamic range of result frame
C  ***
C      
C  take care of descriptors
8000  IF (FRACNT.GE.1) THEN
         CALL DSCUPT(IMNO(DSCORG),RIMNO,' ',STAT)
      ELSE
         CALL DSCUPT(RIMNO,RIMNO,' ',STAT)
      ENDIF
      CALL STDWRR(RIMNO,'LHCUTS',CUTS,1,4,UNIT,STAT)
C      
C  That's it folks...
9000  RETURN
C
C  formats...
10000 FORMAT(G15.7)
      END
