	SUBROUTINE RECALC
C COPYRIGHT (C) 1983 GLENN EVERHART
C PERMISSION IS GIVEN TO ANYONE TO USE, DISTRIBUTE, OR COPY THIS
C PROGRAM FREELY BUT NOT TO SELL IT COMMERICALLY.
C RECALCULATE COMMAND
C RECOMPUTE ALL ELEMENTS OF SPREADSHEET WHERE VALID.
C
	INCLUDE 'VKLUGPRM.FTN'
	PARAMETER CUP=1,EL=12
C	PARAMETER RRW = 32
C	PARAMETER RCL = 32 ! REAL ROWS, COLS
C	PARAMETER DRW = 8
C	PARAMETER DCL = 8 ! DISPLAY MAX ROWS, COLS.
C	PARAMETER RRCL = 1024
C PARAMETER RRCL=RRW*RCL
C NOTE: THROUGHOUT, ROWS ARE ACTUALLY DOWN, COLUMNS ACROSS ON
C SCREEN. ROW 0 IN DISPLAY IS THE 27 ACCUMULATORS A-Z AND %, WITH
C % BEING THE LAST-COMPUTED VALUE FROM THE CALC PROGRAM, WHICH
C KNOWS HOW TO ACCESS THE DATA BUT IS JUST PASSED COMMAND STRINGS
C FROM THE DISK BASED FILE HERE.
	LOGICAL*1 FORM,FVLD,CMDLIN(132)
	INTEGER*4 VNLT
	INTEGER*2 FORMFG,RCFGX,PZAP,RCONE
	COMMON/FFGG/FORMFG,RCFGX,PZAP,RCONE
	DIMENSION FORM(128),FVLD(RRW,RCL)
	COMMON/FVLDC/FVLD
C FVLD FLAG 0 = NO FORMULA, -1= DISPLAY FORMULA ITSELF, NOT VALUE
C 1=VALID ACTIVE FORMULA THERE TO EVALUATE. INITIALLY ALL 0'S
C SO INITIALLY IGNORE.
C FVLD=-2 OR -3 = DISPLAY FORMULA
C FVLD=3 NUMERIC, COMPUTE ONCE THEN SET FVLD TO 2
C FVLD=2 NUMERIC CONSTANT, ALREADY COMPUTED... DO NOT RECOMPUTE.
C
C ROUTINE IN2AS COMPUTES ASCII CHARACTER NAMES OF SUBSCRIPTS IN1,IN2
C SO DISPLAY CAN HAVE THEM. IT MUST BE THE INVERSE OF VARSCN.
	INTEGER*2 PROW,PCOL,DROW,DCOL,DRWV,DCLV
	COMMON/DCTL/PROW,PCOL,DROW,DCOL,DRWV,DCLV
	DIMENSION NRDSP(DRW,DCL),NCDSP(DRW,DCL)
	COMMON/D2R/NRDSP,NCDSP
	INTEGER*2 TYPE(RRW,RCL),VLEN(9)
	LOGICAL*1 AVBLS(100,27),VBLS(8,RRW,RCL)
	INTEGER*2 KDRW,KDCL
	COMMON /DOT/KDRW,KDCL
	COMMON/V/TYPE,AVBLS,VBLS,VLEN
	INTEGER*2 PRS,PCS,DRS,DCS
	PRS=PROW
	PCS=PCOL
	DRS=DROW
	DCS=DCOL
C THE FOLLOWING 2 LOOPS DEFINE ORDER OF CALCULATION.
C HERE THIS IS: OUTER LOOP ON ROWS (ACROSS), INNER LOOP ON COLUMNS (DOWN).
	DO 1 N2=1,RCL
	DO 2 N1=1,RRW
	IRRX=(N2-1)*RRW+N1
	IF (FVLD(N1,N2).LE.0) GOTO 2
C IF CONSTANT WAS COMPUTED ALREADY, NO NEED TO RECOMPUTE. SKIP IT.
C NOTE: WE MUST ALWAYS RECOMPUTE IF R COMMAND WAS GIVEN...
	IF ((RCONE.EQ.0).AND.(FVLD(N1,N2).EQ.2)) GOTO 2
	KDRW=N1
	KDCL=N2
	PROW=N1
	PCOL=N2
C SEE IF THIS PHYS COL HAS A DISPLAY COL. AND IF SO SET THAT UP.
	DO 10 M1=1,DRW
	DO 20 M2=1,DCL
	M1X=M1
	M2X=M2
	IF(NRDSP(M1,M2).EQ.N1.AND.NCDSP(M1,M2).EQ.N2)GOTO 9
20	CONTINUE
10	CONTINUE
9	CONTINUE
C IF NO DISPLAY ROW, LEAVE AT LOW RIGHT...
C USE SAVED VALUES SO WE DON'T RELY ON DO LOOP INDEX AFTER LOOP END.
	DROW=M1X
	DCOL=M2X
	READ(7'IRRX)FORM
C NOW HAVE THE FORMULA LINE. PASS TO DOENTRY TO HANDLE IT.
	LLST=110
	LFST=1
	FORM(111)=0
	IF(FORM(118).NE.15)GOTO 2
	CALL DOENTR(FORM,LFST,LLST)
C IF WE JUST COMPUTED A CONSTANT, FLAG IT COMPUTED AND SKIP IT.
	IF(FVLD(N1,N2).EQ.3)FVLD(N1,N2)=2
2	CONTINUE
1	CONTINUE
	PROW=PRS
	PCOL=PCS
	DROW=DRS
	DCOL=DCOL
C FORCE FUNCTION WORKS ONCE ONLY.
	RCONE=0
	RETURN
	END
	SUBROUTINE DOENTR(FORM,LOW,LHIGH)
C +++++++++++++++++++++++++++++++++++
	INCLUDE 'VKLUGPRM.FTN'
	PARAMETER CUP=1,EL=12
C	PARAMETER RRW = 32
C	PARAMETER RCL = 32 ! REAL ROWS, COLS
C	PARAMETER DRW = 8
C	PARAMETER DCL = 8 ! DISPLAY MAX ROWS, COLS.
C	PARAMETER RRCL = 1024
C PARAMETER RRCL=RRW*RCL
	LOGICAL*1 FORM,FVLD,CMDLIN(132)
	INTEGER*4 VNLT
	DIMENSION FORM(128),FVLD(RRW,RCL)
C FVLD FLAG 0 = NO FORMULA, -1= DISPLAY FORMULA ITSELF, NOT VALUE
C 1=VALID ACTIVE FORMULA THERE TO EVALUATE. INITIALLY ALL 0'S
C SO INITIALLY IGNORE.
	INTEGER*2 PROW,PCOL,DROW,DCOL,DRWV,DCLV
	COMMON/DCTL/PROW,PCOL,DROW,DCOL,DRWV,DCLV
	DIMENSION NRDSP(DRW,DCL),NCDSP(DRW,DCL)
	COMMON/D2R/NRDSP,NCDSP
	INTEGER*2 TYPE(RRW,RCL),VLEN(9)
	LOGICAL*1 AVBLS(100,27),VBLS(8,RRW,RCL)
	COMMON/V/TYPE,AVBLS,VBLS,VLEN
	COMMON/FVLDC/FVLD
C +++++++++++++++++++++++++++++++++++
	LCURR=LOW
C DO AN ENTRY. MUST SCAN FOR MULTIPLE STATEMENTS PER LINE AND ALSO
C RECOGNIZE FUNCTION NAMES.
1000	CONTINUE
	LSL=INDEX(FORM(LCURR),'\')
	IF(LSL.EQ.0)LSL=LHIGH
C CLAMP AT 80 CHARS LONG INPUT.
	IF(LSL.LE.79)GOTO 1200
C STMT HAS NO MULTIPLES. SQUASH IT TO USE ONLY 1ST PART...
	LSL=79
	LCURR=LHIGH
	FORM(80)=0
1200	CONTINUE
	CALL DOSTMT(FORM(LCURR),LSL)
	IF (LCURR.GE.LHIGH)RETURN
	LCURR=LCURR+LSL
	GOTO 1000
	END
	SUBROUTINE DOSTMT(LINE,LLAST)
C HANDLE 1 STATEMENT PARSING (DOES A BIT MORE OF THE WORK WITH THE
C PART OF THE LINE STRIPPED TO HAVE EXACTLY ONE COMMAND IN IT.
	LOGICAL*1 LINE(110)
C +++++++++++++++++++++++++++++++++++
	INCLUDE 'VKLUGPRM.FTN'
	PARAMETER CUP=1,EL=12
C	PARAMETER RRW = 32
C	PARAMETER RCL = 32 ! REAL ROWS, COLS
C	PARAMETER DRW = 8
C	PARAMETER DCL = 8 ! DISPLAY MAX ROWS, COLS.
C	PARAMETER RRCL = 1024
C PARAMETER RRCL=RRW*RCL
	LOGICAL*1 FORM,FVLD,CMDLIN(132)
	INTEGER*4 VNLT
	DIMENSION FORM(128),FVLD(RRW,RCL)
C FVLD FLAG 0 = NO FORMULA, -1= DISPLAY FORMULA ITSELF, NOT VALUE
C 1=VALID ACTIVE FORMULA THERE TO EVALUATE. INITIALLY ALL 0'S
C SO INITIALLY IGNORE.
	COMMON/FVLDC/FVLD
	INTEGER*2 PROW,PCOL,DROW,DCOL,DRWV,DCLV
	COMMON/DCTL/PROW,PCOL,DROW,DCOL,DRWV,DCLV
	DIMENSION NRDSP(DRW,DCL),NCDSP(DRW,DCL)
	COMMON/D2R/NRDSP,NCDSP
	INTEGER*2 TYPE(RRW,RCL),VLEN(9)
	REAL*8 XVBLS(RRW,RCL)
	LOGICAL*1 AVBLS(100,27),VBLS(8,RRW,RCL)
	INTEGER*4 JVBLS(2,RRW,RCL)
	EQUIVALENCE(JVBLS(1,1,1),XVBLS(1,1))
	EQUIVALENCE(VBLS(1,1,1),XVBLS(1,1))
	COMMON/V/TYPE,AVBLS,VBLS,VLEN
	REAL*8 ACX,ACY
	EQUIVALENCE(ACY,AVBLS(1,27))
	INTEGER*2 KDRW,KDCL
	COMMON /DOT/KDRW,KDCL
	LOGICAL*1 ILINE(106)
	INTEGER*2 ILNFG,ILNCT
	COMMON/ILN/ILNFG,ILNCT,ILINE
C +++++++++++++++++++++++++++++++++++
	CALL FNAME(LINE,LLAST,INDEXF)
C ABOVE GETS FUNCTION NAMES.
C	NAME	INDEXF
C	MIN	1
C	MAX	2
C	AVG	3
C	SUM	4
C	STD	5	(STD DEVIATION)
C	IF	6	(IF STMT)
C USE [ AND ] TO DELIMIT FUNCTION ARGS.
	IF(INDEXF.LT.1.OR.INDEXF.GT.6)GOTO 1000
C HERE IF A FUNCTION OR AN IF STMT (FORMAT= IF [varRELvar]stmt|else-stmt)
	LLB=INDEX(LINE,'[')
	LRB=INDEX(LINE,']')
C *** ERROR WITH FORMAT -- NO [ SEEN IN TIME. JUST IGNORE IT.
	IF(LLB.GT.LLAST)RETURN
	IF(LRB.GT.LLAST)LRB=LLAST
	IF(INDEXF.EQ.6)GOTO 2000
C ISOLATE MATH FUNCTIONS
	CALL DOMFCN(LINE,LLB,LRB,INDEXF,ACX)
C GET % ABOVE
	IF(ABS(TYPE(KDRW,KDCL)).NE.2)GOTO 1760
	XVBLS(KDRW,KDCL)=ACX
C LEAVE RESULT IN % TOO.
	ACY=ACX
	TYPE(1,27)=TYPE(KDRW,KDCL)
	RETURN
1760	JVBLS(1,KDRW,KDCL)=ACX
	RETURN
2000	CONTINUE
C HANDLE AN "IF" STATEMENT
	CALL DOIF(LINE,LLB,LRB,LLAST)
C PASS LLAST TO DOIF SINCE WE DON'T EXPECT ] AS LAST CHAR OF STMT.
C NO DIRECT SET OF VRBL HERE...
	RETURN
1000	CONTINUE
C HERE JUST HAVE SOMETHING TO PASS TO CALC. DO SO.
	ILNFG=1
	LMX=LLAST-1
	DO 1001 N1=1,LMX
1001	ILINE(N1)=LINE(N1)
	ILNCT=LMX
C PROTECT CALC FROM ANY PART OF A LINE LONGER THAN 80 CHARS (ITS MAX)
	IF(ILNCT.GT.80)ILNCT=80
	CALL CALC
C STORE EXPRESSION RESULT.
	XVBLS(KDRW,KDCL)=ACY
	RETURN
	END
	SUBROUTINE FNAME(LINE,LLAST,INDEXF)
C RETURN FUNCTION NAME IF ANY
	LOGICAL*1 LINE(110)
	INTEGER*4 FNAM(6)
	LOGICAL*1 FCHNM(4,6)
	EQUIVALENCE(FNAM(1),FCHNM(1,1))
	DATA FNAM/'MIN ','MAX ','AVG ','SUM ','STD ','IF  '/
	INDEXF=0
	DO 1 N1=1,6
	DO 2 N2=1,3
	IF(LINE(N2).NE.FCHNM(N2,N1))GOTO 1
2	CONTINUE
C IF WE FALL THROUGH, WE HAVE A VALID FCN NAME INDEX IN INDEXF
	INDEXF=N1
	GOTO 3
1	CONTINUE
3	CONTINUE
	RETURN
	END
	SUBROUTINE TEST(LOGTYP,FLAG,V1,V2)
	INTEGER*2 FLAG,V1,V2
	FLAG=0
	IF(LOGTYP.EQ.1.AND.V1.GT.V2)FLAG=1
	IF(LOGTYP.EQ.2.AND.V1.LT.V2)FLAG=1
	IF(LOGTYP.EQ.3.AND.V1.EQ.V2)FLAG=1
	IF(LOGTYP.EQ.4.AND.V1.NE.V2)FLAG=1
	IF(LOGTYP.EQ.5.AND.V1.GE.V2)FLAG=1
	IF(LOGTYP.EQ.6.AND.V1.LE.V2)FLAG=1
C TEST LOGICAL RELATIONS FOR IF STATEMENT, FLAG=1 IF TRUE, 0 ELSE.
	RETURN
	END
	SUBROUTINE MTHINI(INDEXF,AC,SS,CTR,ACX)
	REAL*8 AC,SS,CTR,ACX
	SS=0.
	CTR=0.
	ACX=0.
	AC=0.
	IF(INDEXF.EQ.1)AC=1.E20
	IF(INDEXF.EQ.2)AC=-1.E20
	RETURN
	END
	SUBROUTINE DOMATH(INDEXF,VAR,AC,SS,CTR,ACX)
	REAL*8 AC,SS,CTR,ACX
	REAL*8 VAR
	IF(INDEXF.NE.1)GOTO 100
	IF(VAR.LT.AC)AC=VAR
	ACX=AC
100	IF(INDEXF.NE.2)GOTO 200
	IF(VAR.GT.AC)AC=VAR
	ACX=AC
200	IF(INDEXF.NE.3)GOTO 300
	AC=AC+VAR
	CTR=CTR+1.
	ACX=AC/CTR
300	IF(INDEXF.NE.4)GOTO 400
	AC=AC+VAR
	ACX=AC
400	IF(INDEXF.NE.5)GOTO 500
	AC=AC+VAR
	SS=SS+(VAR*VAR)
	CTR=CTR+1.
	ACX=(SS-((AC*AC)/CTR))/CTR
500	CONTINUE
	RETURN
	END
	SUBROUTINE DOMFCN(LINE,LLB,LRB,INDEXF,ACX)
C LLB = LOC OF [
C LRB = LOC OF ]
C INDEXF IS AS ABOVE. GUARANTEED IN RANGE 1-5.
	LOGICAL*1 LINE(110)
C +++++++++++++++++++++++++++++++++++
	INCLUDE 'VKLUGPRM.FTN'
	PARAMETER CUP=1,EL=12
C	PARAMETER RRW = 32
C	PARAMETER RCL = 32 ! REAL ROWS, COLS
C	PARAMETER DRW = 8
C	PARAMETER DCL = 8 ! DISPLAY MAX ROWS, COLS.
C	PARAMETER RRCL = 1024
C PARAMETER RRCL=RRW*RCL
	LOGICAL*1 FORM,FVLD,CMDLIN(132)
	INTEGER*4 VNLT
	DIMENSION FORM(128),FVLD(RRW,RCL)
C FVLD FLAG 0 = NO FORMULA, -1= DISPLAY FORMULA ITSELF, NOT VALUE
C 1=VALID ACTIVE FORMULA THERE TO EVALUATE. INITIALLY ALL 0'S
C SO INITIALLY IGNORE.
	INTEGER*2 PROW,PCOL,DROW,DCOL,DRWV,DCLV
	COMMON/DCTL/PROW,PCOL,DROW,DCOL,DRWV,DCLV
	DIMENSION NRDSP(DRW,DCL),NCDSP(DRW,DCL)
	COMMON/D2R/NRDSP,NCDSP
	INTEGER*2 TYPE(RRW,RCL),VLEN(9)
	REAL*8 XVBLS(RRW,RCL)
	LOGICAL*1 AVBLS(100,27),VBLS(8,RRW,RCL)
	INTEGER*4 JVBLS(2,RRW,RCL)
	EQUIVALENCE(JVBLS(1,1,1),XVBLS(1,1))
	REAL*8 XXX
	EQUIVALENCE(VBLS(1,1,1),XVBLS(1,1))
	COMMON/V/TYPE,AVBLS,VBLS,VLEN
	REAL*8 ACX,ACY
	REAL*8 AC,SS,CTR
	EQUIVALENCE(ACY,AVBLS(1,27))
	INTEGER*2 KDRW,KDCL
	COMMON /DOT/KDRW,KDCL
	LOGICAL*1 ILINE(106)
	INTEGER*2 ILNFG,ILNCT
	COMMON/ILN/ILNFG,ILNCT,ILINE
	COMMON/FVLDC/FVLD
C +++++++++++++++++++++++++++++++++++
C
C FIRST GET A VARIABLE NAME. ALL MATH FUNCTIONS REQUIRE VARIABLE
C NAMES SINCE THEIR VARIABLES ARE THEIR ONLY VALID ARGS.
	CALL MTHINI(INDEXF,AC,SS,CTR,ACX)
C SET UP PROPER INITS
C KV2=1 IF A 2ND VBL EXISTS
	LCR=LLB+1
100	CONTINUE
	KV2=0
	LB=LCR
	LE=LRB-1
	IF(LB.GE.LE)RETURN
	CALL VARSCN(LINE,LB,LE,LASST,ID1,ID2,IVALID)
	IF(IVALID.EQ.0)RETURN
	IF(LINE(LASST).NE.':')GOTO 110
	LB=LASST+1
	LE=LRB-1
	CALL VARSCN(LINE,LB,LE,LASST,ID1B,ID2B,IVALID)
	IF(IVALID.NE.0)KV2=1
110	CONTINUE
	XXX=XVBLS(ID1,ID2)
	IF(ABS(TYPE(ID1,ID2)).NE.2)XXX=JVBLS(1,ID1,ID2)
	CALL DOMATH(INDEXF,XXX,AC,SS,CTR,ACX)
	IF(KV2.EQ.0)GOTO 200
	IF(ID1.NE.ID1B) GOTO 120
	IF(ID2.GT.ID2B)GOTO 200
	M=ID2+1
	DO 121 MM=M,ID2B
	XXX=XVBLS(ID1,MM)
	IF(ABS(TYPE(ID1,MM)).NE.2)XXX=JVBLS(1,ID1,MM)
	CALL DOMATH(INDEXF,XXX,AC,SS,CTR,ACX)
121	CONTINUE
	GOTO 200
120	CONTINUE
	IF(ID2.NE.ID2B)GOTO 130
	IF(ID1.GT.ID1B)GOTO 200
	M=ID1+1
	DO 131 MM=M,ID1B
	XXX=XVBLS(MM,ID2)
	IF(ABS(TYPE(MM,ID2)).NE.2)XXX=JVBLS(1,MM,ID2)
	CALL DOMATH(INDEXF,XXX,AC,SS,CTR,ACX)
131	CONTINUE
130	CONTINUE
200	CONTINUE
C IF NEXT CHAR IS A COMMA, SKIP IT AND KEEP UP SCAN UNLESS DONE
	IF(LINE(LASST).EQ.',')GOTO 300
	RETURN
300	LCR=LASST+1
	GOTO 100
	END
	SUBROUTINE DOIF(LINE,LLB,LRB,LLAST)
	INCLUDE 'VKLUGPRM.FTN'
	PARAMETER CUP=1,EL=12
	LOGICAL*1 LINE(110)
	REAL*8 V1,V2
	V1=0.
	V2=0.
	LS=LRB-LLB+1
	CALL GETLOG(LINE(LLB),LS,LOGTYP,LASST)
	LOV1=LLB
	LHIV1=LASST+LLB-1
	IF(LOV1.GE.LHIV1)GOTO 100
C USE SUM FUNCTION HERE AS TYPE OF FCN
	LT=4
	CALL DOMFCN(LINE,LOV1,LHIV1,LT,V1)
100	CONTINUE
	IF(LOGTYP.EQ.0)GOTO 1000
	LOV2=LASST+2+LLB
	LHIV2=LRB
	IF(LOV2.GE.LHIV2)GOTO 200
	LT=4
	CALL DOMFCN(LINE,LOV2,LHIV2,LT,V2)
200	CONTINUE
	CALL TEST(LOGTYP,LFLAG,V1,V2)
	IF(LFLAG.EQ.0)GOTO 700
C HERE HAVE "TRUE" ALTERNATIVE OF IF STMT
	LBAR=INDEX(LINE,'|')
	LBAR=MIN0(LBAR,LLAST)
	LSTM=LRB+1
C LSTM TO LBAR IS NOW THE STMT TO EVALUATE. SINCE WE ALREADY HAVE A
C ROUTINE TO EVALUATE A STMT, DO SO. NOTE PARTIAL RECURSION, SO
C NO NESTED IFS ALLOWED, AND CALL MUST PERMIT RECURSION ON YOUR
C MACHINE OR FORGET IT. (OK ON PDP11, VAX).
	LSZ=LBAR-LSTM
	IF(LSZ.LT.1)GOTO 1000
	CALL DOSTMT(LINE(LSTM),LSZ)
	GOTO 1000
700	CONTINUE
C HERE HAVE "FALSE" ALTERNATIVE OF IF STMT
	LBAR=INDEX(LINE,'|')+1
	LBAR=MIN0(LBAR,LLAST)
	LSZ=LLAST-LBAR
	IF(LSZ.LT.1)GOTO 1000
	CALL DOSTMT(LINE(LBAR),LSZ)
1000	CONTINUE
C THAT'S ALL.
	RETURN
	END
	SUBROUTINE GETLOG(LINE,LMX,LOGTYP,LASST)
	LOGICAL*1 LINE(110)
	LOGICAL*1 LFN(4,6)
	INTEGER*4 LF(6)
	EQUIVALENCE(LF(1),LFN(1,1))
	DATA LF/'.GT.','.LT.','.EQ.','.NE.','.GE.','.LE.'/
C LOGTYP RELATIONSHIP TO RELATIONSHIPS OF 2 VARIABLES
C IS DEFINED IN ABOVE DATA STMT.
C IF LINE CONTAINS STRING IN NAME, RETURN TYPE AND END LOC.
	LMX4=LMX-3
	DO 100 LL=1,6
	LOGTYP=LL
	DO 1 N1=1,LMX4
	IF(LINE(N1  ).NE.LFN(1,LL))GOTO 2
	IF(LINE(N1+1).NE.LFN(2,LL))GOTO 2
	IF(LINE(N1+2).NE.LFN(3,LL))GOTO 2
	IF(LINE(N1+3).NE.LFN(4,LL))GOTO 2
C HERE HAVE A MATCH
	LASST=N1
C RETURN LOC OF NEXT CHAR AFTER RELATION.
	GOTO 200
2	CONTINUE
1	CONTINUE
100	CONTINUE
	LOGTYP=0
200	CONTINUE
	RETURN
	END