/*	Copyright (C) 1990 Free Software Foundation, Inc.

This file is part of Oleo, the GNU Spreadsheet.

Oleo is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

Oleo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Oleo; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <math.h>
#include <ctype.h>
#include <stdio.h>

#ifdef __TURBOC__
#define SMALLEVAL
#else
#include <signal.h>
#ifdef __GNU_LIBRARY__
typedef void SIGTY;
#define SIGRET	return;
#else
typedef int SIGTY;
#define SIGRET return 0;
#ifndef NeXT
#ifndef AMIGA
extern long fseek();
#endif
/* extern fread();
extern fclose(); */
#endif
#endif
#endif

#include "funcdef.h"

#ifdef NO_RINT
#define rint(x) (((x)<0) ? ceil((x)-.5) : floor((x)+.5))
#else
extern double rint EXT1(double);
#endif

#define obstack_chunk_alloc ck_malloc
#define obstack_chunk_free free
#include "obstack.h"

#include "sysdef.h"

#include "global.h"
#include "cell.h"
#include "eval.h"
#include "errors.h"

struct value {
	int	type;
	union vals x;
};

#define Float	x.c_d
#define String	x.c_s
#define Int	x.c_l
#define Value	x.c_i
#define Rng	x.c_r

#define PI (3.14159265358979326848)

double to_int EXT1(double);
static int deal_area EXT3(unsigned char, unsigned char, struct value *);

static void add_int EXT1(long);
static void add_flt EXT1(double);
#ifndef __TURBOC__
SIGTY math_sig EXT1(int);
#endif

static struct value   *stack;
static int             stackmax;
static int             curstack;

unsigned short current_cycle;

CELLREF cur_row;
CELLREF cur_col;

static double exp10_arr[] = {
	1E0,	1E1,	1E2,	1E3,	1E4,
	1E5,	1E6,	1E7,	1E8,	1E9,
	1E10,	1E11,	1E12,	1E13,	1E14,
	1E15,	1E16,	1E17,	1E18,	1E19,
	1E20,	1E21,	1E22,	1E23,	1E24,
	1E25,	1E26,	1E27,	1E28,	1E29
};

/* Various math conversions with error checking */
#define I_ADD(i1,i2) {	itmp=(i1)+(i2);					\
			if((i1>0)==(i2>0) && (itmp>0)!=(i1>0)) {	\
				p->Float=(double)(i1)+(double)(i2);	\
				p->type=TYP_FLT;			\
			} else						\
				p->Int=itmp;	}

#define I_SUB(i1,i2) {	itmp=(i1)-(i2);					\
			if(((i1)<0)==((i2)>0) && ((itmp)>0)!=((i2)<0)) {\
				p->Float=(double)(i1)-(double)(i2);	\
				p->type=TYP_FLT;			\
			} else						\
				p->Int=itmp;	}

#define I_DIV(i1,i2) {	ftmp=(double)(i1)/(double)(i2);			\
			/* ... */;					\
			p->Float=ftmp;					\
			p->type=TYP_FLT;	}

#define I_MOD(i1,i2) {itmp=(i1)%(i2);/* ... */;p->Int=itmp;}

int fls EXT1(long);
#define I_MUL(i1,i2) {	if(fls(i1)+fls(i2)>32) {			\
				p->Float=(double)(i1)*(double)(i2);	\
				p->type=TYP_FLT;			\
			} else						\
				p->Int=(i1)*(i2);	}

#define F_ADD(f1,f2) {	ftmp=(f1)+(f2);/* ... */;p->Float=ftmp;	}

#define F_SUB(f1,f2) {	ftmp=(f1)-(f2);/* ... */;p->Float=ftmp;}

#define F_DIV(f1,f2) {	ftmp=(f1)/(f2);/* ... */;p->Float=ftmp;}

#define F_MOD(f1,f2) {	itmp=(long)(f1)%(long)(f2);/* ... */;p->Int=itmp;p->type=TYP_INT;}

#define F_MUL(f1,f2) {	ftmp=(f1)*(f2);/* ... */;p->Float=ftmp;}

double ftmp;
long itmp;
int overflow;

/* You may ask:  Why not jsut put the value in stack[0] and goto break_out
   The answer is that ERROR is a valid input type for several operators, so
   we want to work if we're feeding an error into one of these operators. . .
 */
#define ERROR(cause)		\
	do {			\
		p->type=TYP_ERR;\
		p->Value=cause; \
		goto next_byte; \
	} while(0)

#ifdef SMALLEVAL
int __to_flt EXT1(struct value *);
int __to_int EXT1(struct value *);
int __to_num EXT1(struct value *);
int __to_str EXT1(struct value *);
int __to_bol EXT1(struct value *);
int __to_rng EXT1(struct value *);

#define TO_FLT(val)			\
	if((tmp=__to_flt(val))!=0)	\
		ERROR(tmp);

#define TO_INT(val)			\
	if((tmp=__to_int(val))!=0)	\
		ERROR(tmp);

#define TO_NUM(val)			\
	if((tmp=__to_num(val))!=0)	\
		ERROR(tmp);

#define TO_STR(val)			\
	if((tmp=__to_str(val))!=0)	\
		ERROR(tmp);

#define TO_BOL(val)			\
	if((tmp=__to_bol(val))!=0)	\
		ERROR(tmp);

#define TO_RNG(val)			\
	if((tmp=__to_rng(val))!=0)	\
		ERROR(tmp);

#else
#define TO_FLT(val)	\
	if((val)->type==TYP_FLT) \
		; \
	else if((val)->type==TYP_INT) { \
		(val)->type=TYP_FLT; \
		(val)->Float=(double)(val)->Int; \
	} else if((val)->type==TYP_STR) { \
		(val)->type=TYP_FLT; \
		strptr=(val)->String; \
		(val)->Float=astof(&strptr); \
		if(*strptr) \
			ERROR(NON_NUMBER); \
	} else if((val)->type==TYP_ERR) {\
		ERROR((val)->Value); \
	} else if((val)->type==0) { \
		(val)->type=TYP_FLT; \
		(val)->Float=0.0; \
	} else \
		ERROR(NON_NUMBER);

#define TO_INT(val)	\
	if((val)->type==TYP_INT) \
		; \
	else if((val)->type==TYP_FLT) { \
		(val)->type=TYP_INT; \
		(val)->Int=(long)(val)->Float; \
	} else if((val)->type==TYP_STR) { \
		(val)->type=TYP_INT; \
		strptr=(val)->String; \
		(val)->Int=astol(&strptr); \
		if(*strptr) \
			ERROR(NON_NUMBER); \
	} else if((val)->type==TYP_ERR) \
		ERROR((val)->Value); \
	else if((val)->type==0) { \
		(val)->type=TYP_INT; \
		(val)->Int=0; \
	} else \
		ERROR(NON_NUMBER);

#define TO_NUM(val)	\
	if((val)->type==TYP_INT || (val)->type==TYP_FLT) \
		; \
	else if((val)->type==TYP_STR) { \
		(val)->type=TYP_FLT; \
		strptr=(val)->String; \
		(val)->Float=astof(&strptr); \
		if(*strptr) \
			ERROR(NON_NUMBER); \
	} else if((val)->type==TYP_ERR) \
		ERROR((val)->Value); \
	else if((val)->type==0) { \
		(val)->type=TYP_INT; \
		(val)->Int=0; \
	} else \
		ERROR(NON_NUMBER);

#define TO_STR(val)	\
	if((val)->type==TYP_STR) \
		; \
	else if((val)->type==TYP_INT) { \
		char *s; \
		(val)->type=TYP_STR; \
		s=obstack_alloc(&tmp_mem,30); \
		sprintf(s,"%ld",(val)->Int); \
		(val)->String=s; \
	} else if((val)->type==TYP_FLT) {		\
		char *s;				\
		s=flt_to_str((val)->Float);		\
		(void)obstack_grow(&tmp_mem,s,strlen(s)+1); \
		(val)->String=obstack_finish(&tmp_mem);	\
		(val)->type=TYP_STR;			\
	} else if((val)->type==TYP_ERR)			\
		ERROR((val)->Value); \
	else if((val)->type==0) { \
		(val)->type=TYP_STR; \
		(val)->String=obstack_alloc(&tmp_mem,1); \
		(val)->String[0]='\0'; \
	} else \
		ERROR(NON_STRING);

#define TO_BOL(val) \
	if((val)->type==TYP_BOL) \
		; \
	else if((val)->type==TYP_ERR) \
		ERROR((val)->Value); \
	else \
		ERROR(NON_BOOL);

#define TO_RNG(val) \
	if((val)->type==TYP_RNG) \
		; \
	else if((val)->type==TYP_ERR) \
		ERROR((val)->Value); \
	else \
		ERROR(NON_RANGE);

#endif

#define TO_ANY(val) \
	if((val)->type==TYP_RNG) \
		ERROR(BAD_INPUT); \

#define PUSH_ANY(cp)				\
	if(!cp || !GET_TYP(cp)) {		\
		p->type=0;			\
		p->Int=0;			\
	} else {				\
		p->type=GET_TYP(cp);		\
		p->x=cp->c_z;			\
	}

void
init_eval FUN0()
{
	stack=(struct value *)ck_malloc(20*sizeof(struct value));
	stackmax=20;
	curstack=0;
	current_cycle++;
#ifndef __TURBOC__
	(void)signal(SIGFPE,math_sig);
#endif
}

/* This huge function takes a byte-compiled expression and executes it. */
struct value *
eval_expression FUN1(unsigned char  *,expr)
{
	unsigned char   byte;
	unsigned numarg;
	unsigned jumpto;
	struct function *f;
	struct value	*p;
	char	*strptr;
	int	tmp;

	CELLREF	lrow,	hrow,	crow;
	CELLREF	lcol,	hcol,	ccol;

	struct cell *cell_ptr;

	if(!expr)
		return 0;
#ifdef TEST
	jumpto=0;
	numarg=0;
	p=0;
#endif
	curstack=0;
	while((byte= *expr++)!=ENDCOMP) {
		if(byte<USR1)
			f= &the_funs[byte];
		else if(byte<SKIP) {
#ifdef TEST
			extern int n_usr_funs;

			if(byte-USR1>=n_usr_funs)
				panic("Only have %d usr-function slots, but found byte for slot %d",n_usr_funs,1+byte-USR1);
#endif
			tmp= *expr++;
			f= &usr_funs[byte-USR1][tmp];
		} else
			f= &skip_funs[byte-SKIP];

		if(f->fn_argn&X_J)
			jumpto= *expr++;
		else if(f->fn_argn&X_JL) {
			jumpto= expr[0]+((unsigned)(expr[1])<<8);
			expr+=2;
		}

		switch(f->fn_argn & X_ARGS) {
			/* A0 is special, since it makes the stack grow, while
			   all the others make the stack the same size or
			   less. . . */
		case X_A0:
			numarg= 0;
			if(curstack==stackmax) {
				stackmax*=2;

				stack=(struct value *)ck_realloc(stack,sizeof(struct value)*stackmax);
			}
			p= &stack[curstack];
			curstack++;
			break;

		case X_A1:
			numarg=1;
			break;

		case X_A2:
			numarg=2;
			break;
		case X_A3:
			numarg=3;
			break;
		case X_A4:
			numarg=4;
			break;
		case X_AN:
			numarg= *expr++;
			break;
#ifdef TEST
		default:
			panic("Unknown arg_num %d",f->fn_argn);
			numarg=0;
			p=0;
#endif
		}
		if(numarg>0) {
			int xt;

#ifdef TEST
			if(curstack<numarg)
				panic("Only %u values on stack, not %u", curstack, numarg);
#endif
			p= &stack[curstack-numarg];
			curstack-=(numarg-1);
			for(xt=0;xt<numarg;xt++) {
				switch(f->fn_argt[xt<=3 ? xt : 3]) {
					/* A is for anything */
					/* Any non-range value */
				case 'A':
					TO_ANY(p+xt);
					break;
					/* B is for boolean */
				case 'B':
					TO_BOL(p+xt);
					break;
					/* D is for Don't check */
				case 'D':
					break;
					/* E is for Everything */
				case 'E':
					break;
					/* F is for Float */
				case 'F':
					TO_FLT(p+xt);
					break;
					/* I is for Int */
				case 'I':
					TO_INT(p+xt);
					break;
					/* N is for Number (int or float) */
				case 'N':
					TO_NUM(p+xt);
					break;
					/* R is for Range */
				case 'R':
					TO_RNG(p+xt);
					break;
					/* S is for String */
				case 'S':
					TO_STR(p+xt);
					break;
#ifdef TEST
				default:
					error_msg("YIKE!  Unknown argtype for Fun %u  arg #%u",byte,xt);
					break;
#endif
				}
			}
		}

		switch (byte) {
		case IF_L:
		case F_IF_L:
		case IF:
		case F_IF:
			if(p->type!=TYP_BOL) {
				if(p->type!=TYP_ERR) {
					p->type=TYP_ERR;
					p->Value=NON_BOOL;
				}
				expr+=jumpto;
				if(expr[-2]!=SKIP)
					jumpto=expr[-1]+(((unsigned)expr[-2])<<8);
				else
					jumpto=expr[-1];
				expr+=jumpto;	/* Skip both branches of the if */

			} else if(p->Value==0) {
				expr+=jumpto;
				--curstack;
			} else
				--curstack;
			break;
		
		case SKIP_L:
		case SKIP:
			--curstack;
			expr+=jumpto;
			break;

		case AND_L:
		case AND:
			if(p->type==TYP_ERR)
				expr+=jumpto;
			else if(p->type!=TYP_BOL) {
				p->type=TYP_ERR;
				p->Value=NON_BOOL;
				expr+=jumpto;
			} else if(p->Value==0)
				expr+=jumpto;
			else
				--curstack;
			break;

		case OR_L:
		case OR:
			if(p->type==TYP_ERR)
				expr+=jumpto;
			else if(p->type!=TYP_BOL) {
				p->type=TYP_ERR;
				p->Value=NON_BOOL;
				expr+=jumpto;
			} else if(p->Value)
				expr+=jumpto;
			else
				--curstack;
			break;

		case CONST_FLT:
			p->type=TYP_FLT;
			bcopy((VOIDSTAR)expr,(VOIDSTAR)(&(p->Float)),sizeof(double));
			expr+=sizeof(double);
			break;

		case CONST_INT:
			p->type=TYP_INT;
			bcopy((VOIDSTAR)expr,(VOIDSTAR)(&(p->Int)),sizeof(long));
			expr+=sizeof(long);
			break;

		case CONST_STR:
		case CONST_STR_L:
			p->type=TYP_STR;
			p->String=(char *)expr+jumpto;
			break;

		case CONST_ERR:
			p->type=TYP_ERR;
			p->Value= *expr++;
			/* expr+=sizeof(char *); */
			break;

		case CONST_INF:
		case CONST_NINF:
		case CONST_NAN:
			p->type=TYP_FLT;
			p->Float= (byte==CONST_INF) ? __plinf : ((byte==CONST_NINF) ? __neinf : __nan);
			break;

		case VAR:
		{
			struct var *varp;

			bcopy((VOIDSTAR)expr,(VOIDSTAR)(&varp),sizeof(struct var *));
			expr+=sizeof(struct var *);
			switch(varp->var_flags) {
			case VAR_UNDEF:
				p->type=TYP_ERR;
				p->Value=BAD_NAME;
				break;

			case VAR_CELL:
				cell_ptr=find_cell(varp->v_rng.lr,varp->v_rng.lc);
				PUSH_ANY(cell_ptr);
				break;

			case VAR_RANGE:
				p->type=TYP_RNG;
				p->Rng=varp->v_rng;
				break;
#ifdef TEST
			default:
				panic("Unknown var type %d",varp->var_flags);
#endif
			}
		}
			break;

			/* Cell refs */
		case R_CELL:
		case R_CELL|COLREL:
		case R_CELL|ROWREL:
		case R_CELL|ROWREL|COLREL:
		{
			CELLREF torow,tocol;

			torow = GET_ROW(expr);
			tocol = GET_COL(expr);
			expr+=EXP_ADD;
#ifdef DONTDEF
			if(byte&ROWREL)
				torow=(short)torow+cur_row;
			if(byte&COLREL)
				tocol=(short)tocol+cur_col;
#endif
#ifdef DONTDEF
			if(torow<MIN_ROW || torow>MAX_ROW || tocol<MIN_COL || tocol>MAX_COL) {
				p->type=TYP_ERR;
				p->Value=OUT_OF_RANGE;
			} else {
#endif
				cell_ptr=find_cell((CELLREF)torow,(CELLREF)tocol);
				PUSH_ANY(cell_ptr);
#ifdef DONTDEF
			}
#endif
		}
			break;

		case RANGE:
		case RANGE|LRREL:
		case RANGE|LRREL|LCREL:
		case RANGE|LRREL|LCREL|HCREL:
		case RANGE|LRREL|HCREL:
		case RANGE|LRREL|HRREL:
		case RANGE|LRREL|HRREL|LCREL:
		case RANGE|LRREL|HRREL|LCREL|HCREL:
		case RANGE|LRREL|HRREL|HCREL:
		case RANGE|HRREL:
		case RANGE|HRREL|LCREL:
		case RANGE|HRREL|LCREL|HCREL:
		case RANGE|HRREL|HCREL:
		case RANGE|LCREL:
		case RANGE|LCREL|HCREL:
		case RANGE|HCREL:
			p->type=TYP_RNG;
			GET_RNG(expr,&(p->Rng));
			expr+=EXP_ADD_RNG;
			break;

		case F_TRUE:
		case F_FALSE:
			p->type=TYP_BOL;
			p->Value=(byte==F_TRUE);
			break;

		case F_PI:
			p->type=TYP_FLT;
			p->Float=PI;
			break;

		case F_ROW:
		case F_COL:
			p->type=TYP_INT;
			p->Int= ((byte==F_ROW) ? cur_row : cur_col);
			break;

		case F_NOW:
			p->type=TYP_INT;
			p->Int=time((VOIDSTAR)0);
			break;

			/* Single operand instrs */
		case F_ABS:
		case F_ACOS:
		case F_ASIN:
		case F_ATAN:
		case F_CEIL:
		case F_COS:
		case F_DTR:
		case F_EXP:
		case F_FLOOR:
		case F_INT:
		case F_LOG:
		case F_LOG10:
		case F_RTD:
		case F_SIN:
		case F_SQRT:
		case F_TAN:
		{
			double (*funp1) EXT1(double);

			funp1=(double(*) EXT1(double))(f->fn_fun);
			p->Float=(*funp1)(p->Float);
			if(p->Float!=p->Float)
				ERROR(OUT_OF_RANGE);
		}
			break;

		case F_CTIME:
			p->type=TYP_STR;
			strptr=ctime(&(p->Int));
			p->String=obstack_alloc(&tmp_mem,25);
			strncpy(p->String,strptr,24);
			p->String[24]='\0';
			break;

		case NEGATE:
		case F_NEG:
			if(p->type==TYP_ERR)
				break;
			if(p->type==TYP_INT)
				p->Int= -(p->Int);
			else if(p->type==TYP_FLT)
				p->Float= -(p->Float);
			else
				ERROR(NON_NUMBER);
			break;

		case F_RND:
			p->Int=(random()%(p->Int))+1;
			break;

		case NOT:
		case F_NOT:
			p->Value= !(p->Value);
			break;

		case F_ISERR:
			p->Value= (p->type==TYP_ERR);
			p->type=TYP_BOL;
			break;

		case F_ISNUM:
			if(p->type==TYP_FLT || p->type==TYP_INT)
				p->Value=1;
			else if(p->type==TYP_STR) {
				strptr=p->String;
				(void)astof(&strptr);
				p->Value= (*strptr=='\0');
			} else
				p->Value=0;
			p->type=TYP_BOL;
			break;

		case F_ROWS:
		case F_COLS:
			p->type=TYP_INT;
			p->Int=1+(byte==F_ROWS ? (p->Rng.hr-p->Rng.lr) : (p->Rng.hc-p->Rng.lc));
			break;

			/* Two operand cmds */
		case F_ATAN2:
		case F_HYPOT:
		case POW:
		{
			double (*funp2) EXT2(double,double);

			funp2=(double(*) EXT2(double,double))(f->fn_fun);
			p->Float=(*funp2)(p->Float,(p+1)->Float);
			if(p->Float!=p->Float)
				ERROR(OUT_OF_RANGE);
		}
			break;

		case DIFF:
		case DIV:
		case MOD:
		case PROD:
		case SUM:

			if(p->type!=(p+1)->type) {
				if(p->type==TYP_INT) {
					p->type=TYP_FLT;
					p->Float=(double)p->Int;
				}
				if((p+1)->type==TYP_INT) {
					(p+1)->type=TYP_FLT;
					(p+1)->Float=(double)((p+1)->Int);
				}
			}
			if(p->type==TYP_INT) {
				switch(byte) {
				case DIFF:
					I_SUB(p->Int,(p+1)->Int);
					break;
				case DIV:
					if((p+1)->Int==0)
						ERROR(DIV_ZERO);
					I_DIV(p->Int,(p+1)->Int);
					break;
				case MOD:
					if((p+1)->Int==0)
						ERROR(DIV_ZERO);
					I_MOD(p->Int, (p+1)->Int);
					break;
				case PROD:
					I_MUL(p->Int,(p+1)->Int);
					break;
				case SUM:
					I_ADD(p->Int,(p+1)->Int);
					break;
#ifdef TEST
				default:
					panic("Evaluator confused by byte-value %d",byte);
#endif
				}
			} else {
				switch(byte) {
				case DIFF:
					F_SUB(p->Float, (p+1)->Float);
					break;
				case DIV:
					if((p+1)->Float==0)
						ERROR(DIV_ZERO);
					F_DIV(p->Float, (p+1)->Float);
					break;
				case MOD:
					if((p+1)->Float==0)
						ERROR(DIV_ZERO);
					F_MOD(p->Float, (p+1)->Float);
					break;
				case PROD:
					F_MUL(p->Float, (p+1)->Float);
					break;
				case SUM:
					F_ADD(p->Float, (p+1)->Float);
					break;
#ifdef TEST
				default:
					panic("Unknown operation %d",byte);
#endif
				}
			}
			if(overflow)
				ERROR(OUT_OF_RANGE);
			break;

		case EQUAL:
		case NOTEQUAL:

		case GREATEQ:
		case GREATER:
		case LESS:
		case LESSEQ:
			if(p->type==TYP_ERR)
 				break;
			if((p+1)->type==TYP_ERR)
				ERROR((p+1)->Value);

			if(p->type==TYP_BOL || (p+1)->type==TYP_BOL) {
				if(p->type!=(p+1)->type || (byte!=EQUAL && byte!=NOTEQUAL))
					ERROR(BAD_INPUT);
				if(byte==EQUAL)
					p->Value=p->Value==(p+1)->Value;
				else
					p->Value=p->Value!=(p+1)->Value;
				break;
			}
			if(p->type!=(p+1)->type) {
				if(p->type==0) {
					if((p+1)->type==TYP_STR) {
						p->type=TYP_STR;
						p->String="";
					} else if((p+1)->type==TYP_INT) {
						p->type=TYP_INT;
						p->Int=0;
					} else {
						p->type=TYP_FLT;
						p->Float=0.0;
					}
				} else if((p+1)->type==0) {
					if(p->type==TYP_STR) {
						(p+1)->type=TYP_STR;
						(p+1)->String="";
					} else if(p->type==TYP_INT) {
						(p+1)->type=TYP_INT;
						(p+1)->Int=0;
					} else {
						(p+1)->type=TYP_FLT;
						(p+1)->Float=0.0;
					}
				} else if(p->type==TYP_STR) {
					strptr=p->String;
					if((p+1)->type==TYP_INT) {
						p->type=TYP_INT;
						p->Int=astol(&strptr);
					} else {
						p->type=TYP_FLT;
						p->Float=astof(&strptr);
					}
					if(*strptr) {
						p->type=TYP_BOL;
						p->Value=(byte==NOTEQUAL);
						break;
					}
				} else if((p+1)->type==TYP_STR) {
					strptr=(p+1)->String;
					if(p->type==TYP_INT)
						(p+1)->Int=astol(&strptr);
					else
						(p+1)->Float=astof(&strptr);
					if(*strptr) {
						p->type=TYP_BOL;
						p->Value=(byte==NOTEQUAL);
						break;
					}

				/* If we get here, one is INT, and the other
				   is FLT  Make them both FLT */
				} else if(p->type==TYP_INT) {
					p->type=TYP_FLT;
					p->Float=(double)p->Int;
				} else
					(p+1)->Float=(double)(p+1)->Int;
			}
			if(p->type==TYP_STR)
				tmp=strcmp(p->String,(p+1)->String);
			else if(p->type==TYP_FLT)
				tmp= (p->Float < (p+1)->Float) ? -1 : ((p->Float>(p+1)->Float) ? 1 : 0);
			else if(p->type==TYP_INT)
				tmp= (p->Int < (p+1)->Int ? -1 : ((p->Int>(p+1)->Int) ? 1 : 0));
			else if(p->type==0)
				tmp = 0;
			else {
				tmp=0;
				panic("Bad type value %d",p->type);
			}
			p->type=TYP_BOL;
			if(tmp<0)
				p->Value=(byte==NOTEQUAL || byte==LESS || byte==LESSEQ);
			else if(tmp==0)
				p->Value=(byte==EQUAL || byte==GREATEQ || byte==LESSEQ);
			else
				p->Value=(byte==NOTEQUAL || byte==GREATER || byte==GREATEQ);
			break;

		case F_FIXED:
			tmp=(p+1)->Int;
			if(tmp<-29 || tmp>29)
				ERROR(OUT_OF_RANGE);
			if(tmp<0)
				p->Float=rint((p->Float)/exp10_arr[-tmp])*exp10_arr[-tmp];
			else
				p->Float=rint((p->Float)*exp10_arr[tmp])/exp10_arr[tmp];
			break;

		case F_IFERR:
			if(p->type==TYP_ERR)
				*p= *(p+1);
			break;

		case F_INDEX:
			tmp=(p+1)->Int - 1;
			if(tmp<0)
				ERROR(OUT_OF_RANGE);
			lrow=p->Rng.lr;
			lcol=p->Rng.lc;
			hrow=p->Rng.hr;
			hcol=p->Rng.hc;
			if(lrow!=hrow && lcol!=hcol) {
				int dex;

				dex=1+hrow-lrow;
				if(tmp>=dex*(1+hcol-lcol))
					ERROR(OUT_OF_RANGE);
				crow=tmp%dex;
				ccol=tmp/dex;
				lrow+=crow;
				lcol+=ccol;
			} else if(lrow!=hrow) {
				if(tmp>(hrow-lrow))
					ERROR(OUT_OF_RANGE);
				lrow+=tmp;
			} else {
				if(tmp>(hcol-lcol))
					ERROR(OUT_OF_RANGE);
				lcol+=tmp;
			}
			cell_ptr=find_cell(lrow,lcol);
			PUSH_ANY(cell_ptr);
			break;

		case F_INDEX2:
			crow=(p+1)->Int - 1;
			ccol=(p+2)->Int - 1;
			lrow=p->Rng.lr;
			lcol=p->Rng.lc;
			hrow=p->Rng.hr;
			hcol=p->Rng.hc;
			if(crow<0 || ccol<0 || crow>(hrow-lrow) || ccol>(hcol-lcol))
				ERROR(OUT_OF_RANGE);
			cell_ptr=find_cell(lrow+crow,lcol+ccol);
			PUSH_ANY(cell_ptr);
			break;

		/* case F_PRINTF:
			panic("no printf yet");
			break; */

		case CONCAT:
			strptr=(char *)obstack_alloc(&tmp_mem,strlen(p->String)+strlen((p+1)->String)+1);
			strcpy(strptr,p->String);
			strcat(strptr,(p+1)->String);
			p->String=strptr;
			break;

		case F_ONEOF:
			if(numarg<2)
				ERROR(NO_VALUES);
			--numarg;
			tmp=p->Int;
			if(tmp<1 || tmp>numarg)
				ERROR(OUT_OF_RANGE);
			/* Can never happen? */
			TO_ANY(p+tmp);
			p[0]= p[tmp];
			break;

		case F_FILE:
		{
			FILE *fp;
			char buf[128];
			int num;
			extern size_t fread();

			if(numarg<1)
				ERROR(NO_VALUES);
			fp=fopen(p->String,"r");
			if(!fp)
				ERROR(BAD_INPUT);
			switch(numarg) {
			case 2:
				fseek(fp,(p+1)->Int,0);
				/* Fallthrough */

			case 1:
				while((num=fread(buf,sizeof(char),sizeof(buf),fp))>0)
					(void)obstack_grow(&tmp_mem,buf,num);
				break;

			case 3:
				fseek(fp,(p+1)->Int,0);
				for(;;) {
					num=((p+2)->Int<sizeof(buf)) ? (p+2)->Int : sizeof(buf);
					(p+2)->Int-=num;
					num=fread(buf,sizeof(char),num,fp);
					(void)obstack_grow(&tmp_mem,buf,num);
					if(num==0 || (p+2)->Int==0)
						break;
				}
				break;

			default:
				ERROR(BAD_INPUT);
			}
			fclose(fp);
			(void)obstack_1grow(&tmp_mem,0);
			p->String=obstack_finish(&tmp_mem);
			break;
		}

		case AREA_SUM:
		case AREA_PROD:
		case AREA_AVG:
		case AREA_STD:
		case AREA_MAX:
		case AREA_MIN:
		case AREA_CNT:
		case AREA_VAR:
			tmp=deal_area(byte,numarg,p);
			if(tmp)
				ERROR(tmp);
			break;

			/* This is now a fallthrough for all the USRmumble codes */
		case USR1:
		default:
			if((f->fn_argn&X_ARGS)==X_AN) {
				void (*funp) EXT2(int, struct value *);

				funp=(void (*) EXT2(int, struct value *))f->fn_fun;
				(*funp)(numarg,p);
			} else {
				void (*funp) EXT1(struct value *);
				funp=(void(*)EXT1(struct value *))f->fn_fun;
				(*funp)(p);
			}
			break;

/* #ifdef TEST
		default:
			panic("Unknown byte-value %d",byte);
			break; 
#endif */
		}
		/* Goto next-byte is the equiv of a multi-level break, which
		   C doesn't allow. */
	next_byte:
		;
	}
#ifdef TEST
	if (curstack != 1)
		error_msg("%d values on stack", curstack);
#endif
	return stack;
}

/* These helper functions were split out so that eval_expression would compile
   under Turbo C 2.0 on my PC.
 */


static int	cnt_flt;
static int	cnt_int;

static long	int_tmp;
static double	flt_tmp;

static long	sqr_int_tmp;	/* for AREA_STD */
static double	sqr_flt_tmp;

static unsigned char area_cmd;

static int
deal_area FUN3(unsigned char, cmd, unsigned char,num_args, struct value *,p)
{
	double flt_cnt_flt;
	CELL *cell_ptr;
	char *strptr;

	area_cmd=cmd;
	cnt_flt = 0;
	cnt_int = 0;
	for(;num_args--;) {
		switch(p[num_args].type) {
		case TYP_INT:
			add_int(p[num_args].Int);
			break;

		case TYP_FLT:
			add_flt(p[num_args].Float);
			break;

		case TYP_STR:
			strptr=p[num_args].String;
			flt_cnt_flt=astof(&strptr);
			if(*strptr)
				return NON_NUMBER;
			add_flt(flt_cnt_flt);
			break;

		case TYP_RNG:
			find_cells_in_range(&(p[num_args].Rng));
			while(cell_ptr=next_cell_in_range()) {
				if(GET_TYP(cell_ptr)==TYP_FLT)
					add_flt(cell_ptr->cell_flt);
				else if(GET_TYP(cell_ptr)==TYP_INT)
					add_int(cell_ptr->cell_int);
				else if(GET_TYP(cell_ptr)==TYP_STR) {
					strptr=cell_ptr->cell_str;
					flt_cnt_flt=astof(&strptr);
					if(!*strptr)
						add_flt(flt_cnt_flt);
				}
			}
			break;

		case 0:
			break;

		case TYP_ERR:
			return p[num_args].Value;

		default:
			return NON_NUMBER;
		}
	}
	if(!cnt_flt && !cnt_int && area_cmd!=AREA_CNT)
		return NO_VALUES;

	switch(area_cmd) {
	case AREA_SUM:
		if(cnt_flt && cnt_int) {
			flt_tmp+=(double)int_tmp;
			cnt_int=0;
		}
		break;
	case AREA_PROD:
		if(cnt_flt && cnt_int) {
			flt_tmp*=(double)int_tmp;
			cnt_int=0;
		}
		break;
	case AREA_AVG:
		if(cnt_flt && cnt_int) {
			flt_tmp+=(double)int_tmp;
			flt_tmp/=(double)((cnt_flt+cnt_int));
			cnt_int=0;
		} else if(cnt_flt)
			flt_tmp/=(double)cnt_flt;
		else {
			flt_tmp=(double)int_tmp/(double)cnt_int;
			cnt_int=0;
		}
		break;
	case AREA_STD:
		if(cnt_int && cnt_flt) {
			flt_tmp+=(double)int_tmp;
			sqr_flt_tmp+=(double)sqr_int_tmp;
			cnt_flt+=cnt_int;
			cnt_int=0;
		} else if(cnt_int) {
			flt_tmp=(double)int_tmp;
			sqr_flt_tmp=(double)sqr_int_tmp;
			cnt_flt=cnt_int;
			cnt_int=0;
		}
		flt_cnt_flt=(double)cnt_flt;
		flt_tmp=sqrt( ( (flt_cnt_flt*sqr_flt_tmp) -
				(flt_tmp*flt_tmp) )         /
				(flt_cnt_flt * (flt_cnt_flt-1)));
		break;
	case AREA_VAR:
		if(cnt_int && cnt_flt) {
			flt_tmp+=(double)int_tmp;
			sqr_flt_tmp+=(double)sqr_int_tmp;
			cnt_flt+=cnt_int;
			cnt_int=0;
		} else if(cnt_int) {
			flt_tmp=(double)int_tmp;
			sqr_flt_tmp=(double)sqr_int_tmp;
			cnt_flt=cnt_int;
			cnt_int=0;
		}
		flt_cnt_flt=(double)cnt_flt;
		flt_tmp=( (flt_cnt_flt*sqr_flt_tmp) -
				(flt_tmp*flt_tmp) )         /
				(flt_cnt_flt * flt_cnt_flt);
		break;

	case AREA_MAX:
		if(cnt_flt && cnt_int && flt_tmp>(double)int_tmp)
			cnt_int=0;
		break;

	case AREA_MIN:
		if(cnt_flt && cnt_int && flt_tmp<(double)int_tmp)
			cnt_int=0;
		break;

	case AREA_CNT:
		int_tmp=cnt_int+cnt_flt;
		cnt_int=1;
		break;

#ifdef TEST
	default:
		panic("Unknown AREA command %d",area_cmd);
#endif
	}
	if(cnt_int) {
		p->type=TYP_INT;
		p->Int=int_tmp;
	} else {
		p->type=TYP_FLT;
		p->Float=flt_tmp;
	}
	return 0;
}

static void
add_flt FUN1(double, value)
{
	if(cnt_flt++==0) {
		flt_tmp = value;
		sqr_flt_tmp=value*value;
		return;
	}

	switch(area_cmd) {
	case AREA_STD:
	case AREA_VAR:
		sqr_flt_tmp+=value*value;
		/* Fall through */
	case AREA_SUM:
	case AREA_AVG:
		flt_tmp +=value;
		return;
	case AREA_PROD:
		flt_tmp *=value;
		return;
	case AREA_MAX:
		if(flt_tmp<value)
			flt_tmp=value;
		return;
	case AREA_MIN:
		if(flt_tmp>value)
			flt_tmp=value;
		return;
	case AREA_CNT:
		return;
#ifdef TEST
	default:
		panic("Unknown area command %d in add_flt(%g)",area_cmd,value);
#endif
	}
}

static void
add_int FUN1(long, value)
{
	if(cnt_int++==0) {
		int_tmp=value;
		sqr_int_tmp=value*value;
		return;
	}

	switch(area_cmd) {
	case AREA_STD:
	case AREA_VAR:
		sqr_int_tmp+=value*value;
		/* Fall through */
	case AREA_SUM:
	case AREA_AVG:
		int_tmp+=value;
		return;
	case AREA_PROD:
		int_tmp*=value;
		return;
	case AREA_MAX:
		if(int_tmp<value)
			int_tmp=value;
		return;
	case AREA_MIN:
		if(int_tmp>value)
			int_tmp=value;
		return;
	case AREA_CNT:
		return;
#ifdef TEST
	default:
		panic("Unknown Area command %d in add_int(%ld)",area_cmd,value);
#endif
	}
}

double
dtr FUN1(double, x)
{
	return x * (PI / (double)180.0);
}

double
rtd FUN1(double, x)
{
	return x * (180.0 / (double)PI);
}

double
to_int FUN1(double, x)
{
	return (x<0 ? ceil(x) : floor(x));
}

/* Various methods of dealing with arithmatic overflow.  They don't work well.
   Someone should really convince this thing to properly deal with it.
 */
#ifdef __TURBOC__
int
matherr FUN1(struct exception *, exc)
{
	stack[curstack].type=TYP_ERR;
	stack[curstack].Value=BAD_INPUT;
	write(2,"MATHERR\n",8);
	return 1;
}
#endif

#ifndef __TURBOC__
SIGTY
math_sig FUN1(int, sig)
{
	stack[curstack].type=TYP_ERR;
	stack[curstack].Value=BAD_INPUT;
	(void)write(2,"MATH_SIG\n",9);
	SIGRET;
}
#endif

/* Here's the entry point for this module. */
void
update_cell FUN1(CELL *,cell)
{
	struct value *new;
	int new_val;

#ifdef TEST
	extern int debug;

	if(cell->cell_cycle==current_cycle && (debug&01)) {
		error_msg("Cycle detected at %s (%d)",cell_name(cur_row,cur_col),current_cycle);
		/* return; */
	} else if(debug&02)
		error_msg("Update %s",cell_name(cur_row,cur_col));
#endif
	new=eval_expression(cell->cell_formula);
	if(!new) {
		push_refs(cell->cell_refs_from);
		return;
	}
	cell->cell_cycle=current_cycle;

	if(new->type!=GET_TYP(cell)) {
		if(GET_TYP(cell)==TYP_STR)
			free(cell->cell_str);
		SET_TYP(cell,new->type);
		new_val=1;
		if(new->type==TYP_STR)
			new->String=strdup(new->String);
	} else switch(new->type) {
	case 0:
		new_val=0;
		break;
	case TYP_FLT:
		new_val=new->Float!=cell->cell_flt;
		break;
	case TYP_INT:
		new_val=new->Int!=cell->cell_int;
		break;
	case TYP_STR:
		new_val= strcmp(new->String,cell->cell_str);
		if(new_val) {
			free(cell->cell_str);
			new->String=strdup(new->String);
		}
		break;
	case TYP_BOL:
		new_val= new->Value!=cell->cell_bol;
		break;
	case TYP_ERR:
		new_val=new->Value!=cell->cell_err;
		break;
#ifdef TEST
	default:
		new_val=0;
		panic("Unknown type %d in update_cell",new->type);
#endif
	}
	if(new_val) {
		cell->c_z=new->x;
		push_refs(cell->cell_refs_from);
	}
	(void)obstack_free(&tmp_mem,tmp_mem_start);
}

int
fls FUN1(long, num)
{
	int ret=1;

	if(!num)
		return 0;
	if(num<0)
		num= -num;
	if(num&0xffff0000) {
		ret+=16;
		num= (num>>16)&0xffff;
	}
	if(num&0xff00) {
		ret+=8;
		num>>=8;
	}
	if(num&0xf0){
		ret+=4;
		num>>=4;
	}
	if(num&0x0c) {
		ret+=2;
		num>>=2;
	}
	if(num&2)
		ret++;
	return ret;
}

#ifdef SMALLEVAL
int
__to_flt FUN1(struct value *,p)
{
	char *strptr;

	switch(p->type) {
	case 0:
		p->type=TYP_FLT;
		p->Float=0;
		/* Fallthrough */
	case TYP_FLT:
		return 0;
	case TYP_INT:
		p->Float=(double)p->Int;
		p->type=TYP_FLT;
		return 0;
	case TYP_STR:
		p->type=TYP_FLT;
		strptr=p->String;
		p->Float=astof(&strptr);
		if(*strptr)
			return NON_NUMBER;
		return 0;
	case TYP_ERR:
		return p->Value;
	default:
		return NON_NUMBER;
	}
}

int
__to_int FUN1(struct value *,p)
{
	char *strptr;

	switch(p->type) {
	case 0:
		p->type=TYP_INT;
		p->Int=0;
	case TYP_INT:
		return 0;
	case TYP_FLT:
		p->type=TYP_INT;
		p->Int=(long)p->Float;
		return 0;
	case TYP_STR:
		p->type=TYP_INT;
		strptr=p->String;
		p->Int=astol(&strptr);
		if(*strptr)
			return NON_NUMBER;
		return 0;
	case TYP_ERR:
		return p->Value;
	default:
		return NON_NUMBER;
	}
}

int
__to_num FUN1(struct value *,p)
{
	char *strptr;

	switch(p->type) {
	case 0:
		p->type=TYP_INT;
		p->Int=0;
		return 0;
	case TYP_FLT:
	case TYP_INT:
		return 0;
	case TYP_STR:
		p->type=TYP_FLT;
		strptr=p->String;
		p->Float=astof(&strptr);
		if(*strptr)
			return NON_NUMBER;
		return 0;
	case TYP_ERR:
		return p->Value;
	default:
		return NON_NUMBER;
	}
}

int
__to_str FUN1(struct value *,p)
{
	char *strptr;

	switch(p->type) {
	case 0:
		p->type=TYP_STR;
		p->String=obstack_alloc(&tmp_mem,1);
		p->String[0]='\0';
		return 0;

	case TYP_STR:
		return 0;

	case TYP_INT:
		p->type=TYP_STR;
		strptr=obstack_alloc(&tmp_mem,30);
		sprintf(strptr,"%ld",p->Int);
		p->String=strptr;
		return 0;

	case TYP_FLT:
		p->type=TYP_STR;
		strptr=flt_to_str(p->Float);
		(void)obstack_grow(&tmp_mem,strptr,strlen(strptr)+1);
		p->String=obstack_finish(&tmp_mem);
		return 0;

	case TYP_ERR:
		return p->Value;

	default:
		return NON_STRING;
	}
}

int
__to_bol FUN1(struct value *,p)
{
	switch(p->type) {
	case TYP_BOL:
		return 0;
	case TYP_ERR:
		return p->Value;
	default:
		return NON_BOOL;
	}
}

int
__to_rng FUN1(struct value *,p)
{
	switch(p->type) {
	case TYP_RNG:
		return 0;
	case TYP_ERR:
		return p->Value;
	default:
		return NON_BOOL;
	}
}

#endif
