/*
 * BASIC by Phil Cockcroft
 */
#include        "bas.h"

/*
 *      This file contains the numeric evaluation routines and some
 *    of the numeric functions.
 */

/*
 *      evalint() is called by a routine that requires an integer value
 *    e.g. string functions. It will always return an integer. If
 *    the result will not overflow an integer -1 is returned.
 *      N.B. most ( all ) routines assume that a negative return is an
 *    error.
 */

union	ffn_vars {
	struct	{
		value	_ovr;
		value	_nvr;
	} _ivs;
	struct	{
		STR	_ostr;
		STR	_nstr;
	} _svs;
};

#define	ovr	_ivs._ovr
#define	nvr	_ivs._nvr
#define	ostr	_svs._ostr
#define	nstr	_svs._nstr

#ifdef	__STDC__
static	void	recov_parms(struct entry **, int, union ffn_vars *, int);
static	void	setdrg(int);
static	void	hyper_sc(int);
#else
static	void	recov_parms();
static	void	setdrg();
static	void	hyper_sc();
#endif

#ifndef SOFTFP
#ifdef	__STDC__
extern	double  sin(double);
extern	double  cos(double);
extern	double	asin(double);
extern	double	acos(double);
extern	double  atan(double);
extern	double	exp(double);
extern	double	log(double);
extern	double  sqrt(double);
#else
extern	double  sin();
extern	double  cos();
extern	double	asin();
extern	double	acos();
extern	double  atan();
extern	double	exp();
extern	double	log();
extern	double  sqrt();
#endif
static	const	double	logmaxval = LOGMAXVAL;
static	const	double	TWO = 2.0;
static	const	double	INSIG = MAX_INSIG;
static	const	double	logof2 = 0.69314718055994530942;
#endif

itype
evalint()
{
	eval();
	if(vartype != RVAL)
		return(res.i);
	if(conv(&res)){
		error(INTOVER);
#if 0
		if(res.f < ZERO)
			res.i = -MAX_INT-1;
		else
			res.i = MAX_INT;
#endif
	}
	return(res.i);
}

/*
 * evalreal is called in a similar manner to evalint but it always returns
 * a real value (in res).
 */

void
evalreal()
{
	eval();
	if(vartype != RVAL){
		cvt(&res);
		vartype= RVAL;
	}
}

/*
 *      This structure is only ever used by eval() and so is not declared
 *    in 'bas.h' with the others.
 */

struct  m {
	value   r1;
	int     lastop;
	int	mvalue;
	char    vty;
};

/*
 *      eval() will evaluate any numeric expression and return the result
 *    in the UNION 'res'.
 *      A valid expression can be any numeric expression or a string
 *    comparison expression e.g. "as" <> "gh" . String expressions can
 *    themselves be used in relational tests and also be used with the
 *    logical operators. e.g. "a" <> "b" and "1" <> a$ is a valid
 *    expression.
 */

#define	SETNOT		1
#define	SETMINUS	2

void
eval()
{
	register int    c;
	register struct	m	*j;
	register int    i;
	value	*pp;
	int	firsttime;
	int	unaries[2];
	struct	m	restab[6];

	j=restab;
	j->mvalue=0;
	unaries[0] = unaries[1] = 0;
	firsttime = 1;

for(;;){
	c=getch();
	if(c=='-' && firsttime){
		if((unaries[0] == SETMINUS) || unaries[1])
			error(SYNTAX);
		unaries[1] = unaries[0];
		unaries[0] = SETMINUS;
		continue;
	}
	else if(c==NOTT){
		if(unaries[0] == SETNOT){
			unaries[0] = unaries[1];
			unaries[1] = 0;
			firsttime++;
			continue;
		}
		if(unaries[1])
			error(SYNTAX);
		unaries[1] = unaries[0];
		unaries[0] = SETNOT;
		firsttime++;
		continue;
	}
	else if(c & SPECIAL){
		if(c == IFUNCN)	/* functions that don't have brackets */
			(*functs[*point++ & 0177])();
		else if(c == IFUNCA){	/* functions that do have brackets */
			c = (int)*point++ & 0177;
			if(*point++ !='(')
				error(SYNTAX);  /* functions that do */
			(*functb[c])();
			if(getch()!=')')
				error(SYNTAX);
		}
		else if(c == OFUNC)
			error(SYNTAX);
		else if(c == FN)
			ffn((struct entry *)0, (STR)0);
		else
			goto err1;
#ifdef	NaN
		if(vartype == RVAL && NaN(res.f))
			(*fpfunc)();
#endif
	}
	else if(isletter(c)){
		CHAR    *sp = --point;

		pp= (value *)getname(0);         /* we have a variable */
#if 0
		if(pp == 0){
			point = sp;
			ffn((struct entry *)0, (STR)0);
#ifdef	NaN
			/*LINTED*/
			if(vartype == RVAL && NaN(res.f))
				(*fpfunc)();
#endif
			goto ex;
		}
#endif
		if(vartype== SVAL){       /* a string !!!!!! */
			if(firsttime){  /* no need for checktype() since */
				point = sp;     /* we know it's a string */
				stringcompare();
				goto ex;
			}
			else error(2);          /* variable required */
		}
		if(vartype == IVAL)
			res.i = pp->i;
		else
			res = *pp;
	}
	else if(isnumber(c) || c=='.' || c == '&'){
		if(!getnumb(--point, &point))   /* we have a number */
			error(36);      	/* bad number */
	}
	else if(c=='('){                /* bracketed expression */
		eval();                 /* recursive call of eval() */
		if(getch()!=')')
			error(SYNTAX);
	}
	else  {
err1:           /* get here if the function we tried to access was not   */
		/* a legal maths func. or a string variable */
		/* stringcompare() will give a syntax error if not a valid */
		/* string. therefore this works ok */
		point--;
		if(!firsttime)
			error(SYNTAX);
		stringcompare();
	}
ex:
	/*
	 * now perform unary operations.
	 * only do this if we have some
	 */
	if(unaries[0]){
		if(unaries[0] == SETMINUS)
			negate();	/* unary minus */
		else
			notit();	/* unary not */
		if(unaries[1] == SETMINUS)
			negate();
		else if(unaries[1] == SETNOT)
			notit();
		unaries[0] = unaries[1] = 0;
	}
	firsttime = 1;
	switch(c = getch()){            /* get the precedence of the */
	case '^':			/* operator */
		i = 5;
		break;
	case '*':
	case '/':
	case '\\':
	case MODD:
		firsttime = 0;
		i = 4;
		break;
	case '+':
	case '-':
		firsttime = 0;
		i = 3;
		break;
	case APRX:
	case EQL:            /* comparison operators */
	case LTEQ:
	case NEQE:
	case LTTH:
	case GTEQ:
	case GRTH:
		i = 2;
		break;
	case ANDD:		/* logical operators */
	case ORR:
	case XORR:
	case IMPP:
	case EQVV:
		i = 1;
		break;
	default:
		i=0;
		break;
	}

#if 1
	while(j->mvalue >= i){
		if(! j->mvalue ){               /* end of expression */
			point--;
			return;
		}
		if(j->vty!=vartype){            /* make both parameters */
			if(vartype != RVAL)             /* the same type */
				cvt(&res);
			else
				cvt(&j->r1);  /* if changed then they must be */
			vartype= RVAL;              /* changed to reals */
		}
		(*mbin[(j->mvalue<<1)+vartype])(&j->r1,&res,j->lastop);
#ifdef	NaN
		if(vartype == RVAL && NaN(res.f))
			(*fpfunc)();
#endif
		j--;                    /* execute it then pop the stack and */
	}				/* deal with the next operator */
	(++j)->lastop=c;                        /* precedence */
	j->r1 = res;
	j->mvalue= i;
	j->vty=vartype;
#else
ame:    if(j->mvalue < i){         /* current operator has higher */
		(++j)->lastop=c;                        /* precedence */
		j->r1 = res;
		j->mvalue= i;
		j->vty=vartype;
		continue;
	}
	if(! j->mvalue ){               /* end of expression */
		point--;
		return;
	}
	if(j->vty!=vartype){            /* make both parameters */
		if(vartype != RVAL)             /* the same type */
			cvt(&res);
		else
			cvt(&j->r1);    /* if changed then they must be */
		vartype= RVAL;              /* changed to reals */
	}
	(*mbin[(j->mvalue<<1)+vartype])(&j->r1,&res,j->lastop);
#ifdef	NaN
	if(vartype == RVAL && NaN(res.f))
		(*fpfunc)();
#endif
	j--;                    /* execute it then pop the stack and */
	goto ame;               /* deal with the next operator */
#endif
	}
}

/*
 *      The rest of the routines in this file evaluate functions and are
 *    relatively straight forward.
 */

void
tim()
{
#ifndef	__STDC__
	long	t;
#else
	time_t	t;
#endif
	VOID time(&t);

#ifndef SOFTFP
#ifdef	BIG_INTS
	res.i = t;
	vartype = IVAL;
#else
	res.f = t;
	vartype = RVAL;
#endif
#else
	overfl = t;
	over(0,&res);           /* convert from long to real */
#endif
}

#ifdef	RAND48

extern	double	drand48(void);
extern	long	lrand48(void);
extern	void	srand48(long);

void
rnd()
{
	itype	rnumb;

	if(*point != '('){
		res.i = (itype)lrand48();
		vartype = IVAL;
		return;
	}
	point++;
	rnumb = evalint();
	if(getch()!=')')
		error(SYNTAX);
	if(rnumb == 0){
		res.f = drand48();
		vartype = RVAL;
	}
	else {
		res.i = lrand48() % rnumb + 1;
		vartype = IVAL;
	}
}

/*
 *      This routine is the command 'random' and is placed here for some
 *    unknown reason it just sets the seed to rnd to the value from
 *    the time system call ( is a random number ).
 */

int
brandom()
{
	long    m;

	VOID time(&m);
	srand48((long)m);
	normret;
}

#else

void
rnd()
{
	static  const	double  recip32 = 32767.0;
#ifdef	SOFTFP
	value   temp;
#endif
	register int    rn;

	rn = rand() & 077777;
	if(*point!='('){
		res.i= (short)rn;
		vartype= IVAL;
		return;
	}
	point++;
	eval();
	if(getch()!=')')
		error(SYNTAX);
	if(!IS_ZERO(res)){
		if(vartype == RVAL && conv(&res))
			error(FUNCT);
		res.i= rn % res.i + 1;
		vartype= IVAL;
		return;
	}
#ifndef SOFTFP
	res.f = (double)rn / recip32;
#else
	temp.i=rn;
	cvt(&temp);
	res = *( (value *)( &recip32 ) );
	fdiv(&temp,&res);            /* horrible */
#endif
	vartype = RVAL;
}

/*
 *      This routine is the command 'random' and is placed here for some
 *    unknown reason it just sets the seed to rnd to the value from
 *    the time system call ( is a random number ).
 */

int
brandom()
{
	long    m;

	VOID time(&m);
	srand((int)m);
	normret;
}

#endif

void
erlin()
{
	res.i = (itype)elinnumb;
	vartype= IVAL;
#ifndef	BIG_INTS
	if(res.i < 0 ){                      /* make large linenumbers */
#ifndef SOFTFP
		res.f = (unsigned)elinnumb;
		vartype = RVAL;
#else
		overfl=(unsigned)elinnumb;      /* into reals as they */
		over(0,&res);                   /* overflow integers */
#endif
	}
#endif
}

void
erval()
{
	res.i = (itype)ecode;
	vartype= IVAL;
}

void
sgn()
{
	eval();
#ifndef UNPORTABLE
	if(vartype == RVAL){
		if(res.f < ZERO)
			res.i = -1;
		else if(res.f > ZERO)
			res.i = 1;
		else res.i = 0;
		vartype = IVAL;
		return;
	}
#endif
	if(res.i<0)             /* bit twiddling */
		res.i = -1;     /* real numbers have the top bit set if */
	else if(res.i>0)        /* negative and the top word is non-zero */
		res.i= 1;       /* for all non-zero numbers */
	vartype=IVAL;
}

void
babs()
{
	eval();
#ifndef UNPORTABLE
	if(vartype == RVAL){
		if(res.f < ZERO)
#ifndef	SOFTFP
			res.f = -res.f;
#else
			negate();
#endif
		return;
	}
#endif
	if(res.i<0)
		negate();
}

void
len()
{
	register STR	st;

	st = stringeval();
	res.i = (itype)st->strlen;
	vartype= IVAL;
	FREE_STR(st);
}

void
ascval()
{
	register STR	st;

	st = stringeval();
	if(!st->strlen)
		error(FUNCT);
	res.i = (itype)UC(*st->strval);
	vartype= IVAL;
	FREE_STR(st);
}

void
bsqrtf()
{
	evalreal();
	if(res.f < ZERO){
		c_error(37);      /* negative square root */
		return;
	}
#ifndef SOFTFP
	res.f = sqrt(res.f);
#else
	sqrt(&res);
#endif
}

void
blogf()
{
	evalreal();
	if(res.f <= ZERO){
		c_error(38);      /* bad log value */
		return;
	}
#ifndef SOFTFP
	res.f = log(res.f);
#else
	log(&res);
#endif
}

void
blog10f()
{
	static	const	double	log10val = 2.30258509299404568402;

	evalreal();
	if(res.f <= ZERO){
		c_error(38);      /* bad log value */
		return;
	}
#ifndef SOFTFP
	res.f = log(res.f) / log10val;
#else
	log(&res);
	fdiv(&log10val, &res);
#endif
}

void
bexpf()
{
	evalreal();
#ifndef SOFTFP
	if(res.f > logmaxval){
		c_error(39);
		res.f = logmaxval;
	}
	res.f = exp(res.f);
#else
	if(!exp(&res))
		error(39);      /* overflow in exp */
#endif
}

void
pii()
{
#ifndef SOFTFP
	res.f = pivalue;
#else
	movein(&pivalue,&res);
#endif
	vartype= RVAL;
}

/*
 *      This routine will deal with the eval() function. It has to do
 *    a lot of moving of data. to enable it to 'compile' an expression
 *    so that it can be evaluated.
 */

void
evalu()
{
	register CHAR   *tmp;
	register STR	st;
	int	c;

	if(evallock>10)
		error(43);      /* mutually recursive eval */
	evallock++;
	st = stringeval();
	if(st->strlen > MAXLIN-1)
		error(10);
	else if(!st->strlen)
		error(SYNTAX);
	*strmov(line, st->strval, st->strlen) = 0;
#if 0
	/*
	 * when compiling, the resultant string will be less than or equal
	 * to the length of the original string
	 */
	st->strlen = 0;			/* defeat default copy action */
	RESERVE_SPACE(st, MAXLIN);
#endif
	VOID compile(0, st->strval, 1);
	tmp=point;
	point = st->strval;
	eval();
	c = getch();
	point=tmp;
	evallock--;
	FREE_STR(st);
	if(c)
		error(SYNTAX);
}

void
ffn(pep, strp)
struct	entry	*pep;
STR	strp;
{
	register struct  deffn   *p;
	register struct	entry	*ep;
	register int    i;
	union	ffn_vars *cur_arg;
	struct	entry	**rp, *rep;
	CHAR    *spoint;
	struct	forst	*fp;
	char	vty;
	char	ctype;
	STR	st;
	STR	retst = 0;
	union	ffn_vars args[FN_MAX_ARGS];

	if( (ep = pep) == 0){
		if(!ispletter(point))
			error(SYNTAX);
		ep = getnm(ISFUNC, 0);
		if(!ep)
			error(UNDEFFN);
		ctype = IS_MFN;
		vty = vartype;
		if( (strp && vty != SVAL) || (!strp && vty == SVAL))
			error(UNDEFFN);
		retst = strp;
	}
	else
		ctype = IS_MPR;
	p = ep->_deffn;
	if(p->narg){
		if(*point++!='(')
			error(SYNTAX);
		rp = p->vargs;
		for(cur_arg = args, i=0 ;; cur_arg++, rp++){
			rep = *rp;
			if(rep->vtype == SVAL){
				if(rep->flags & IS_FSTRING)
					error(3);
				st = ALLOC_STR( (ival)0);
				st->strval = rep->_dst.str;
				st->strlen = rep->_dst.len;
				RESERVE_SPACE(st, (ival)rep->_dst.len);
				cur_arg->ostr = st;
				cur_arg->nstr = stringeval();
			}
			else {
				cur_arg->ovr = rep->_dval; /* save values */
				eval();
				putin(&cur_arg->nvr,
						(int) (rep->vtype & NVALMASK));
			}
			if(++i >= p->narg)
				break;
			if( getch() != ',' )
				error(SYNTAX);
		}
		if( getch() != ')' )
			error(SYNTAX);
					      /* got arguments in nvrs[] */
					      /* put in new values */

		rp = p->vargs;
		for(cur_arg = args, i=0; i < p->narg; i++, cur_arg++, rp++){
			rep = *rp;
			if(rep->vtype == SVAL)
				stringassign(&rep->_dst, rep, cur_arg->nstr, 1);
			else
				rep->_dval = cur_arg->nvr;
		}
	}
	if(p->mline != IS_FN){
		if(p->mline != ctype)
			error(56);
		if(ctype == IS_MPR)
			check();
		if(p->ncall >= MAX_FCALLS)
			error(44);
		fp = (forstp)mmalloc((ival)(sizeof(struct forst) +
						sizeof(struct JMPBUF)));
		fp->fnJMP = (struct JMPBUF *)(fp + 1);
		if((fp->prev = estack) != 0)
			fp->prev->next = fp;
		else
			bstack = fp;
		fp->next = 0;
		estack = fp;
		if(p->mline == IS_MFN){
			if(vty == RVAL)
				fp->fnval.f = ZERO;
			else if(vty == SVAL){
				fp->fnsval.str = 0;
				fp->fnsval.len = 0;
			}
			else
				fp->fnval.i = 0;
		}
		fp->fnvar = ep;
		fp->fnLOCAL = 0;	/* by default there is no hash table */
		fp->stolin = stocurlin;
		fp->pt = point;
		fp->elses = elsecount;
		fp->fortyp = FNTYP;	/* get the right type */
		fp->fnSBEG = str_used;
		fp->fnSEND = str_uend;
		str_used = str_uend = 0;
		stocurlin = p->mpnt;
		point = stocurlin->lin;
		elsecount = 0;
		p->ncall++;
		if(setjmp(fp->fnenv) != NORM_RESET)
			execute();
		/*
		 * get the right values for local vars
		 * setjmp does not save register vars
		 */
		for(fp = estack ; fp ; fp = fp->prev)
			if(fp->fortyp == FNTYP)
				break;
		if(!fp)	/* fire door to stop improper stacking */
			reset();
		ep = fp->fnvar;
		p = ep->_deffn;
		/*
		 * recover all environment
		 */
		stocurlin = fp->stolin;
		point = fp->pt;
		elsecount = fp->elses;
		if(p->mline == IS_MFN){
			if(vty == SVAL){
				retst->strval = fp->fnsval.str;
				retst->strlen = fp->fnsval.len;
				RESERVE_SPACE(retst, (ival)fp->fnsval.len);
			}
			else {
				res = fp->fnval;
				vartype = vty;
			}
		}

		recov_parms(p->vargs, p->narg, args, 0);
		if( (estack = fp->prev) == 0)
			bstack = 0;
		else
			fp->prev->next = 0;
		clr_stack(fp);	/* WARNING - also recovers any local vars */
		return;
	}
	if(++fnlock >= MAX_FCALLS)
		error(44);
	spoint=point;
	point=p->exp;
	if(vty == SVAL){
		/*
		 * this is horrible. We must recover this string
		 */
		st = stringeval();
		COPY_OVER_STR(retst, st);
		FREE_STR(st);
	}
	else
		eval();
	if(fnlock > 0)
		fnlock--;
	recov_parms(p->vargs, p->narg, args, 1);
	if(getch())
		error(SYNTAX);
	point= spoint;
	if(vty != SVAL && vartype != vty){
		if(vartype != RVAL)
			cvt(&res);
		else if(conv(&res))
			error(INTOVER);
		vartype = vty;
	}
}

static	void
recov_parms(arp, nargs, args, tofree)
struct	entry	**arp;
int	nargs;
union	ffn_vars *args;
int	tofree;
{
	register int    i;
	union	ffn_vars *cur_arg;
	struct	entry	**rp, *rep;
	STR	ost = 0;

	for(rp = arp, cur_arg = args, i=0; i < nargs; i++, cur_arg++,rp++){
		rep = *rp;
		if(rep->vtype == SVAL){
			stringassign(&rep->_dst, rep, cur_arg->ostr, 1);
			if(ost == 0)
				ost = cur_arg->ostr;
		}
		else
			rep->_dval = cur_arg->ovr;
	}
	if(ost && tofree)
		FREE_STR(ost);
}

void
drop_fns()
{
	register forstp	fp, nfp = 0;
	register struct entry	*ep;

	for(fp = bstack ; fp ; fp = nfp){
		nfp = fp->next;
		if(fp->fortyp == FNTYP){
			ep = fp->fnvar;
			ep->_deffn->ncall--;
			if(ep->vtype == SVAL && ep->_deffn->mline == IS_MFN){
				if(fp->fnsval.str != 0){
					mfree( (MEMP)fp->fnsval.str);
					fp->fnsval.str = 0;
				}
			}
			if(fp->next)
				fp->next->prev = fp->prev;
			else
				estack = fp->prev;
			if(fp->prev)
				fp->prev->next = fp->next;
			else
				bstack = fp->next;
			if(fp->fnLOCAL)
				recover_vars(fp, 0);
			if(str_used)
				FREE_STR(str_used);
			str_used = fp->fnSBEG;
			str_uend = fp->fnSEND;
			mfree( (MEMP)fp);
		}
	}
}

int
fnend()
{
	register forstp	fp;

	check();
	for(fp = estack ; fp ; fp = fp->prev)
		if(fp->fortyp == FNTYP)
			break;
	if(!fp)
		error(51);
	longjmp(fp->fnenv, NORM_RESET);
	normret;
}

int
fncmd()
{
	register struct	entry	*ep;
	register forstp	fp;
	STR	st;

	if(!ispletter(point))
		error(SYNTAX);
	ep = getnm(ISFUNC, 0);
	if(!ep)
		error(UNDEFFN);
	if(ep->_deffn->mline == IS_FN)
		error(UNDEFFN);
	if(ep->_deffn->mline == IS_MPR){
/*
		check();
*/
		ffn(ep, (STR)0);
		normret;
	}
	if(getch() != '=')
		error(SYNTAX);
	for(fp = estack ; fp ; fp = fp->prev)
		if(fp->fortyp == FNTYP)
			break;
	if(!fp || fp->fnvar != ep)
		error(UNDEFFN);
	if(vartype == SVAL){
		st = stringeval();
		check();
		stringassign(&fp->fnsval, ep, st, 0);
	}
	else {
		eval();
		check();
		putin(&fp->fnval, (int)(ep->vtype & NVALMASK));
	}
	normret;
}

void
recover_vars(sptr, doit)
forstp	sptr;
int	doit;
{
	register loc_sav_t *ls;
	register struct	loc_sav_e *lse;
	loc_sav_t *nls;

	ls = sptr->fnLOCAL;
	sptr->fnLOCAL = 0;
	while(ls != 0){
		nls = ls->next;
		lse = ls->arg;
		if(!doit){
			for(; ls->narg ; ls->narg--, lse++)
				if(lse->lentry)
					free_entry(lse->lentry);
		}
		else {
			for(; ls->narg ; ls->narg--, lse++){
				drop_val(lse->hentry, 0);
				if(lse->lentry)
					add_entry(lse->lentry);
				free_entry(lse->hentry);
			}
		}
		mfree( (MEMP)ls);
		ls = nls;
	}
}

/* int() - return the greatest integer less than x */

void
intf()
{
#ifndef SOFTFP
	extern	double  floor();

	eval();
	if(vartype != RVAL)
		return;
	res.f = floor(res.f);
	if(!conv(&res))
		vartype= IVAL;
#else
	value   temp;
	static  double  ONE = 1.0;

	eval();
	if(vartype != RVAL)             /* conv and integ truncate not round */
		return;
#ifndef UNPORTABLE
	if(res.f >= ZERO){
#else
	if(res.i>=0){                   /* positive easy */
#endif
		if(!conv(&res))
			vartype= IVAL;
		else integ(&res);
		return;
	}
	temp = res;
	integ(&res);
	if(cmp(&res,&temp)){            /* not got an integer subtract one */
		res = *((value *)&ONE);
		fsub(&temp,&res);
		integ(&res);
	}
	if(!conv(&res))
		vartype= IVAL;
#endif                                  /* not floating point */
}

void
bfixf()
{
	extern	double  floor();

	eval();
	if(vartype != RVAL)
		return;

	if(res.f < ZERO)
		res.f = -floor(-res.f);
	else
		res.f = floor(res.f);
}

static	char	*
real_memory()
{
	itype	l;
	char	*p;
#ifdef	pdp11
	l = evalint();
	p = (char *)l;
#else
#ifdef	BIG_INTS
	l = evalint();
	p = (char *)l;
#else
#ifdef	MSDOS
	l = evalint();
	p = (char *)l;
#else
	register long   ll;	/* really only for a vax */

	evalreal();
	if(res.f > 0x7fff000 || res.f < 0)      /* check this */
		error(FUNCT);
	ll = res.f;
	p = (char *)ll;
#endif
#endif
#endif
	return(p);
}

#if	defined(SYS5_4) && __STDC__ == 0
static	sigjmp_buf	pksig_catch;
#else
static	jmp_buf	pksig_catch;
#endif

static	SIGFUNC
pksig_catchf(sig)
int	sig;
{
#if	defined(SYS5_4) && __STDC__ == 0
	siglongjmp(pksig_catch, sig);
#else
	longjmp(pksig_catch, sig);
#endif
}

static	int
pkpok(loc, val, mode)
char	*loc;
itype	val;
int	mode;
{
	int	rval = -1;
#ifdef	__STDC__
	SIGFUNC	(*old_bus)(int), (*old_seg)(int);
#else
	SIGFUNC	(*old_bus)(), (*old_seg)();
#endif

	old_bus = signal(SIGBUS, pksig_catchf);
	old_seg = signal(SIGSEGV, pksig_catchf);

#if	defined(SYS5_4) && __STDC__ == 0
	switch(sigsetjmp(pksig_catch, 0)){
#else
	switch(setjmp(pksig_catch)){
#endif
	case 0:
		if(mode)
			rval = (int)UC(*loc);
		else
			*loc = (char) val;
		break;
	case SIGBUS:
		break;
	case SIGSEGV:
		break;
	default:
		break;
	}
	VOID signal(SIGBUS, old_bus);
	VOID signal(SIGSEGV, old_seg);
	return(rval);
}

void
peekf()
{
	register char   *p;

	p = real_memory();
	res.i = (itype)pkpok(p, (itype)0, 1);
	vartype = IVAL;
}

int
poke()                		/* sp = approx position of stack */
{                                       /* can give bus errors */
	register char   *p;
	register itype	i;

	p = real_memory();
	if(getch() != ',')
		error(SYNTAX);
	i = evalint();
	check();
	if(i<0 || i > 255)
		error(FUNCT);
	VOID pkpok(p, i, 0);
	normret;
}


static void
setdrg(tofrom)
int	tofrom;
{
#ifndef	SOFTFP
	static	const	double	grad_to_rad = PI_VALUE/200;
	static	const	double	deg_to_rad = PI_VALUE/180;

	if(drg_opt == OPT_RAD)
		return;
	if(tofrom){
		/* for sin and cos. and tan */
		if(drg_opt == OPT_GRAD)
			res.f *= grad_to_rad;
		else
			res.f *= deg_to_rad;
	}
	else {	/* for atan */
		if(drg_opt == OPT_GRAD)
			res.f /= grad_to_rad;
		else
			res.f /= deg_to_rad;
	}
#endif
}

void
bsinf()
{
	evalreal();
	setdrg(1);
#ifndef SOFTFP
	res.f = sin(res.f);
#else
	sin(&res);
#endif
}

void
bcosf()
{
	evalreal();
	setdrg(1);
#ifndef SOFTFP
	res.f = cos(res.f);
#else
	cos(&res);
#endif
}

void
btanf()
{
	double	x;

	evalreal();
	setdrg(1);
#ifndef	SOFTFP
	x = cos(res.f);
	if(x == ZERO){
		c_error(25);
		res.f = BIG;
	}
	else
		res.f = sin(res.f) / x;
#else
	tan(&res);
#endif
}

void
batanf()
{
	evalreal();
#ifndef SOFTFP
	res.f = atan(res.f);
#else
	atan(&res);
#endif
	setdrg(0);
}

void
basinf()
{
	evalreal();
#ifndef	SOFTFP
	res.f = asin(res.f);
#endif
	setdrg(0);
}

void
bacosf()
{
	evalreal();
#ifndef	SOFTFP
	res.f = acos(res.f);
#endif
	setdrg(0);
}
/*
 * hyperbolic functions
 */
#ifndef	SOFTFP
static	int
hyp_sign(xp)
double	*xp;
{
	register int	res;

	if(*xp < ZERO){
		res = -1;
		*xp = - *xp;
	}
	else
		res = 1;
	return(res);
}
#endif

static	void
hyper_sc(sin_cos_tan)
int	sin_cos_tan;
{
	double	x, y;
	int	sign;

	evalreal();

#ifndef	SOFTFP
	sign = hyp_sign(&res.f);
	if(res.f >= 20.0){
		switch(sin_cos_tan){
		case 2:	/*TANH*/
			res.f = (sign > 0) ? ONE : -ONE;
			break;
		case 1: /*COSH*/
		case 0: /*SINH*/
			/* there is a discontinuity here from a
			 * number <= logmaxval to > logmaxval.
			 * can solve this problem if we do
			 * exp(res.f - ln2) between logmaxval and
			 * logmaxval + ln2
			 */
			if(res.f > logmaxval){
				if(res.f > logmaxval + logof2){
					c_error(34);
					res.f = BIG;
				}
				else
					res.f = exp(res.f - logof2);
			}
			else
				res.f = exp(res.f) / TWO;
			if(sin_cos_tan == 0 && sign < 0)
				res.f = -res.f;
			break;
		}
		return;
	}
	x = exp(res.f);
	y = ONE / x;
	switch(sin_cos_tan){
	case 2:	/*TANH*/
		res.f = (x - y) / (x + y);
		break;
	case 1: /*COSH*/
		res.f = (x + y) / TWO;
		break;
	case 0: /*SINH*/
		res.f = (x - y) / TWO;
		break;
	}
	if(sin_cos_tan != 1 && sign < 0)
		res.f = -res.f;
#endif
}

static	void
ahyper_sc(sin_cos_tan)
int	sin_cos_tan;
{
	double	x;
	int	neg;

	evalreal();

#ifndef	SOFTFP
	x = res.f;
	neg = hyp_sign(&x);
	switch(sin_cos_tan){
	case 2:	/* TANH */
		if(x >= ONE)
			goto setnan;
		res.f = log(ONE + (res.f + res.f) / ( ONE - res.f)) / TWO;
		break;
	case 1:	/* COSH */
		if(res.f < ONE)
			goto setnan;
		if(x < INSIG)
			res.f = log(x + sqrt(x * x - ONE));
		else 
			res.f = log(x) + logof2;
		break;
	case 0: /* SINH */
		if(x < INSIG)
			res.f = log(x + sqrt(x * x + ONE));
		else
			res.f = log(x) + logof2;
		if(neg < 0)
			res.f = -res.f;
		break;
	}
	return;
setnan:
	c_error(34);
	res.f = (neg > 0) ? BIG : BIGminus;
#endif
}

void
bsinh()
{
	hyper_sc(0);
}

void
bcosh()
{
	hyper_sc(1);
}

void
btanh()
{
	hyper_sc(2);
}

void
basinh()
{
	ahyper_sc(0);
}

void
bacosh()
{
	ahyper_sc(1);
}

void
batanh()
{
	ahyper_sc(2);
}

/*
 * the option command.
 */

#ifdef	OWN_ALLOC
extern	int	max_mem_size;
#endif

int
bopts()
{
	register int	c;
	itype	memsiz;

	if( (c = getch()) == OPT_BASE){
		VOID base();
		normret;
	}
	if(c != OFUNC)
		error(SYNTAX);
		
	switch(c = UC(*point++)){
#ifndef	SOFTFP
	case OPT_GRAD:
	case OPT_DEG:
#endif
	case OPT_RAD:
		drg_opt = c;
		break;

	case OPT_MEM:
		memsiz = evalint();
		if(memsiz <= 0)
			memsiz = MAX_MEM_DEFAULT;
		else if(memsiz > MAX_MEM_MAX)
			memsiz = MAX_MEM_MAX;
#ifdef	OWN_ALLOC
		max_mem_size = memsiz;
#endif
		break;
	default:
		error(SYNTAX);
		break;
	}
	normret;
}

/*
 * the "system" function, returns the status of the command it executes
 */

void
ssystem()
{
	register STR	st;

	st = stringeval();
	NULL_TERMINATE(st);

	flushall();

	res.i = (itype)do_system(st->strval);
	vartype = IVAL;
	FREE_STR(st);
}

/*
 * perform a system call. parameters are taken as is
 */
#define	MAX_SYS_ARGS	6

extern	int	errno;
#ifdef	__STDC__
extern	int	syscall(int, ...);
#else
extern	int	syscall();
#endif

static	int	sys_error;

void
bsyscall()
{
	register int	nargs;
	int	args[MAX_SYS_ARGS];
	itype	scall;
	itype	rval;

	sys_error = 0;
	scall = evalint();

	if(scall < 1 || scall > 10000)
		error(FUNCT);	

	for(nargs = 0 ; nargs < MAX_SYS_ARGS ; nargs++)
		args[nargs] = 0;

	for(nargs = 0; getch() == ',' ; nargs++){
		if(nargs >= MAX_SYS_ARGS)
			error(FUNCT);
		args[nargs] = (int)evalint();
	}
	point--;
	errno = 0;
	rval = syscall(scall, args[0],args[1],args[2],args[3],args[4],args[5]);
	sys_error = errno;
	vartype = IVAL;
	res.i = rval;
}

void
bsyserr()
{
	res.i = (ival)sys_error;
	vartype = IVAL;
}

static	void
bminmax(is_min)
int	is_min;
{
	value	curval;
	char	vtyp;
	int	rc;

	eval();
	curval = res;
	vtyp = vartype;
	if(getch() != ',')
		error(SYNTAX);
	do {
		eval();
		if(vtyp != vartype){
			if(vartype != RVAL)
				cvt(&res);
			else
				cvt(&curval);
			vartype = RVAL;
			vtyp = RVAL;
		}
		rc = cmp(&res, &curval);
		if( (rc < 0 && is_min) || (rc > 0 && !is_min)){
			curval = res;
			vtyp = vartype;
		}
	}while(getch() == ',');
	res = curval;
	vartype = vtyp;
	point--;
}

void
bmax()
{
	bminmax(0);
}

void
bmin()
{
	bminmax(1);
}

void
bcreal()
{
	evalreal();
}

void
bcint()
{
	ival	ret;

	ret = evalint();
	res.i = ret;
	vartype = IVAL;
}

/*
 * matrix commands.
 */
static	void	chk_dims(struct entry *, struct entry *);
static	int	mat_len(struct entry *);
static	void	matmuli(struct entry *, struct entry *, struct entry *,
					ival, ival, ival);
static	void	matmulr(struct entry *, struct entry *, struct entry *,
					ival, ival, ival);

int
bmat()
{
	struct	entry	*lhep;
	struct	entry	*arg1;
	struct	entry	*arg2;
	struct	entry	*newent;
	int	c;
	int	rcnt;
	valp	vp, xp, zp;
	ival	*vpp, *xpp, *zpp;
	char	vty;
	ival	da1, da2, db2, db1;

	c = getch();
	switch(c){
	case INPUT:
		return(matinput());
	case READ:
		do {
			lhep = getmat(0);
			matread((MEMP)lhep->_darr, (int)vartype, mat_len(lhep));
		} while(getch() == ',');
		point--;
		normret;
	case PRINT:
		return(matprint());
	default:
		point--;
		break;
	}
	lhep = getmat(1);
	newent = newentry;
	vty = vartype;
	if(getch() != '=')
		error(4);

	c = getch();
	switch(c){
	default:
		point--;
		break;
	}
	arg1 = getmat(0);

	c = getch();
	if(istermin(c)){
		point--;
		if(lhep == 0){
			lhep = newent;
			vartype = vty;
			def_darr(lhep, arg1->_dims[0],
				(arg1->dimens > 1) ? arg1->_dims[1] : 0);
			newentry = 0;
		}
		else
			chk_dims(lhep, arg1);
		VOID strmov(lhep->_darr, arg1->_darr,
				(ival)(mat_len(lhep) * TYP_SIZ(lhep->vtype)));
		normret;
	}
	switch(c){
	case '.':
		arg2 = getmat(0);
		if(arg1->dimens > 1){
			da1 = arg1->_dims[1];
			da2 = arg1->_dims[0];
		}
		else {
			da1 = arg1->_dims[0];
			da2 = 1;
		}
		if(arg2->dimens > 1){
			db1 = arg2->_dims[1];
			db2 = arg2->_dims[0];
		}
		else {
			db1 = arg2->_dims[0];
			db2 = 1;
		}
		if(da2 != db1)
			error(58);
		if(lhep == 0){
			lhep = newent;
			vartype = vty;
			def_darr(lhep, da1, (db2 > 1) ? db2 : 0);
			newentry = 0;
		}
		else {
			/*
			 * result cannot be one of the two parameters
			 */
			if(lhep == arg1 || lhep == arg2)
				error(58);
			if(lhep->vtype != arg1->vtype || lhep->_dims[0] != da1)
				error(58);
			if(db2 > 1){
				if(lhep->dimens <= 1 || lhep->_dims[1] != db2)
					error(58);
			}
			else {
				if(lhep->dimens > 1 && lhep->_dims[1] != 1)
					error(58);
			}
		}
		/*
		 * now do matrix multiplication
		 */
		if(vartype == RVAL)
			matmulr(lhep, arg1, arg2, da1, da2, db2);
		else
			matmuli(lhep, arg1, arg2, da1, da2, db2);
		break;
	case '+':
	case '-':
		arg2 = getmat(0);
		chk_dims(arg1, arg2);
		if(lhep == 0){
			lhep = newent;
			vartype = vty;
			def_darr(lhep, arg1->_dims[0],
				(arg1->dimens > 1) ? arg1->_dims[1] : 0);
			newentry = 0;
		}
		else
			chk_dims(lhep, arg1);
		rcnt = mat_len(lhep);
		xp = (valp)(MEMP)arg1->_darr;
		zp = (valp)(MEMP)arg2->_darr;
		vp = (valp)(MEMP)lhep->_darr;

		if(vartype == RVAL){
			if(c == '+'){
				for(; rcnt ; rcnt--){
					vp->f = xp->f + zp->f;
					vp++;
					xp++;
					zp++;
				}
			}
			else for(; rcnt ; rcnt--){
				vp->f = xp->f - zp->f;
				vp++;
				xp++;
				zp++;
			}
		}
		else {
			xpp = &xp->i;
			vpp = &vp->i;
			zpp = &zp->i;
			if(c == '+'){
				for(; rcnt ; rcnt--){
					long	l = *xpp + *zpp;
					if(IS_OVER(*zpp, *xpp, l))
						error(INTOVER);
					*vpp = l;
					vpp++;
					xpp++;
					zpp++;
				}
			}
			else {
				for(; rcnt ; rcnt--){
					long	l = *xpp - *zpp;
					if(IS_OVER(*zpp, *xpp, l))
						error(INTOVER);
					*vpp = l;
					vpp++;
					xpp++;
					zpp++;
				}
			}
		}
		break;
	case '*':
		if(lhep == 0){
			lhep = newent;
			vartype = vty;
			def_darr(lhep, arg1->_dims[0],
				(arg1->dimens > 1) ? arg1->_dims[1] : 0);
			newentry = 0;
		}
		else
			chk_dims(lhep, arg1);
		eval();
		if(vartype != lhep->vtype){
			if(vartype != RVAL)
				cvt(&res);
			else if(conv(&res))
				error(INTOVER);
			vartype = lhep->vtype;
		}

		rcnt = mat_len(lhep);
		xp = (valp)(MEMP)arg1->_darr;
		vp = (valp)(MEMP)lhep->_darr;

		if(vartype == RVAL){
			for(; rcnt ; rcnt--){
				vp->f = xp->f * res.f;
				vp++;
				xp++;
			}
		}
		else {
			xpp = &xp->i;
			vpp = &vp->i;
			for(; rcnt ; rcnt--){
#ifdef	BIG_INTS
				*vpp = mmult_ply(*xpp, res.i, INTOVER);
#else
				long	l = *xpp * res.i;
				if(IS_OVER(res.i, *xxp, l))
					error(INTOVER);
				*vpp = l;
#endif
				vpp++;
				xpp++;
			}
		}
		break;
	default:
		error(SYNTAX);
	}
	normret;
}

#if 0
#define	MAT_LEN(lhep, cnt)	\
	do { \
		(cnt) = (lhep)->_dims[0]; \
		if((lhep)->dimens > 1) \
			(cnt) *= (lhep)->_dims[1]; \
	} while(0)
#define	mat_len(lhep) \
	(((lhep)->dimens > 1 ) ? ((lhep)->_dims[0] * (lhep)->_dims[1]) : \
							(lhep)->_dims[0])
#else
static	int
mat_len(lhep)
struct	entry	*lhep;
{
	int	rcnt;

	rcnt = lhep->_dims[0];
	if(lhep->dimens > 1)
		rcnt *= lhep->_dims[1];
	return(rcnt);
}
#endif

static void
chk_dims(lhep, arg1)
struct	entry	*lhep, *arg1;
{
	if(lhep->vtype == arg1->vtype && lhep->dimens == arg1->dimens &&
	   lhep->_dims[0] == arg1->_dims[0] && (lhep->dimens == 1 ||
					lhep->_dims[1] == arg1->_dims[1]))
		return;
	error(58);
}

/*
 * matrix multiplication (finally!!)
 */
static void
matmulr(lhep, arg1, arg2, da1, da2, db2)
struct	entry *lhep, *arg1, *arg2;
ival	da1, da2, db2;
{
	ival	i,j,k;
	valp	vp, vpp, zp, zpp, xp, xpp;
	double	x;

	vpp = (valp)(MEMP)arg1->_darr;
	zpp = (valp)(MEMP)lhep->_darr;
	for(i =  0 ; i < da1 ; i++){
		/*
		 * VP = arg1, ZP = lhep
		 * vp = arg1->[i];
		 * zp = lhep->[i];
		 * xp = arg2->[?,j]
		 */
		zp = zpp;
		xpp = (valp)(MEMP)arg2->_darr;
		for(j = 0 ; j < db2 ; j++){
			x = ZERO;
			vp = vpp;
			xp = xpp;
			for(k = 0 ; k < da2 ; k++){
				x = x + vp->f * xp->f;
				xp += db2;
				vp++;
			}
			zp->f = x;
			zp++;
			xpp++;
		}
		vpp += da2;
		zpp += db2;
	}
}

static void
matmuli(lhep, arg1, arg2, da1, da2, db2)
struct	entry *lhep, *arg1, *arg2;
ival	da1, da2, db2;
{
	ival	i,j,k;
	ival	*vp, *vpp, *zp, *zpp, *xp, *xpp;
	long	x, l, ll;

	vpp = (ival *)(MEMP)arg1->_darr;
	zpp = (ival *)(MEMP)lhep->_darr;
	for(i =  0 ; i < da1 ; i++){
		/*
		 * VP = arg1, ZP = lhep
		 * vp = arg1->[i];
		 * zp = lhep->[i];
		 * xp = arg2->[?,j]
		 */
		zp = zpp;
		xpp = (ival *)(MEMP)arg2->_darr;
		for(j = 0 ; j < db2 ; j++){
			x = 0;
			vp = vpp;
			xp = xpp;
			for(k = 0 ; k < da2 ; k++){
#ifdef	BIG_INTS
				l = mmult_ply(*xp, *vp, INTOVER);
#else
				l = *vp * *xp;
				if(IS_OVER(*vp, *xp, l))
					error(INTOVER);
#endif
				
				ll = x + l;
				if(IS_OVER(x, l, ll))
					error(INTOVER);
				x = ll;
				xp += db2;
				vp++;
			}
			*zp = x;
			zp++;
			xpp++;
		}
		vpp += da2;
		zpp += db2;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1