C SCIENTIFIC FUNCTION CALLER
C This version is a dummy placeholder.
C The SCIFCT subroutine exists to allow AnalytiCalc to call just
C about *ANY* Fortran callable routine.
C   The operation is to use a formula in AnalytiCalc which includes
c a call of form:
c  *U STxxxxxx range;range;range;range;range;...;range>outrange;outrange;outrange
c so that the "xxxxxx" part is the function name to be called.
c  input ranges are the parts of the sheet for input to the function; these
c are internally copied to a large array (defined here) which is a normal
c Fortran array. They are converted to integer*4 as needed if the function
c being called needs this. Once all conversion is done, the subroutine is
c called using an argument list built up by this call list. At the end,
c the output ranges are filled in from the internal Fortran array.
c   Because Fortran callable subroutines (e.g. those in the SSP) may pass
c their return arguments in ANY of their arguments, seeing a ; will increment
c the output range counter.
c
c To add more:
c  * Select desired sizes for work area (must be big enough to hold ALL
c  arguments used), max number of arguments per function, etc.
c  * Add new function name and characteristics to tables. Note that the
c  name, integer/float stuff for all args, which arg is first OUTPUT arg,
c  and map of output args, all are needed. Don't make first output arg
c  bigger than the max. number of args.
c  * Add another call and element in the computed GOTO for each function
c  desired.
c  * Build and enjoy.
c
c   Internally we need tables of
c      * Function names (up to 6 characters long per classical Fortran rules)
c      * Number of arguments needed per function
c      * Integer/real flags for arguments' data types
c      * First output argument number (user convenience and less error
c           prone than having to have a bunch of ;;;;'s to force the
c           outputrange to come from the right area
c      * Length of the Fortran array used for each input argument
c Note: Provision is made for "scratch array" arguments, but is a bit
c  crude. However, if extra space is needed, user can specify a larger
c  input area and the larger chunk of scratch space will be present.
c  Unused argument areas will generally be zeroed on each call.
c   It is perfectly reasonable to have input-only functions (e.g. plots)
c   or several subroutines called in sequence for a function.
c
	SUBROUTINE SCIFCT(LINE,RETCD)
	Integer BigSpc
	Parameter (BigSpc=1024)
	Parameter (MaxArgs=10)
	Parameter (NFCT=5)
c NFCT is number of functions included in the list. Update the parameter
c and the tables together (please!)
	INTEGER*2 RETCD
	LOGICAL*1 LINE(80)
	Real*8 ArgAry(BigSpc)
	INTEGER*4 IARGAR(2,BIGSPC)
	EQUIVALENCE(IARGAR(1,1),ARGARY(1))
	Integer*4 ArgCtr,IntPar
	Integer*4 ArgPtr(MaxArgs)
	Integer*4 NARGin(NFct)
c nargin is number input args needed.
	Integer*4 OutArg(MaxArgs,NFct)
	Integer*4 RevStr(MaxArgs,NFct)
c RevStr will be nonzero to reverse storage of arrays
c from normal row-first to column-first order.
	Integer*4 OutBgn(NFct)
c OutArg is 0 for no output, 1 for output area
	Integer*4 IsReal(MaxArgs,NFCT)
c
C Since there are some subs that need dummy argument scratch
c areas, encode IsReal as follows:
c  0 = Real
c  -1 = Integer
c  +nn = Use argument nn's VALUE (after grabbing it) for
c        size of area to allocate. Always allocate floats
c        since they're longer.
c
c Note: Due to the way the program allocates scratch array, the
c  arguments with size info for dummy arrays must be present
c  ahead of the scratch space arguments.
c
C Argument coordinate lists
	Integer*4 InCord(4,MaxArgs)
	Integer*2 InType(MaxArgs)
	Integer*4 OutCor(4,MaxArgs)
	REAL*8 R8WRK,R8WRK2
	INTEGER*4 I4WRK,I4WRK2
	Integer*2 OutTyp(MaxArgs)
c
	Character*6 WrkFnm
	Logical*1 WFNm(6)
	Equivalence(WFNm(1),WrkFnm)
	Integer*4 IniOut(NFCT)
	Integer*4 AryPtr
	Character*6 FName(NFCT)
	Logical*1 FNameB(6,NFCT)
	Equivalence(Fname(1),FNameB(1,1))
c allows access of function names by byte, but data stmts to set up
c as full names...
c    This example has only 2 functions:
c  *U STDLLSQ   and
c  *U STCHISQ
c        from the Scientific Subroutine Package library...
	Data FnameB/
     1  'D','L','L','S','Q',0,
     2  'C','H','I','S','Q',0,
     3  'V','E','C','N','O','R',
     4  'A','S','Y','M', 0 ,0,
     4  'A','S','U','M', 0 ,0 /
	DATA IsReal/
     1  0,0,-1,-1,-1,0,5,0,-1,0,
     2  0,-1,-1,0,-1,-1,2,3,0,0,
     3  0,-1,0,0,0,0,0,0,0,0,
     4  0,-1,-1,0,0,0,0,0,0,0,
     5  0,-1,-1,0,-1,0,0,0,0,0  /
	DATA OutBgn/
     1  6,4,3,4,4 /
	DATA OutArg/
     1  0,0,0,0,0,1,0,0,1,1,
     2  0,0,0,1,1,1,0,0,0,0,
     3  0,0,1,0,0,0,0,0,0,0,
     4  0,0,0,1,0,0,0,0,0,0,
     5  0,0,0,1,1,0,0,0,0,0 /
c Note OutArg is just which output arguments are really
c output data. 1 means they are, 0 means they're not.
c
C NARGIN is min number input arguments that must be present.
	Data NARGin/10,8,3,4,5/
	Data RevStr/
     1  0,0,0,0,0,0,0,0,0,0,
     2  0,0,0,0,0,0,0,0,0,0,
     3  0,0,0,0,0,0,0,0,0,0,
     4  0,0,0,0,0,0,0,0,0,0,
     5  0,0,0,0,0,0,0,0,0,0 /

C
C FIRST, before we spend a lot of effort grabbing arguments, make
c  sure we know about the function to be called. If we don't, just
c  return an error.
	KK=0
	DO 101 N=1,NFCT
	DO 110 NN=1,6
	IF(FNAMEB(NN,N).LE.0)GOTO 110
	IF(LINE(NN).NE.FNAMEB(NN,N)) GOTO 112
110	CONTINUE
C WE FELL THRU AND FOUND THE NAME. SAVE ITS' INDEX.
	KK=N
112	CONTINUE
101	CONTINUE
	IF(KK.GT.0)GOTO 115
114	RETCD=3
	RETURN
115	CONTINUE
	NFUNCT=KK
c A little setup...
	ArgCtr=1
	IntPar=1
c integer "parity", used to pack integer args in work array
	Aryptr=1
	Do 1 n=1,MaxArgs
	Argptr(n)=1
	Do 11 nn=1,4
	InCord(nn,n)=0
	OutCor(nn,n)=0
11	Continue
1	CONTINUE
	DO 2 N=1,BigSpc
	ArgAry(N)=0.0D0
2	Continue
C arrange for all uninitialized numbers to contain zeroes
	RETCD=1
C HANDLE *U STXXXX FUNCTIONS. LINE ARRAY PASSED IN HERE
C STARTS AFTER THE "ST" SO WE CAN DECODE IT.
c if we can't get the function, return RETCD=3...
c
c Now grab the arguments and store them in InCord, Intype, OutCor, OutTyp
	K=INDEX(LINE,32)
C FIND STUFF AFTER SPACE
	K=K+1
	NArg=1
	IBGN=1
100	Continue
	LEND=IBGN+20
C GET LOC OF MATRIX A (MUST BE SQUARE)
	ID1B=0
	ID2B=0
	ID1A=0
	ID2A=0
	CALL VARSCN(LINE(K),IBGN,LEND,LSTCHR,ID1A,ID2A,IVALID)
	IF(IVALID.EQ.0)GOTO 300
	IF(LINE(K+LSTCHR-1).NE.':')GOTO 1000
	IBGN=LSTCHR+1
	LEND=IBGN+20
	CALL VARSCN(LINE(K),IBGN,LEND,LSTCHR,ID1B,ID2B,IVALID)
	IF(IVALID.EQ.0)GOTO 300
1000	CONTINUE
C GMTX GETS ARGS FOR ONE RANGE
	InCord(1,NArg)=ID1A
	InCord(2,NArg)=ID2A
	INCord(3,NARG)=ID1B
	INCORD(4,NARG)=ID2B
	IBGN=LSTCHR+1
	NARG=NARG+1
	IF(LINE(K+LSTCHR-1).EQ.';')GOTO 100
C
300	CONTINUE
C NOW HAVE ALL ARGS FOR INPUT COLLECTED
	INARGS=NARG
	If(INargs.lt.NARGin(NFunct)) GOTO 114
c Flag error if not enough input args presented.
	K=INDEX(LINE,62)
C FIND STUFF AFTER > CHARACTER
	IF(K.EQ.0.OR.K.GT.70)GOTO 500
C MUST HAVE A > OR no outputs are present.
C This is perfectly legal; outputs like graphs or auxiliary
C files (unknown to rest of program) are possible too.
	K=K+1
	NArg=1
	IBGN=1
400	Continue
	LEND=IBGN+20
C GET LOC OF MATRIX A (MUST BE SQUARE)
	ID1B=0
	ID2B=0
	ID1A=0
	ID2A=0
C TEST FOR NULL ARGUMENT (;; PAIR)
	IF(LINE(K+IBGN-1).EQ.';')GOTO 450
	CALL VARSCN(LINE(K),IBGN,LEND,LSTCHR,ID1A,ID2A,IVALID)
	IF(IVALID.EQ.0)GOTO 500
	IF(LINE(K+LSTCHR-1).NE.':')GOTO 1500
	IBGN=LSTCHR+1
	LEND=IBGN+20
	CALL VARSCN(LINE(K),IBGN,LEND,LSTCHR,ID1B,ID2B,IVALID)
	IF(IVALID.EQ.0)GOTO 500
1500	CONTINUE
	IBGN=LSTCHR+1
	GOTO 455
450	CONTINUE
	IBGN=IBGN+1
	LSTCHR=IBGN
C PASS ;
455	CONTINUE
C GMTX GETS ARGS FOR ONE RANGE
	OUTCor(1,NArg)=ID1A
	OUTCor(2,NArg)=ID2A
	OUTCor(3,NARG)=ID1B
	OUTCor(4,NARG)=ID2B
	NARG=NARG+1
	IF(LINE(K+LSTCHR-1).EQ.';')GOTO 400
C	GOTO 500
C
500	CONTINUE
C NOW HAVE OUTPUT ARGUMENT LIST COLLECTED
C BEGIN COLLECTING DATA
	NARG=1
	IntPar=1
2000	CONTINUE
	IACNTR=ARGCTR
C  GET INPUT DATA INTO OUR BIG ARRAY
	IF(INCORD(1,NARG).LE.0)GOTO 3000
	ARGPTR(NARG)=ARGCTR
	IF(INCORD(3,NARG).NE.0)GOTO 2011
C SINGLE ARGUMENT; GRAB IT
	nn=incord(1,narg)
	mm=incord(2,narg)
	call typget(nn,mm,itype)
	If(Itype.ne.4) then
	  CALL XVBLGT(NN,MM,R8WRK)
	Else
	  Call JVBLGT(NN,MM,I4wrk)
	  R8WRK=I4WRK
	End If
c	CALL XVBLGT(INCORD(1,NARG),INCORD(2,NARG),R8WRK)
	IF(ISREAL(NARG,NFUNCT).LT.0) THEN
	  INTPAR=1
	  I4WRK=R8WRK
	  IARGAR(IntPar,ARGCTR)=I4WRK
	ELSE
	  If(IntPar.ne.1)ARGCTR=MIN0(ARGCTR+1,BigSpc)
	  IntPar=1
C if we last packed the second word of an integer, bump to next
	  ARGARY(ARGCTR)=R8WRK
	END IF
	ARGCTR=MIN0(ARGCTR+1,BigSpc)
	NARG=NARG+1
	GOTO 2000
2011	CONTINUE
C 2-D AREA
	IntPar=1
	DO 2020 LNN=INCORD(1,NARG),INCORD(3,NARG)
	DO 2020 LMM=INCORD(2,NARG),INCORD(4,NARG)
	NN=LNN
	IF(REVSTR(NARG,NFUNCT).NE.0)NN=LMM
	MM=LMM
	IF(REVSTR(NARG,NFUNCT).NE.0)MM=LNN
	call typget(nn,mm,itype)
	If(Itype.ne.4) then
	  CALL XVBLGT(NN,MM,R8WRK)
	Else
	  Call JVBLGT(NN,MM,I4wrk)
	  R8WRK=I4WRK
	End If
	IF(ISREAL(NARG,NFUNCT).LT.0) THEN
	  I4WRK=R8WRK
	  IARGAR(IntPar,ARGCTR)=I4WRK
	  IntPar=3-IntPar
c if IntPar is 1 make it 2; if it's 2, make it 1
	ELSE
	  If(IntPar.ne.1)ARGCTR=MIN0(ARGCTR+1,BigSpc)
	  IntPar=1
C if we last packed the second word of an integer, bump to next
	  ARGARY(ARGCTR)=R8WRK
	END IF
	If(IntPar.eq.1)ARGCTR=MIN0(ARGCTR+1,BigSpc)
2020	CONTINUE
	NARG=NARG+1
	ARGCTR=MIN0(ARGCTR+1,BigSpc)
	IntPar=1
C
C FIX UP DUMMY ARGUMENTS
C
	IF(ISREAL(NARG,NFUNCT).GT.0.AND.ISREAL(NARG,NFUNCT)
     1  .LE.MAXARGS) THEN
c If user allocated more space than the dummy calc, use bigger
c allocation. However, add a little more and check for array
c overflow.
	  ARGCTR=MAX0(ARGCTR,IACNTR+IARGAR(1,ISREAL(NARG,NFUNCT)))
	  ARGCTR=ARGCTR+30
	  ARGCTR=MIN0(ARGCTR+1,BigSpc)
C ADD A LITTLE FOR GOOD LUCK
	END IF
	GOTO 2000
3000	CONTINUE
C NOW SHOULD BE READY TO CALL THIS STUFF...
C GENERATE CALLS LIKE THE TEMPLATES BELOW. NO NEED TO MODIFY
C THE FUNCTIONS, BUT WE DO NEED TO MESS WITH THIS STUFF BECAUSE
C I DON'T KNOW OFFHAND HOW TO DO A DYNAMIC CALLING LIST IN FORTRAN
C THAT'LL WORK ON STACK IMPLEMENTATIONS.
c
c Add more numbers to the list here to get more function calls.
c
	GOTO (4001,4002,4003,4004,4005),NFUNCT
	RETCD=3
	RETURN
c *************** BEGINNING OF CALLS ****************
4001	CONTINUE
C DLLSQ FUNCTION.... 10 ARGS
	CALL DLLSQ(ARGARY(ARGPTR(1)),ARGARY(ARGPTR(2)),
     1  ARGARY(ARGPTR(3)),ARGARY(ARGPTR(4)),ARGARY(ARGPTR(5)),
     2  ARGARY(ARGPTR(6)),ARGARY(ARGPTR(7)),ARGARY(ARGPTR(8)),
     3  ARGARY(ARGPTR(9)),ARGARY(ARGPTR(10)))
	GOTO 5000
4002	CONTINUE
C CHISQ FUNCTION.... 8 ARGS
	CALL CHISQ(ARGARY(ARGPTR(1)),ARGARY(ARGPTR(2)),
     1  ARGARY(ARGPTR(3)),ARGARY(ARGPTR(4)),ARGARY(ARGPTR(5)),
     2  ARGARY(ARGPTR(6)),ARGARY(ARGPTR(7)),ARGARY(ARGPTR(8)))
	GOTO 5000
4003	CONTINUE
C Vector Norm function
	CALL VECNOR(ARGARY(ARGPTR(1)),ARGARY(ARGPTR(2)),
     1  ARGARY(ARGPTR(3)))
	GOTO 5000
4004	CONTINUE
C ASYMMETRY
	CALL ASYM(ARGARY(ARGPTR(1)),ARGARY(ARGPTR(2)),
     1  ARGARY(ARGPTR(3)),ARGARY(ARGPTR(4)))
	GOTO 5000
4005	CONTINUE
C ARRAY SUM
	CALL ASUM(ARGARY(ARGPTR(1)),ARGARY(ARGPTR(2)),
     1  ARGARY(ARGPTR(3)),ARGARY(ARGPTR(4)),ARGARY(ARGPTR(5)))
	GOTO 5000
C Use this for debugging too...
c
c insert more function calls here... they all look alike except for
c function name.
c
c  It's also completely permissible to call several Fortran subroutines
c  in sequence here if it makes sense; it's up to the user. This code
c  just gives a way to call unmodified Fortran callable code and have
c  it make sense in the AnalytiCalc context. ANY Fortran callable code
c  is OK.
c
c *****************end of calls *****************
c
5000	CONTINUE
C NOW GET ARGUMENTS BACK TO DUMP TO SHEET
	KARG=0
	DO 5100 NARG=OUTBGN(NFUNCT),MAXARGS
	KARG=KARG+1
	IF(OUTARG(NARG,NFUNCT).LE.0)GOTO 5100
	IF(OUTCOR(1,KARG).EQ.0)GOTO 5100
C +++
	ARGCTR=ARGPTR(NARG)
	IF(OUTCOR(3,KARG).NE.0)GOTO 6014
C SINGLE ARGUMENT; GRAB IT
	IF(ISREAL(NARG,NFUNCT).LT.0) THEN
	  I4WRK=IARGAR(1,ARGCTR)
	  R8WRK=I4WRK
	ELSE
	  R8WRK=ARGARY(ARGCTR)
	END IF
	nn=outcor(1,karg)
	mm=outcor(2,karg)
	Call typget(nn,mm,itype)
	If (Itype.ne.4) then
	  CALL XVBLST(NN,MM,R8WRK)
	Else
	  I4WRK=R8WRK
	  CALL JVBLST(nn,mm,I4WRK)
	End If
	ARGCTR=MIN0(ARGCTR+1,BigSpc)
	GOTO 5100
6014	CONTINUE
C 2-D AREA
	DO 6020 LNN=OUTCOR(1,KARG),OUTCOR(3,KARG)
	DO 6020 LMM=OUTCOR(2,KARG),OUTCOR(4,KARG)
	NN=LNN
	IF(REVSTR(NARG,NFUNCT).NE.0)NN=LMM
	MM=LMM
	IF(REVSTR(NARG,NFUNCT).NE.0)MM=LNN
	IF(ISREAL(NARG,NFUNCT).LT.0) THEN
	  I4WRK=IARGAR(1,ARGCTR)
	  R8WRK=I4WRK
	ELSE
	  R8WRK=ARGARY(ARGCTR)
	END IF
	Call typget(nn,mm,itype)
	If (Itype.ne.4) then
	  CALL XVBLST(NN,MM,R8WRK)
	Else
	  I4WRK=R8WRK
	  CALL JVBLST(nn,mm,I4WRK)
	End If
c	CALL XVBLST(NN,MM,R8WRK)
	ARGCTR=MIN0(ARGCTR+1,BigSpc)
6020	CONTINUE
C +++
5100	CONTINUE
C AT LAST, DONE
	RETURN
	END
	Subroutine VecNor(InRng,NVEC,Val)
C test subroutine
c Computes norm of input range, where NVEC is number of
c elements in the INRNG array.
	REAL*8 InRng
	Dimension InRng(1)
	Integer*4 NVEC
	Real*8 Val,X
C	VAL=0.0d0
	If(NVEC.LE.0)val=-1.0
	If(NVEC.LE.0)return
c return -1 if bad dimensions.
	X=0.0D0
	Do 1 n=1,nvec
	x=x+InRng(n)*InRng(n)
1	Continue
	x=dsqrt(x)
	Val=X
	Return
	End
c test subroutine
	Subroutine Asym(a,n,m,asymm)
	Real*8 a(n,m),asymm
	Integer*4 l
	asymm=0.0
	L=Min0(n,m)
	Do 1 k1=1,l
	Do 2 k2=1,l
	If(k2.ge.k1)goto 2
	asymm=asymm+a(k1,k2)-a(k2,k1)
2	Continue
1	Continue
	Return
	End
c test subroutine
c sums array values
	Subroutine Asum(a,n,m,asymm,icnt)
	Real*8 a(n,m),asymm
	Integer*4 icnt
	asymm=0.0
	Icnt=0
	Do 1 k1=1,n
	Do 2 k2=1,m
	If(a(k1,k2).ne.0.0)icnt=icnt+1
	asymm=asymm+a(k1,k2)
2	Continue
1	Continue
	Return
	End
