1000 '*************************************************************************
1010 '* PROGRAM NONLIN  version 3.0  5/1/82                                   *
1020 '*                                                                       *
1030 '* By:       Dave Whitman                                                *
1040 '*                                                                       *
1050 '*           Box 383 Baker Lab                                           *
1060 '*           Cornell U.                                                  *
1070 '*           Ithaca, NY  14853                                           *
1080 '*           (607) 256-6479                                              *
1090 '*                                                                       *
1100 '* Performs non-linear least squares analysis to determine parameters    *
1110 '* to fit any function to observed data.                                 *
1120 '*                                                                       *
1130 '* Inspired by a FORTRAN program by C.F. Wilcox, which was in turn       *
1140 '* based on "Rigorous Least Squares Adjustment"; Wentworth, W. E.        *
1150 '* J . Chem Ed. 42, 96 (1965).                                           *
1160 '*                                                                       *
1170 '*************************************************************************
1180 '* The program requires two input files to work:                         *
1190 '*                                                                       *
1200 '*    The first file contains BASIC code which is specific to the        *
1210 '*    paticular function being fit.  This code will be automatically     *
1220 '*    merged into the program at run time.  For the merge to work        *
1230 '*    properly, this file MUST BE SAVED IN ASCII FORMAT.  Otherwise, a   *
1240 '*    "Bad File Mode" error occurs.                                      *
1250 '*    To save in ascii mode, use the "a" option in your save command:    *
1260 '*    Example:  SAVE"function",a                                         *
1270 '*                                                                       *
1280 '*    When nonlin prompts: FUNCTION?, input the name of this file.       *
1290 '*                                                                       *
1300 '*    Code required:                                                     *
1310 '*       A line 10050 which sets the variables nvar [# of independant    *
1320 '*       variables] and np [# of parameters]. Example:                   *
1330 '*                                                                       *
1340 '*   10050 nvar = 1: np = 3                                              *
1350 '*                                                                       *
1360 '*       A subroutine which fills the array fcalc(nobs) with values of   *
1370 '*       the function, given observed values of the variables in array   *
1380 '*       vobs(nobs,nvar) and current guesses at the parameters in p(np). *
1390 '*       The subroutine should start at line 11000 and end at or before  *
1400 '*       line 11999. Example: (fitting data to a parabola)               *
1410 '*                                                                       *
1420 '*   11000 FOR I = 1 to NOBS                                             *
1430 '*   11010    fcalc(i) = p(1) * vobs(i,1)^2 + p(2) * vobs(i,1) + p(3)    *
1440 '*   11020    NEXT I                                                     *
1450 '*   11030 RETURN                                                        *
1460 '*                                                                       *
1470 '*                                                                       *
1480 '*    Optional code:                                                     *
1490 '*       Before stopping, nonlin makes a subroutine call to line 20000.  *
1500 '*       Normally, this subroutine consists of a stub, containing only   *
1510 '*       a return statement. If any final processing using the least     *
1520 '*       squares parameter set in p(np) or the variance/covariance       *
1530 '*       matrix in b(np,np) is desired, supply an appropriate            *
1540 '*       subroutine starting at line 20000. The only restriction on the  *
1550 '*       length of this subroutine is the available memory.              *
1560 '*                                                                       *
1570 '* The second file contains the observed data to be fit.                 *
1580 '* When nonlin prompts: INPUT FILE?, enter the name of this file.        *
1590 '*                                                                       *
1600 '*    Required data:                                                     *
1610 '*       nobs: the number of observations being fit                      *
1620 '*       numit: the # of iterations of the fitting process to be         *
1630 '*              performed. [ 5-10 is generally sufficient ]              *
1640 '*       iuserwt: a flag. If iuserwt = 1, nonlin expects all observed    *
1650 '*                function and variable values to be followed by a       *
1660 '*                weighting factor. If iuserwt = 0, nonlin automatically *
1670 '*                sets all initial weights to 1.                         *
1680 '*      internalwt: a flag. If internalwt = 1, nonlin estimates          *
1690 '*                weighting factors for each function value based on     *
1700 '*                the slope of the function.  If internalwt = 0, no      *
1710 '*                estimate.  Use with care, internal weighting often     *
1720 '*                causes divergance. Start with internalwt = 0.          *
1730 '*                                                                       *
1740 '*      fract: The fraction of the calculated change to apply to each    *
1750 '*             of the parameters.  Use to restrict changes when function *
1760 '*             trys to diverge. Normally equal to 1.                     *
1770 '*                                                                       *
1780 '*      Repeat the following lines for each observation:                 *
1790 '*           fobs(i): observed function value                            *
1800 '*           if iuserwt = 1 include obswt(i): function weight, point i   *
1810 '*           vobs(i,1): observed value, variable 1.                      *
1820 '*           if iuserwt = 1 include varwt(i,1): variable weight          *
1830 '*           vobs(i,2): observed value, variable 2.                      *
1840 '*           if iuserwt = 1 include varwt(i,2): variable weight          *
1850 '*           ...vobs(i,nvar): observed value, variable nvar              *
1860 '*              if iuserwt = 1 include varwt(i,nvar): variable weight    *
1870 '*                                                                       *
1880 '*      Repeat the following lines for each parameter:                   *
1890 '*           pname$(i): the name of the parameter (length <= 8)          *
1900 '*           p(i): initial guess at the parameter's value                *
1910 '*                                                                       *
1911 '*************************************************************************
1912 '* Note to IPCO users:                                                   *
1913 '* Included on this disk are the following files for testing and         *
1914 '* demonstration purposes:                                               *
1915 '*     DATA       set of test data to be fit to the following functions: *
1916 '*     FUNC1.BAS  gaussian function                                      *
1917 '*     FUNC2.BAS  lorentzian function                                    *
1920 '*************************************************************************
1921 '*                                                                       *
1922 '* Note: ocasionally the program diverges, i.e. the fit of the calculated*
1923 '* function gets worse each iteration, rather than better. If this       *
1924 '* occurs, it indicates one of two things:                               *
1925 '*    1. Your initial guesses for the parameters were too far off.       *
1926 '*       Make a better guess, and try again.                             *
1927 '*    2. The function you are using really can't adequately describe     *
1928 '*       your data.                                                      *
1929 '*************************************************************************
1930 '* DECLARATIONS:                                                         *
1940 '* NOBS: INT                                      NUMBER OF OBSERVATIONS *
1950 '* NVAR: INT                                NUMBER OF VARIABLES OBSERVED *
1960 '* FOBS: ARRAY(1..NOBS) OF DOUBLE               OBSERVED FUNCTION VALUES *
1970 '* FCALC:ARRAY(1..NOBS) OF DBL                CALCULATED FUNCTION VALUES *
1980 '* FTEMP:ARRAY(1..NOBS) OF DBL            HOLDS A SET OF FUNCTION VALUES *
1990 '*                                         DURING DERIVATIVE CALCULATION *
2000 '* VOBS: ARRAY(1..NOBS,1..NVAR) OF DBL          OBSERVED VARIABLE VALUES *
2010 '* NP:   INT                                        NUMBER OF PARAMETERS *
2020 '* P:    ARRAY(1..NP) OF DBL                            PARAMETER VALUES *
2030 '* PNAME$: ARRAY(1..NP) OF STRING                        PARAMETER NAMES *
2040 '* DFDP:ARRAY(1..NOBS,1..NP) OF DBL   PARTIALS OF FUNC W.R.T. PARAMETERS *
2050 '* DFDV:ARRAY(1..NOBS,1..NVAR) OF DBL     "     "   "     "   VARIABLES  *
2060 '* OBSWT:ARRAY(1..NOBS) OF DBL             OBSERVATION WEIGHTING FACTORS *
2070 '* VARWT:ARRAY(1..NOBS,1..NVAR) OF DBL        VARIABLE WEIGHTING FACTORS *
2080 '* DLAMBDA:ARRAY(1..NOBS) OF DBL                  LAGRANGIAN MULTIPLIERS *
2090 '* B:   ARRAY(1..NP,1..NP) OF DBL        COEFFICIENTS IN MATRIX EQUATION *
2100 '* RHS: ARRAY(1..NP) OF DBL           RIGHT HAND SIDE OF MATRIX EQUATION *
2110 '*                     NOTE: AFTER SOLUTION OF EQN, RHS HOLDS CHANGES TO *
2120 '*                               PARAMETERS, AND B HOLDS ITS OWN INVERSE *
2130 '* FRACT: DBL       FRACTION OF CALCULATED CHANGE TO APPLY TO PARAMETERS *
2140 '* DEVSQ: DBL       SUM OF WEIGHTED SQUARED DEVIATIONS OF CALC. FUNCTION *
2150 '* DEVSQ1:DBL                                DEVSQ VALUE, LAST ITERATION *
2160 '* DEVSQ2:DBL                           DEVSQ VALUE, TWO ITERATIONS BACK *
2170 '* IUSERWT: INT            IF IUSERWT=1, USER SUPPLIES WEIGHTING FACTORS *
2180 '*                         IF IUSERWT=0, NONLIN ASSUMES ALL WEIGHTS = 1  *
2190 '* INTERNALWT: INT             IF INTERNALWT=1 NONLIN CALCULATES WEIGHTS *
2200 '*                                 IF INTERNALWT=0 NO WEIGHT CALCULATION *
2210 '*************************************************************************
10000 OPTION BASE 1
10010 DEFINT I-N
10020 DEFDBL A-H,O-Z
10025 ON ERROR GOTO 65000
10030 CLS: LOCATE 10,30: INPUT "FUNCTION?  ",FUNCTION$: COMMON FUNCTION$
10040 CHAIN MERGE FUNCTION$,10050   'bring in function specific lines
10045 ON ERROR GOTO 0
10050 NVAR = 1: NP = 3
10060 GOSUB 18000   'initialization routine
10070 '
10080 FOR IT = 1 TO NUMIT
10090    'print progress report on screen
10100        GOSUB 12000   'subroutine iteration report
10110    'Test for non-convergance, exit if so
10120        GOSUB 13000      'subroutine converge
10130        IF NONCONVERGENCE = 1                                                              THEN PRINT"nonconvergence termination" : GOTO 10300
10140    'Calculate lagrangian multipliers
10150        GOSUB 13500    'subroutine lambda
10160    'If internal weighting desired, calculate new obswts
10170        IF INTERNALWT = 1                                                                  THEN GOSUB 14500
10180    'Set up matrix equation to get parameter changes
10190        GOSUB 15000  'SUBROUTINE SETUP
10200    'Solve equation for parameter changes
10210        GOSUB 16000  'subroutine solve
10220    'Apply changes
10230        GOSUB 17000  'subroutine deltap
10240    NEXT IT
10250 '
10260 'print final report
10270    GOSUB 19000      'subroutine report
10280 'Do any final processing (user supplied)
10290    GOSUB 20000
10300 END
11000 '********************************************************************
11010 '* SUBROUTINE LORENTZIAN                                            *
11020 '*                                                                  *
11030 '* Fills the array fcalc(nobs) with values along a lorentzian of    *
11040 '* the form:                                                        *
11050 '*                                                                  *
11060 '* lor(pos) = H / [(1/W)^2 * (pos - P)^2 + 1]                       *
11070 '*                                                                  *
11080 '* where H, W, and P are parameters to be fit.                      *
11090 '* Assignments: H = p(1)  W = p(2) P = p(3)    pos = vobs(i,1)      *
11100 '********************************************************************
11110 '
11130 WSQ2 = 1# / (P(2) * P(2))
11160 FOR I = 1 TO NOBS
11170    FCALC(I) =  P(1) / (WSQ2 * (VOBS(I,1) - P(3))^2 + 1#)
11230    NEXT I
11240 RETURN
12000 '***********************************************************************
12010 '* SUBROUTINE ITERTATION REPORT                                        *
12020 '*                                                                     *
12030 '* Prints out current parameters, function values, and deviation       *
12040 '***********************************************************************
12050 GOSUB 12200                   'print parameters
12060 GOSUB 11000                   'get function values in fcalc
12070 GOSUB 12500                   'print function values and deviation
12080 RETURN
12200 '*********************************************************************
12210 '* SUBROUTINE PARAMREPORT( IT, NP, P)                                *
12220 '* 2/7/82 by Dave Whitman                                            *
12230 '* Prints out current parameter values                               *
12240 '*********************************************************************
12250 CLS : LOCATE 4,4 : BEEP  'Wake up,Dave!
12260 PRINT "Parameters, Iteration";IT : PRINT
12270 LOCATE 7,2
12280 COLOR 1 : PRINT "  Name  ?  Value   ? Change "; : COLOR 7 : PRINT
12290 FOR I = 1 TO NP
12300    LOCATE I+7,2
12310    PRINT USING  "\      \\\####.##\ \+##.##"; PNAME$(I);"? "; P(I);                             "  ? ";-1 * RHS(I)
12320    NEXT I
12330 PRINT
12340 RETURN
12500 '***********************************************************************
12510 '* SUBROUTINE FUNCTIONREPORT                                           *
12520 '* Prints obs. and calc. function values, and deviation between them   *
12530 '***********************************************************************
12540 IROW = 1 : ICOL = 40 : IOFFSET = 20 : IROOM = 40 : NUMROWS = 20
12550 LOCATE IROW,ICOL : COLOR 1
12560 PRINT "  obs. ? calc. "; : COLOR 7 : PRINT
12570 IF NOBS >= NUMROWS                                                                 THEN LOCATE IROW,ICOL+IOFFSET : COLOR 1:                                             PRINT "  obs. ? calc. "; : COLOR 7 : PRINT
12580  DEVSQ = 0
12590  FOR I = 1 TO NOBS
12600     IF I > IROOM THEN 12630
12610     IF I <= NUMROWS THEN LOCATE (IROW + I),ICOL                                                     ELSE LOCATE (IROW + I MOD NUMROWS),(ICOL + IOFFSET)
12620     PRINT USING "###.##\ \###.##"; FOBS(I);" ? ";FCALC(I);
12630     DEV = FOBS(I) - FCALC(I)
12640     DEVSQ = DEVSQ + DEV * DEV * OBSWT(I)
12650     NEXT I
12660 LOCATE 20,5 : PRINT USING "\         \#####.##";"? error? = ";DEVSQ
12670 IF IT = 1 THEN 12690  'no change in first iteration
12680 LOCATE 21,5: PRINT USING "\         \#####.##";"Change =   ";DEVSQ-DEVSQ1;
12690  RETURN
13000 '*********************************************************************
13010 '* SUBROUTINE CONVERGE ( ERRSQ, DEVSQ, DEVSQ1, DEVSQ2, NONCONVERGE ) *
13020 '*                                                                   *
13030 '* Compares squared deviation of calculated function from observed   *
13040 '* function with that obtained in the last 2 iterations. If the      *
13050 '* deviation got worse two iterations in a row, set nonconverge flag.*
13060 '*********************************************************************
13070  IF (DEVSQ > DEVSQ1 AND DEVSQ1 > DEVSQ2)                                            THEN NONCONVERGE = 1
13080  DEVSQ2 = DEVSQ1
13090  DEVSQ1 = DEVSQ
13100  RETURN
13500 '********************************************************************
13510 '* SUBROUTINE LAMBDA ( DLAMBDA, FCALC, FOBS, OBSWT)                 *
13520 '*                                                                  *
13530 '* Calculates lagrangian multipliers for setting up matrix equation *
13540 '********************************************************************
13550 FOR I = 1 TO NOBS
13560    DLAMBDA(I) = (FCALC(I) - FOBS(I)) * OBSWT(I)
13570    NEXT I
13580 RETURN
14000 '*************************************************************
14010 '* SUBROUTINE VSLOPE( V,DFDV,NVAR )                          *
14020 '*                                                           *
14030 '* Calculates the partials of the function w/ r.t. each      *
14040 '* of the variables at each of the observed points, and      *
14050 '* stores them in dfdv.                                      *
14060 '*************************************************************
14070 GOSUB 11000   'call function( p, v, nobs, nvar, np, fcalc)
14080 FOR IW = 1 TO NOBS
14090    FTEMP(IW) = FCALC(IW)
14100    NEXT IW
14110 FOR IW = 1 TO NVAR
14120    FOR JW = 1 TO NOBS
14130       IFLAG(JW) = 0
14140       IF ABS(VOBS(JW,IW)) < 1D-20                                                           THEN VOBS(JW,IW) = .0005# : IFLAG(JW) = 1                                       ELSE VOBS(JW,IW) = VOBS(JW,IW) * 1.0005#
14150 PRINT "modified var:" ;VOBS(JW,IW)
14160       NEXT JW
14170    GOSUB 11000    'call function(fcalc)
14180    FOR JW = 1 TO NOBS
14190       IF IFLAG(JW) = 1                                                                   THEN DFDV(JW,IW) = (FCALC(JW) - FTEMP(JW)) / .0005# :                                VOBS(JW,IW) = VOBS(JW,IW) - .0005#
14200       IF IFLAG(JW) <> 1                                                                  THEN DFDV(JW,IW) = (FCALC(JW)-FTEMP(JW)) / (.0005# * VOBS(JW,IW))                    : VOBS(JW,IW) = VOBS(JW,IW) / 1.0005#
14210       PRINT"dfdv(";JW;IW;")="; DFDV(JW,IW)
14220       NEXT JW
14230    NEXT IW
14240 RETURN
14500 '********************************************************************
14510 '* subroutine weigh[ p, nobs, nvar, v, dfdv, varwt, obswt ]         *
14520 '*                                                                  *
14530 '* calculates new weights for function values,                      *
14540 '* using the follwing formula:                                      *
14550 '* obswt(i) = 1/ ? [(dfdv)^2 * (1/varwt(v))]                        *
14560 '* note: obswt(i) = 1/L(i) in Wentworth article                     *
14570 '********************************************************************
14580 IF IT = 1 THEN RETURN  'skip weighting on first iteration
14590 GOSUB 14000 'subroutine vslope
14600 FOR IW = 1 TO NOBS
14610    SUM = 0#
14620    FOR JW = 1 TO NVAR
14630       SUM = SUM + DFDV(IW,JW)*DFDV(IW,JW)/VARWT(IW,JW)
14640       NEXT JW
14650    OBSWT(IW) = 1# / SUM
14660    NEXT IW
14670 PRINT "new function weights:"
14680 FOR IW = 1 TO NOBS
14690    PRINT OBSWT(IW)
14700    NEXT IW
14710 RETURN
15000 '********************************************************
15010 '* SUBROUTINE SETUP( B,RHS,dfdp)                        *
15020 '* Sets up matrix equation to get changes to parameters *
15030 '********************************************************
15040 '
15050 'Get partials of function w.r.t. parameters
15060     GOSUB 17500     'subroutine pslope
15070 'Now set up matrices
15080     FOR I = 1 TO NP
15090        'Set up right hand side element
15100        RHS(I) = 0#
15110        FOR J = 1 TO NOBS
15120           RHS(I) = RHS(I) + DFDP(J,I) * DLAMBDA(J)
15130           NEXT J
15140        'Set up left hand side elements
15150        FOR J = 1 TO NP
15160           B(I,J) = 0#
15170           FOR K = 1 TO NOBS
15180              B(I,J) = B(I,J) + DFDP(K,I) * DFDP(K,J) * OBSWT(K)
15190              NEXT K
15200           NEXT J
15210        NEXT I
15220 RETURN
16000 '*****************************************************
16010 '* subroutine solve[b#(np,np), rhs(np), np]          *
16020 '* 1/31/82 by Dave Whitman                           *
16030 '* solves matrix equations of the form b# x = rhs#   *
16040 '* inverts b# in place,multiplies rhs# by inverse    *
16050 '* uses Gauss-Jordan matrix inversion                *
16060 '* for good results b# and rhs# must be dbl precision*
16070 '* ref: J.M. McCormick "Numerical Methods in FORTRAN"*
16080 '*****************************************************
16090 DETERM = 1#
16100 FOR I = 1 TO NP
16110    INDEX(I,3) = 0
16120    NEXT I
16130 FOR I = 1 TO NP  'MAIN LOOP
16140    'search for pivot element
16150    MAX# = 0#
16160    FOR J = 1 TO NP
16170       IF INDEX(J,3) = 1 THEN 16260
16180       FOR K = 1 TO NP
16190          IF INDEX(K,3) > 1 THEN 16700
16200          IF INDEX(K,3) = 1 THEN 16250
16210          IF MAX# > ABS(B(J,K)) THEN 16250
16220          IROW = J
16230          ICOL = K
16240          MAX# = ABS(B(J,K))
16250          NEXT K
16260       NEXT J
16270   INDEX(ICOL,3) = INDEX(ICOL,3) + 1
16280   INDEX(I,1) = IROW
16290   INDEX(I,2) = ICOL
16300   'interchange rows to put pivot on diagonal
16310   IF IROW = ICOL THEN 16380  'ALREADY THERE
16320   DETERM = -1# * DETERM
16330   FOR J = 1 TO NP
16340      SWAP B(IROW,J),B(ICOL,J)
16350      NEXT J
16360   SWAP RHS(IROW),RHS(ICOL)
16370   'divide pivot row by pivot element
16380   PIVOT = B(ICOL,ICOL)
16390   DETERM = DETERM * PIVOT
16400   B(ICOL,ICOL) = 1#
16410   FOR J = 1 TO NP
16420      B(ICOL,J) = B(ICOL,J)/PIVOT
16430      NEXT J
16440   RHS(ICOL) = RHS(ICOL)/PIVOT
16450   ' reduce non-pivot rows
16460   FOR J = 1 TO NP
16470      IF J = ICOL THEN 16540
16480      T = B(J,ICOL)
16490      B(J,ICOL) = 0#
16500      FOR K = 1 TO NP
16510         B(J,K) = B(J,K) - B(ICOL,K)*T
16520         NEXT K
16530      RHS(J) = RHS(J) - RHS(ICOL)*T
16540      NEXT J
16550   NEXT I
16560 'interchange columns
16570 FOR I = NP TO 1 STEP -1
16580    IF INDEX(I,1) = INDEX(I,2) THEN 16640
16590    IROW = INDEX(I,1)
16600    ICOL = INDEX(I,2)
16610    FOR J = 1 TO NP
16620       SWAP B(J,IROW), B(J,ICOL)
16630       NEXT J
16640    NEXT I
16650 'test for singularity
16660 FOR I = 1 TO NP
16670    IF INDEX(I,3) <> 1 THEN 16700
16680    NEXT I
16690 RETURN
16700 PRINT"singular matrix error ":RETURN
17000 '**********************************************************
17010 '* SUBROUTINE DELTAP ( P, RHS, NP )                       *
17020 '*                                                        *
17030 '* Modifies parameters according to changes in rhs        *
17040 '**********************************************************
17050 FOR I = 1 TO NP
17060    P(I) = P(I) - RHS(I) * FRACT
17070    NEXT I
17080 RETURN
17500 '************************************************************************
17510 '* subroutine pslope[ p, vobs, nobs, np, nvar, dfdp(nobs,np) ]          *
17520 '* 2/1/82   by Dave Whitman                                             *
17530 '* calculates partial of the function with                              *
17540 '* respect to the each of the parameters at                             *
17550 '* each of the observations, and stores them in dfdp.                   *
17560 '************************************************************************
17570 GOSUB 11000                   'call function( fcalc, p, v, nobs, nvar, np)
17580 FOR IS = 1 TO NOBS
17590    FTEMP(IS) = FCALC(IS)
17600    NEXT IS
17610 FOR IS = 1 TO NP
17620    TP = P(IS)
17630    IF TP < 1D-20                                                                      THEN P(IS) = TP + .0005#                                                        ELSE P(IS) = TP * 1.0005#
17640    GOSUB 11000          'call function( fcalc )
17650    FOR JS = 1 TO NOBS
17660       IF TP < 1D-20                                                                      THEN DFDP(JS,IS) = (FCALC(JS) - FTEMP(JS)) / .0005#                             ELSE DFDP(JS,IS) = (FCALC(JS) - FTEMP(JS)) / (.0005# * TP)
17670       NEXT JS
17680    P(IS) = TP
17690    NEXT IS
17700 RETURN
18000 '**********************************************************************
18010 '* SUBROUTINE initialize                                              *
18020 '*                                                                    *
18030 '* Prompts for name of input file, then reads problem in              *
18040 '* from input file.                                                   *
18050 '**********************************************************************
18060 '
18070 KEY OFF : CLS
18080 LOCATE 13,20 : INPUT "Name of input file?  ",IFILE$
18090 OPEN IFILE$ FOR INPUT AS #1
18100 INPUT#1,NOBS
18110 DIM FOBS(NOBS),FCALC(NOBS),FTEMP(NOBS),OBSWT(NOBS)
18120 DIM VOBS(NOBS,NVAR),V(NOBS,NVAR),VARWT(NOBS,NVAR),DFDV(NOBS,NVAR)
18130 DIM P(NP),PNAME$(NP), DFDP(NOBS,NP)
18140 DIM IFLAG(NOBS),DLAMBDA(NOBS)
18150 INPUT#1,NUMIT
18160 INPUT#1,IUSERWT,INTERNALWT
18170 INPUT#1, FRACT    'fraction of calculated param. change to apply
18180 FOR I = 1 TO NOBS
18190    INPUT#1,FOBS(I)
18200    IF IUSERWT = 1                                                                     THEN INPUT#1,OBSWT(I)                                                           ELSE OBSWT(I) = 1#
18210    FOR J = 1 TO NVAR
18220       INPUT#1,VOBS(I,J)
18230       IF IUSERWT = 1                                                                     THEN INPUT#1,VARWT(I,J)                                                         ELSE VARWT(I,J) = 1#
18240       NEXT J
18250    NEXT I
18260 FOR I = 1 TO NP
18270    INPUT#1,PNAME$(I), P(I)
18280    NEXT I
18290 DEVSQ1 = 1D+20 : DEVSQ2 = 1D+20 'dummy devsqs for non-converge test
18300 TIME$ = "00:00:00"
18310 RETURN
19000 '**********************************************************************
19010 '* SUBROUTINE REPORT                                                  *
19020 '*                                                                    *
19030 '* Prints final report giving observed and calculated values of       *
19040 '* function, and standard deviation                                   *
19050 '* Note: assumes NEC 8023 printer. Modify to suit other printers.     *
19060 '* On NEC, esc X turns on underlining, esc Y turns it off.            *
19070 '* The following is a partial list of IBM screen charactors, followed *
19080 '* by the charactor the NEC will print: ? = ? ; ? = ?; ? = ?.         *
19090 '* Thus "? error? =" prints as "? error? =".                          *
19100 '**********************************************************************
19110 GOSUB 11000  'subroutine function
19130 LPRINT "FINAL REPORT"
19140 LPRINT "FUNCTION: "; FUNCTION$
19150 LPRINT "DATA FILE:";IFILE$
19160 LPRINT : LPRINT"   Function Values"
19170 LPRINT CHR$(27);"X" "Observed ? Calculated"; : LPRINT CHR$(27);"Y"
19180 DEVSQ = 0#
19190 FOR I = 1 TO NOBS
19200     LPRINT USING"####.##\   \####.##";FOBS(I);"  ?  ";FCALC(I)
19210     DEVSQ = DEVSQ + (FOBS(I) - FCALC(I))^2 * OBSWT(I)
19220     NEXT I
19230 LPRINT
19240 LPRINT USING "\        \####.##\                          \+#.##";                  "? error? = "; DEVSQ; "   Change, last iteration = "; DEVSQ - DEVSQ1
19250 GOSUB 19500 'subroutine covariance
19260 LPRINT: LPRINT"FINAL PARAMETERS"
19270 LPRINT CHR$(27);"X" " Name    ?  Value  ? Std.Dev."; : LPRINT CHR$(27);"Y"
19280 FOR I = 1 TO NP
19290   LPRINT USING  "\       \\\####.##\ \##.###"; PNAME$(I);"? "; P(I);" ? ";           SQR(B(I,I))
19300   NEXT I
19310 LPRINT
19320 KEY ON: RETURN
19500 '**********************************************************************
19510 '* SUBROUTINE COVARIANCE                                              *
19520 '*                                                                    *
19530 '* Calculates estimate of unit variance, then calculates              *
19540 '* variance/covariance matrix based on inverted B matrix and the      *
19550 '* variance estimate.                                                 *
19560 '**********************************************************************
19570 'estimate unit variance
19580  IF NOBS <= NP                                                                       THEN VAR = 0#  'should never trust parameters if nobs < np anyways!             ELSE VAR = DEVSQ / (NOBS - NP)
19590 'convert B to covariance matrix
19600 FOR I = 1 TO NP
19610   FOR J = 1 TO NP
19620      B(I,J) = B(I,J) * VAR
19630      NEXT J
19640   NEXT I
19650 RETURN
20000 '**********************************************************************
20010 '* subroutine finalproc                                               *
20020 '*                                                                    *
20030 '* Before nonlin stops, it makes a call to a subroutine at line 20000 *
20040 '* The user may supply a subroutine (in the file with the function    *
20050 '* subroutine) to do any final calculations using either the final    *
20060 '* parameter set and/or the variance-covariance matrix.               *
20070 '**********************************************************************
20080 RETURN
65000 ' Trap error of function file not in ascii mode
65010 IF ERR <> 54 THEN 65090
65020    CLS: BEEP : LOCATE 5,28
65030    PRINT "Bad File Mode Error:"
65040    LOCATE 7,21: PRINT "Function file must be saved in ASCII mode"
65050    LOCATE 8,15
65060    PRINT "Read lines 1200-1260 of this program for clarification."
65070    LOCATE 15,1: LIST 1200-1260
65080    LOCATE 23,1: STOP
65090 RESUME