/*--------------------------------------------------------------------------*/
/*                     Formula Manipulation Module                          */
/*--------------------------------------------------------------------------*/

#include "pcstdio.h"
#include "pcdefs.h"
#include "pcglobal.h"
/*#define TRACE	1*/		/* turns on debugging output */

#define MARK    1               /* for PartCalc() */
#define CALC    2
#define LAST    3
#define MAXFORM 25              /* Maximum length of encoded formula */
                                /* also space for about 25 stack values */
/* GLOBALS */
char *nelt;                     /* next element to be scanned on input line */
int lastelt;                    /* type of last element scanned */
double FormStk[MAXFORM];        /* Formula buffer */
int pcalc;                      /* calculations stack offset */
int calcerr = 0;                /* calculation error */
int formx, formy, xbuf;         /* x, y, and external buffer for spanning */
int Locked;                     /* comm. from EncForm() to MakFElt() */
int RecBuf;                     /* comm. from ReCalc() to BSwap() */
int GotForm;                    /* comm. from KEdit() to KFormula() */
int cwbuf, cw2buf;              /* buffers associated w/ each window */
int nsum;                       /* number of items in a sum() */
double pop(), push(), *GetVar(), *GetXVar(), *SpanRange(), flzero = 0.0;

#define LOCVAR MAXBUF+1         /* local variable in a value[8] array */
#define VARINFO 0xAB            /* var info rather than value */

/* Functions available */
#define MAXFUNC 21
#define STRANGED 11             /* start of ranged (2-param) functions */
char *funcstr[MAXFUNC] = {
        0,                      /* to prevent embedded 0's in function str */
        "not",
        "int",
        "abs",
        "log",
        "ln",
        "exp",
        "sqrt",
        "sin",
        "cos",
        "atan",
        "and",
        "or",
        "min",
        "max",
        "sum",
        "avg",
        "count",
        "if",
        "lookup",
        "npv"   };

extern double abs(), log(), ln(), exp(), sqrt(), sin(), cos(), atan();
double f_if(), not(), and(), or(), sum(), avg(), min(), max(), npv(), lookup();
double intgr(), count();
double (*funcs[MAXFUNC])() = {
        0,
        &not,
        &intgr,
        &abs,
        &log,
        &ln,
        &exp,
        &sqrt,
        &sin,
        &cos,
        &atan,
        &and,
        &or,
        &min,
        &max,
        &sum,
        &avg,
        &count,
        &f_if,
        &lookup,
        &npv    };

InitForm() {}   /* initialization from main module --only call ONCE! */

KFormula()      /* formula construction routine -- called from top level */
{   int NonFEnd(),c;
#ifdef TRACE
    Tposn(Tlines-10,1); Format("KFormula %d"); Printf(tracecnt++);
#endif 
    if (--GotForm)      /* set to 1 if called from Kedit() */
    {   if (CurLocked()) return;
        if (finfp == NULL)
        {  CmdStart("FORMULA: %c%d="); printf(letcoord(cury)); printf(curx); }
        if (FillBuf(0, NonFEnd, EntBuf, 77) == -1) goto rt;
	if (keybuf == VALDELIM) while (NextKey() != '\r');
    }
    if (!MakFElt(EntBuf, curx, cury))
    {   CmdStart("\007Bad formula");  LvCmdLine();  return;   }
rt: ZapCmdLine();  gotkey = GotForm;    /* tricky! -- watch out here */
}

NonFEnd(k)
int k;
{   return(NonTerm(k) && k != VALDELIM);   }


MakFElt(ftext, x, y)  /* make formula elt at <x,y> from 'ftext' */
char *ftext;  int x, y;
{   char *velt, *elt, *AskMem();  struct buffer *Madr();  int flen, OKform;
#ifdef TRACE
    Tposn(Tlines-9,1); Format("MakFElt %d "); Printf(tracecnt++);
#endif 
    OKform = 0;
    if (!(flen=EncForm(ftext, FormStk))) return(0);	/* encode formula */
    if (velt=AskMem(FORMTXT+flen-1))
    {   *((elt=(char *)Madr(velt))+TYPE) = FORMULA;
#ifdef TRACE
	Format("elt:%d "); Printf((unsigned)elt);
	Format("T:%c "); Printf(*(elt+TYPE));
	Format("velt:%d "); Printf(*((int *)(&velt)+1));
	Format("%d "); Printf(*(int *)&velt);
#endif
        copystr(FormStk, elt+FORMTXT);
        *((int *) (elt+RELABS)) = (Locked)? -1 : NORELABS;/* initially unset */
	if (finfp != NULL) {OKform = 1;  *((double *)(elt+VALUE)) = ERRVALUE;}
          else OKform = CalcForm(elt+FORMTXT, elt+VALUE);
        PutElt(velt, x, y);
    }
    return(OKform);
}

copystr(s1, s2) /* string copy; handle embedded 0's in forms */
char *s1, *s2;
{   int i;
    while (*s1)
    {   switch (*s1)
        {   case EXTELT:   i = 3;  while (i--) *s2++ = *s1++;
                           while (*s1) *s2++ = *s1++;  break;
            case VARELT:   *s2++ = *s1++;  *s2++ = *s1++;  break;
        }
        *s2++ = *s1++;
    } *s2 = 0;
}

EncForm(ftext, fstr)    /* encode a formula and return new length */
char *ftext, *fstr;
{   int nfunc;  char name[10];  char *fp, *begstr, *EncVar();
#ifdef TRACE
    Tposn(Tlines-8,1); Format("EncForm %d"); Printf(tracecnt++);
#endif 
    begstr = fstr;  Locked = 0;
    while (*ftext)
    {   if (isalpha(*ftext))
        {   fp = name;                          /* first, make a copy */
            while (isalpha(*ftext) || isnumber(*ftext))
		if (fp >= &name[9]) return(0); else *fp++ = *ftext++;
	    *fp = 0;
            if (*ftext == '[')                  /* is a buffer reference */
            {   *fstr++ = EXTELT;
                if (!(ftext=EncVar(++ftext, fstr))) return(0);
                copystr(name, (fstr += 2));  fstr += fp-name+1;  ++ftext;
            } else if (EncVar(name, fstr+1))    /* is a var reference */
            {   *fstr = VARELT;  fstr += 3;
            } else                              /* is a function reference */
            {   for (nfunc = 1; nfunc < MAXFUNC; nfunc++)
                    if (strcmp(name, funcstr[nfunc])) break;
                if (nfunc == MAXFUNC) return(0);
                *fstr++ = FUNCELT;  *fstr++ = nfunc;
            }
        } else
        if (isnumber(*ftext))           /* scan over a real number */
        {   fp = ftext;  getnum(&ftext, name, 0);
            while (fp < ftext) *fstr++ = *fp++;
        } else
        if (*ftext == '@') {++ftext;  Locked = 1;}
          else *fstr++ = *ftext++;      /* anything else */
    }
    *fstr = 0;  return(fstr-begstr);
}

char *EncVar(ftext, fstr)       /* encode variable from ftext into fstr */
char *ftext, *fstr;             /* return new ftext pointer value */
{   static int line;
#ifdef TRACE
    Tposn(Tlines-7,1); Format("EncVar %d"); Printf(tracecnt++);
#endif 
    if (!isalpha(*ftext)) return(0);    /* must start w/ alpha */
    /* encode as <y,x> for processing efficiency */
    *fstr++ = numcoord(*ftext++);
    if ((line=toint(&ftext)) < 1 || line > MAXLINE) return(0);
      else *fstr = line;        /* must be between 1 and MAXLINE */
    return(ftext);
}

DecForm(elt, ftext, emphvar)    /* decode formula into original string */
char *elt, *ftext;  int emphvar;
{   char *np, *begtxt, *fstr;  int nvar, i, type;
    begtxt = ftext;  nvar = 0;  fstr = elt+FORMTXT;
    while (*fstr)
      switch (type = *fstr)
      { case EXTELT:    np = fstr+3;  while (*np) *ftext++ = *np++;
                        *ftext++ = '[';
        case VARELT:    if (++nvar == emphvar)
                        {   if (Trvstr[i=0])
                                while (Trvstr[i]) *ftext++ = Trvstr[i++];
                            else *ftext++ = '>';
                        }
                        *ftext++ = letcoord(*++fstr);
                        fromint((*++fstr), &ftext);
                        if (nvar == emphvar)
                        {   if (Tnvstr[i=0])
                                while (Tnvstr[i]) *ftext++ = Tnvstr[i++];
                            else *ftext++ = '<';
                        }
                        if (type == EXTELT) {while (*++fstr) ; *ftext++ = ']';}
                        ++fstr;  break;
        case FUNCELT:   fstr++;  copystr(funcstr[*fstr++], ftext);
                        while (*ftext) ++ftext;  break;
        default:        *ftext++ = *fstr++;
      }
    if ((*((int *)(elt+RELABS))) & 0x8000) *ftext++ = '@';
    *ftext = 0;  return(begtxt);
}

/* This function, which performs a recalculation across buffers, 
   may require the BUF overlay to be loaded.  Hence, it must NOT be
   called from any of the other overlays directly.  Simple solution so far
   has been to post a keystroke for KReCalc() when exiting an overlaid
   function which needs it.  Note also that the #define BSWAP is required
   to access the function BSwap() via the *Functions[]() table, and that
   the global RecBuf, known only to this module and the BUF overlay, is
   used to pass which buffer to swap in.
*/

ReCalc()          /* recalculate update region */
{   int seq[MAXBUF], temp[MAXBUF], i, j;
    Tnorm();  CondPrt(curx,cury);       /* print current elt */
#ifdef TRACE
    Tposn(Tlines-2,1); Format("ReCalc %d"); Printf(tracecnt++);
#endif 
    /* remember buffers for each window in globals for Update() */
    cwbuf = w->curbuf;  cw2buf = (w2->active)? w2->curbuf : -1;

    /* trace dependency links forward to get who current depends on */
    i = 0;  temp[i++] = j = w->curbuf;
    while ((j=link(j)) != -1 && not_in(j, temp, i)) temp[i++] = j;
    j = 0;  while (i) seq[j++] = temp[--i];     /* reverse in seq[] */

    /* trace backward to get who depends on current buffer */
    while ((i=depends_on(seq[j-1])) != -1 && not_in(i, seq, j))
        seq[j++] = i;

    /* NOTE: circular references anywhere stop the sequence! */
    /* do the updates forward through seq[] of buffers */
    for (i=0; i<j; i++)
    {   RecBuf = seq[i];  if (w->curbuf != RecBuf) BSwap();
        BReCalc();
    }

    /* reswap current buffer if required */
    if (RecBuf != cwbuf) {RecBuf = cwbuf;  BSwap();}
    movecur(curx,cury);
}

not_in(n, array, len)           /* 'n' is NOT IN array of length 'len' */
int n, array[], len;
{   while (len--) if (n == array[len]) return(0); return(1);   }

link(n)                 /* returns link of Buff[n] */
int n;                  /* or -1 if none */
{   return(Buff[n]? Madr(Buff[n])->linked : -1);   }

depends_on(n)           /* returns any Buff[i] which depends on 'n' */
int n;                  /* or -1 if none */
{   int i;
    for (i=0; i<MAXBUF; i++) if (link(i) == n) return(i);
    return(-1);
}

BReCalc()       /* recalculate current buffer */
{   
#ifdef TRACE
    Tposn(Tlines-2,20); Format("BReCalc %d"); Printf(tracecnt++);
#endif 
PartCalc(MARK);  while (PartCalc(CALC)) ;  PartCalc(LAST);    }

PartCalc(mode)          /* part of ReCalc -- varies depending on mode */
int mode;
{   int x, y, xstart, xstop, ystart, ystop, updated, error;
    struct buffer *cb;  char *elt, *GetElt();

#ifdef TRACE
    Tposn(Tlines-1,1); Format("PartCalc %d "); Printf(tracecnt++);
#endif
    cb = Madr(Buff[w->curbuf]);
    if (cb->updx[0])    /* update region is defined */
    {   xstart = cb->updx[0];  xstop = cb->updx[1];
        ystart = cb->updy[0];  ystop = cb->updy[1];
    }
    else
    {   xstart = ystart = 1;  xstop = lastx;  ystop = lasty;   }
    updated = 0;
    for (x=xstart; x <= xstop; x++)
    for (y=ystart; y <= ystop; y++) {
	elt = GetElt(x,y);
#ifdef TRACE
	Format("elt %d "); Printf((unsigned)elt);
	Format("type %c ");Printf(*(elt+TYPE));
	Format("x %d "); Printf(x);
	Format("y %d "); Printf(y);
	Format("sc:%d "); Printf(*( (int *)(screen[x])+1) );
	Format("%d ");Printf(screen[x]);
	Format("M:%d "); Printf(*((long *)Madr(screen[x])+1+y)-100000L);
#endif	    
        if (elt && *(elt+TYPE) == FORMULA)
        {   error = !OKNum(elt);
            switch(mode)
            {   case MARK:  *((double *)(elt+VALUE)) = ERRVALUE;  break;
                case CALC:  if (error && CalcForm(elt+FORMTXT,elt+VALUE))
			    {
				++updated; 
				}
			    else break;
		case LAST:  if (error)
			    {   if (RecBuf == cwbuf) WCondPrt(w,x,y);
			        if (RecBuf == cw2buf) WCondPrt(w2,x,y);
			    }  break;
            	}
            }
	}
    return(updated);
}

CalcForm(line, answer)  /* Interpret formula from 'line' */
char *line;  double *answer;
{   char opstack[50], value[11], *opsp;  double *vp, *(*fp)();
    int basepri, pri, type;
#ifdef TRACE
    Tposn(Tlines-5,1);Format("CalcForm %d "); Printf(tracecnt++);
#endif

    opsp = opstack;  nelt = line;
    *opsp = *(opsp+1) = 0;  basepri = lastelt = pcalc = calcerr = 0;
    while (type=NextFElt(value))
    {   pri = basepri;
        switch (type)
        {   case '(':       basepri += 5;  break;
            case ')':       basepri -= 5;  break;
                                        /* functions' priorities here */
            case '^':       pri++;      /* exponentiation */
            case '*':
            case '/':       pri++;      /* mul/div at this priority */
            case '+':
            case '-':       pri++;      /* add/subtract at this priority */
            case '<':                   /* less than */
            case '[':                   /* less than or equal to */
            case '=':                   /* equal to */
            case '#':                   /* not equal to */
            case ']':                   /* greater than or equal to */
            case '>':                   /* greater than */
            case ',':   fn: while (*opsp && pri <= *(opsp+1))
                            {   DoOp(*opsp);  opsp -= 2;  }
                            if (type == ',') break;
                            if ((opsp += 2) > opstack+49) goto er;
                            *opsp = type;  *(opsp+1) = pri;
                            break;
            case EXTELT:
            case VARELT:    if (*nelt == ':') /* first element of a range */
                            {   push(value);  ++nelt;
                                if (NextFElt(value) != type) goto er;
                                push(value);  break;    }
                            if (type == VARELT) fp = &GetVar; else
                            {   if ((xbuf=value[3]) == 0xFF) goto er;
                                fp = &GetXVar;
                            }
                            if (!(vp=(*fp)(value[0], value[1]))) goto er;
                              else push((vp == -1)? &flzero : vp);  break;
            case NUMELT:    /* done in NextFElt() */ break;
            case BADTYPE:   goto er;
            default:        if (type < MAXFUNC) {pri += 4;  goto fn;}
                            goto er;
        }
    }
    while (*opsp)   /* pop rest of opstack */
    {   DoOp(*opsp);  opsp -= 2; }
    if (!(calcerr || pcalc != 1 || basepri != 0)) {pop(answer);  return(1);}
 er: *answer = ERRVALUE;  return(0);    /* error return */
}

double *GetVar(x, y)    /* returns ptr to num var value */
int x, y;               /* 0 if error */
{   int use;  char *elt;  double *get_var();
    if (!x || !y) {rterr();  return(0);}
    return(get_var(GetElt(x&0377, y)));
}

double *GetXVar(x, y)           /* returns ptr to num external var value */
int x, y;
{   struct buffer *b;  char **sp;
    b = Madr(Buff[xbuf]);
    if (!b->bscp || x > b->blx || y > b->bly) return(flzero);
    sp = (char **) Madr(b->bscp) + x;           /* pointer to A:screen[x] */
    sp = (char **) Madr(*sp) + y;               /* pointer to A:screen[x]+y */
    return(get_var(Madr(*sp)));                 /* get the element */
}

/* part of Buffer Abstraction which is referenced outside itself */
BNdxBuf(bbname) /* returns index if there is a buffer named 'name'; else -1 */
char *bbname;   /* used here and in PCBUF */
{   int i;  struct buffer *bp;
    for (i=0; i<MAXBUF; i++)
        if ((bp=Madr(Buff[i])) && strcmp(bp->bname,bbname)) return(i);
    return(-1);
}

BDerivName(fn, bn)      /* derive a buffer name from a filename */
char *fn, *bn;          /* used in PCBUF & PCIO */
{   int i;
    i = 8;  if (*(fn+1) == ':') fn += 2;
    while ((isalpha(*fn) || isnumber(*fn)) && i--) 
	*bn++ = tolower(*fn++);
    *bn = 0;
}

double *get_var(elt)            /* common get variable code */
char *elt;
{   if (elt && !OKNum(elt)) {rterr();  return(0);}
    return((IsNumElt(elt))? elt+VALUE : -1);
}

OKNum(elt)      /* returns TRUE if numeric value is OK and not an error */
char *elt;
{   return(*((double *)(elt+VALUE)) != ERRVALUE);   }

DoOp(opr)       /* Do an operation */
char opr;
{   double op1, op2, op3, (*fp)(), pow();
    static double err_val = ERRVALUE;
    if (calcerr) return(0);	/* do nothing if error already */
    pop(&op2);  formx = 0;      /* always want at least one operand */
    switch (opr)                /* spanning global 'formx' reset here */
    {   case '+':       op3 = pop(&op1) + op2;  break;
        case '-':       op3 = pop(&op1) - op2;  break;
        case '*':       op3 = pop(&op1) * op2;  break;
        case '/':       op3 = pop(&op1) / op2;  if (!op2) rterr();
			break;
        case '^':       op3 = pow(pop(&op1), op2);  break;
        case '<':       op3 = (pop(&op1) < op2)? 1 : 0;  break;
        case '[':       op3 = (pop(&op1) <= op2)? 1 : 0;  break;
        case '=':       op3 = (pop(&op1) == op2)? 1 : 0;  break;
        case '#':       op3 = (pop(&op1) != op2)? 1 : 0;  break;
        case ']':       op3 = (pop(&op1) >= op2)? 1 : 0;  break;
        case '>':       op3 = (pop(&op1) > op2)? 1 : 0;  break;
        default:        fp = funcs[opr];
                        if (opr < STRANGED)
                        {   op3 = (*fp)(op2);  break;   }
                        else 
                        {   pop(&op1);  op3 = (*fp)(&op1,&op2);  break;   }
    }
    push((calcerr)? &err_val : &op3);
}       

NextFElt(value)
char value[];
{   while (*nelt == ' ') nelt++; /* ignore blanks */
    switch (*nelt)
    {   case 0:         return(0);      /* end of input string */
#ifdef TRACE
    Tposn(Tlines-4,1);Format("NextFElt %d "); Printf(tracecnt++);
#endif
/* Below we return inside value[8] not a number but the coordinates of
   a variable, as follows:
        value[0] = integer x coord
        value[1] = integer y coord
        value[2] = VARINFO
        value[3] = LOCVAR or buffer # if external
*/
        case EXTELT:    lastelt = EXTELT;
                        if ((value[3]=BNdxBuf(nelt+3)) == -1)  goto er;
                        goto sv;
        case VARELT:    lastelt = VARELT;  value[3] = LOCVAR;
                     sv:nelt++;  value[2] = VARINFO;
                        if(!(value[1] = *nelt++) || !(value[0] = *nelt++))
                        {er:rterr();  return(0);   }
                        if (lastelt == EXTELT) while (*nelt++) ;
                        return(lastelt);
        case FUNCELT:   nelt++;  return(lastelt = *nelt++);
        case '+':
        case '-':       if (lastelt == NUMELT || lastelt == VARELT ||
                            lastelt == EXTELT)
                            return(lastelt = *nelt++);  /* binary operator */
        case '.':
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':       if (pcalc >= MAXFORM) return(BADTYPE);
                        getnum(&nelt, &FormStk[pcalc], 1);
                        pcalc++;  return(lastelt=NUMELT);
        case '<':       if (*++nelt == '>') {++nelt;  return(lastelt='#');}
                        if (*nelt == '=') {++nelt;  return(lastelt='[');}
                        return(lastelt='<');
        case '>':       if (*++nelt == '=') {++nelt;  return(lastelt=']');}
                        return(lastelt='>');
        /* must be single-char operator if we ever get here */
        default:        if (*nelt != '(' && *nelt != ')') lastelt = *nelt;
                        return(*nelt++);
    }
}

double push(pd)     /* push value onto stack */
double *pd;
{   if (pcalc < MAXFORM) FormStk[pcalc++] = *pd;
      else rterr();  return(*pd);
}

double pop(pd)      /* pop value from stack and return it */
double *pd;
{   if (pcalc > 0) *pd = FormStk[--pcalc];
      else {rterr();  *pd = 0.0;}
    return(*pd);
}

rterr()         /* called for run-time errors */
{   calcerr = -1;    }

double intgr(f)
double f;
{   long i;  i = f;  return((double) i);   }

/* ----------------- Ranged Functions follow -------------------------- */

double not(val)         /* returns !(val) */
double val;
{   return((val != 0.0)? 0.0 : 1.0);   }

double and(first, second)       /* returns first && second */
double *first, *second;
{   return((*first != 0.0 && *second != 0.0)? 1.0 : 0.0);   }

double or(first, second)        /* returns first || second */
double *first, *second;
{   return((*first != 0.0 || *second != 0.0)? 1.0 : 0.0);   }

double sum(start, stop) /* sum over a range */
char start[8], stop[8];
{   double *vp, run_sum;
    nsum = 0;  run_sum = 0.0;
    while (vp=SpanRange(start, stop))
    {   run_sum += *vp;  ++nsum;   }
    return(run_sum);
}

double *SpanRange(start, stop)  /* returns pointer to next value */
char start[8], stop[8];
{   double *vp;  static double *(*fp)();
    if (!formx) /* not initialized yet */
    {   if (start[2] != VARINFO || stop[2] != VARINFO) {rterr();  goto rt;}
        xbuf = start[3];  fp = (xbuf == LOCVAR)? &GetVar : &GetXVar;
        if ((vp=(*fp)(formx=start[0], formy=start[1])) != -1) return(vp);
    }
    while (++formy<=stop[1] || (++formx <= (stop[0]) && (formy=start[1])))
        if ((vp=(*fp)(formx, formy)) != -1) return(vp);  
 rt:return(0);
}

double avg(start, stop) /* compute average over a range */
char start[8], stop[8];
{   return(sum(start, stop)/nsum);   }

double count(start, stop)       /* count # numeric values over a range */
char start[8], stop[8];
{   sum(start, stop);  return((double) nsum);   }

double cmp_range(test, start, stop)      /* comparison over a range */
int (*test)();  char start[8], stop[8];
{   double *vp, *cp, *choose();
    if (!(cp=SpanRange(start, stop))) return(0.0);
    while (vp=SpanRange(start, stop)) cp = choose(test, vp, cp);
    return(*cp);
}

double *choose(test, first, second) /* choose first or second */
int (*test)();  double *first, *second;
{   return(((*test)(*first, *second))? first : second);   }

double min(start, stop) /* minimum over a range */
char start[8], stop[8];
{   int f_lt();  return(cmp_range(f_lt, start, stop));   }

double max(start, stop) /* maximum over a range */
char start[8], stop[8];
{   int f_gt();  return(cmp_range(f_gt, start, stop));   }

double f_if(trueval, falseval)          /* if function */
double *trueval, *falseval;
{   double boolexp, result;
    result = (pop(&boolexp) != 0.0)? *trueval : *falseval;
    calcerr = 0;                /* reset; then check again below */
    if (boolexp == ERRVALUE || result == ERRVALUE) rterr();
    return(result);
}

double npv(start, stop) /* Net Present Value -- TOS = discount rate */
char start[8], stop[8];
{   double discount, powdis, run_val, *vp;
    discount = powdis = pop(&run_val) + 1.0;  run_val = 0.0;
    while (vp=SpanRange(start, stop))
    {   run_val += *vp/powdis;  powdis *= discount;   }
    return(run_val);
}

double lookup(start, stop)      /* VisiCalc Table Lookup routine */
char start[8], stop[8];
{   double search, *vp;
    pop(&search);
    while (vp=SpanRange(start, stop)) if (search < *vp) break;
    if (start[0] == stop[0]) {--formy;  if (vp) ++formx;}
      else {--formx;  if (vp) ++formy;}
    vp = (xbuf == LOCVAR)? GetVar(formx, formy) : GetXVar(formx, formy);
    return((vp && vp != -1)? *vp : 0.0);
}
