1043 lines
22 KiB
C
1043 lines
22 KiB
C
/*
|
|
* math.c - mathematics functions for a hand calculator under X
|
|
*
|
|
* Author: John H. Bradley, University of Pennsylvania
|
|
* (bradley@cis.upenn.edu)
|
|
* March, 1987
|
|
*
|
|
* RPN mode added and port to X11 by Mark Rosenstein, MIT Project Athena
|
|
*
|
|
* Modified to be a client of the Xt toolkit and the Athena widget set by
|
|
* Donna Converse, MIT X Consortium. This is all that remains of the
|
|
* original calculator, and it still needs to be rewritten. The HP
|
|
* functionality should be separated from the TI functionality.
|
|
* Beware the HP functions: there are still errors here.
|
|
*
|
|
* Geoffrey Coram fixed most of the HP mode bugs.
|
|
*/
|
|
|
|
#include "xcalc.h"
|
|
|
|
#ifndef M_PI /* sometimes defined in math.h */
|
|
#define M_PI 3.14159265358979323846
|
|
#endif
|
|
|
|
#ifndef M_E /* sometimes defined in math.h */
|
|
#define M_E 2.7182818284590452354
|
|
#endif
|
|
|
|
#define MAXDISP 11
|
|
#define DEG 0 /* DRG mode. used for trig calculations */
|
|
#define RAD 1
|
|
#define GRAD 2
|
|
|
|
#define True 1
|
|
#define False 0
|
|
|
|
#ifndef IEEE
|
|
jmp_buf env;
|
|
#endif
|
|
|
|
|
|
/* This section is all of the state machine that implements the calculator
|
|
* functions. Much of it is shared between the infix and rpn modes.
|
|
*/
|
|
|
|
static int flagINV, flagPAREN, flagM, drgmode, numbase; /* display flags */
|
|
|
|
static double drg2rad=M_PI/180.0; /* Conversion factors for trig funcs */
|
|
static double rad2drg=180.0/M_PI;
|
|
static int entered=1; /* true if display contains a valid number.
|
|
if==2, then use 'dnum', rather than the string
|
|
stored in the display. (for accuracy)
|
|
if==3, then error occurred, only CLR & AC work */
|
|
/* entered seems to be overloaded - dmc */
|
|
static int lift_enabled = 0; /* for rpn mode only */
|
|
|
|
static int CLR =0; /* CLR clears display. if 1, clears acc, also */
|
|
static int Dpoint=0; /* to prevent using decimal pt twice in a # */
|
|
static int clrdisp=1; /* if true clears display before entering # */
|
|
static int lastop =kCLR;
|
|
static int memop =kCLR;
|
|
static int exponent=0;
|
|
static double acc =0.0;
|
|
static double dnum=0.0;
|
|
#define XCALC_MEMORY 10
|
|
static double mem[XCALC_MEMORY] = { 0.0 };
|
|
|
|
static void DrawDisplay(void);
|
|
static void PushOp(int op);
|
|
static int PopOp(void);
|
|
static int isopempty(void);
|
|
#ifdef DEBUG
|
|
static void showstack(char *string);
|
|
#endif
|
|
static void PushNum(double num);
|
|
static double PopNum(void);
|
|
static void RollNum(int dir);
|
|
static void ClearStacks(void);
|
|
static int priority(int op);
|
|
|
|
#ifndef HAVE_STRLCPY
|
|
/* Close enough for the short strings copied in xcalc */
|
|
static inline size_t
|
|
strlcpy(char *dst, const char *src, size_t size)
|
|
{
|
|
strncpy(dst, src, size);
|
|
dst[size - 1] = '\0';
|
|
return strlen(src);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* The following is to deal with the unfortunate assumption that if errno
|
|
* is non-zero then an error has occurred. On some systems (e.g. Ultrix),
|
|
* sscanf will call lower level routines that will set errno.
|
|
*/
|
|
static void
|
|
parse_double(double *dp)
|
|
{
|
|
unsigned long n = 0;
|
|
int olderrno = errno;
|
|
|
|
switch (numbase) {
|
|
case 8:
|
|
(void)sscanf(dispstr, "%lo", &n);
|
|
*dp = (double)n;
|
|
break;
|
|
case 16:
|
|
(void)sscanf(dispstr, "%lX", &n);
|
|
*dp = (double)n;
|
|
break;
|
|
default:
|
|
(void)sscanf(dispstr, "%lf", dp);
|
|
}
|
|
|
|
errno = olderrno;
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Format the given double according to the
|
|
* selected number base.
|
|
*/
|
|
static void
|
|
format_double(double n)
|
|
{
|
|
switch (numbase) {
|
|
case 8:
|
|
snprintf(dispstr, sizeof(dispstr), "%lo", (long)n);
|
|
break;
|
|
case 16:
|
|
snprintf(dispstr, sizeof(dispstr), "%lX", (long)n);
|
|
break;
|
|
default:
|
|
snprintf(dispstr, sizeof(dispstr), "%.8g", n);
|
|
}
|
|
}
|
|
|
|
/*********************************/
|
|
int pre_op(int keynum)
|
|
{
|
|
if (keynum==-1) return(0);
|
|
|
|
errno = 0; /* for non-IEEE machines */
|
|
|
|
if ( (entered==3) && !(keynum==kCLR || keynum==kOFF)) {
|
|
if (rpn) {
|
|
clrdisp++;
|
|
} else {
|
|
ringbell();
|
|
return(1); /* the intent was probably not to do the operation */
|
|
}
|
|
}
|
|
|
|
if (keynum != kCLR) CLR=0;
|
|
return(0);
|
|
}
|
|
|
|
#ifndef IEEE
|
|
|
|
/* cannot assign result of setjmp under ANSI C, use global instead */
|
|
static volatile int SignalKind;
|
|
|
|
void fail_op(void)
|
|
{
|
|
if (SignalKind == SIGFPE)
|
|
strlcpy(dispstr, "math error", sizeof(dispstr));
|
|
else if (SignalKind == SIGILL)
|
|
strlcpy(dispstr, "illegal operand", sizeof(dispstr));
|
|
|
|
entered=3;
|
|
DrawDisplay();
|
|
return;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
void fperr(int sig)
|
|
{
|
|
#if defined(SYSV) || defined(SVR4) || defined(linux)
|
|
signal(SIGFPE, fperr);
|
|
#endif
|
|
SignalKind = sig;
|
|
longjmp(env,1);
|
|
}
|
|
|
|
/* for VAX BSD4.3 */
|
|
/*ARGSUSED*/
|
|
void illerr(int sig)
|
|
{
|
|
/* not reset when caught? */
|
|
signal(SIGILL, illerr);
|
|
|
|
SignalKind = sig;
|
|
longjmp(env,1);
|
|
}
|
|
|
|
#endif /* not IEEE */
|
|
|
|
|
|
void post_op(void)
|
|
{
|
|
#ifdef DEBUG
|
|
showstack("\0");
|
|
#endif
|
|
#ifndef IEEE
|
|
if (errno) {
|
|
strlcpy(dispstr, "error", sizeof(dispstr));
|
|
DrawDisplay();
|
|
entered=3;
|
|
errno=0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void
|
|
DrawDisplay(void)
|
|
{
|
|
if (strlen(dispstr) >= MAXDISP) { /* strip out some decimal digits */
|
|
char *estr = index(dispstr,'e'); /* search for exponent part */
|
|
if (!estr) dispstr[12]='\0'; /* no exp, just trunc. */
|
|
else {
|
|
char tmp[32];
|
|
if (strlen(estr) <= 4) /* leftmost 8 chars plus exponent */
|
|
snprintf(tmp, sizeof(tmp), "%.8s%s", dispstr, estr);
|
|
else /* leftmost 7 chars plus exponent */
|
|
snprintf(tmp, sizeof(tmp), "%.7s%s", dispstr, estr);
|
|
strlcpy(dispstr, tmp, sizeof(dispstr));
|
|
}
|
|
}
|
|
draw(dispstr);
|
|
setflag(XCalc_MEMORY, (flagM));
|
|
setflag(XCalc_INVERSE, (flagINV));
|
|
setflag(XCalc_DEGREE, (drgmode==DEG));
|
|
setflag(XCalc_RADIAN, (drgmode==RAD));
|
|
setflag(XCalc_GRADAM, (drgmode==GRAD));
|
|
setflag(XCalc_PAREN, (flagPAREN));
|
|
setflag(XCalc_HEX, (numbase==16));
|
|
setflag(XCalc_DEC, (numbase==10));
|
|
setflag(XCalc_OCT, (numbase==8));
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
change_base(void)
|
|
{
|
|
parse_double(&dnum);
|
|
|
|
if (dnum >= 0) {
|
|
switch (numbase) {
|
|
case 8: numbase = 10; break;
|
|
case 10: numbase = 16; break;
|
|
case 16: numbase = 8; break;
|
|
}
|
|
|
|
format_double(dnum);
|
|
} else strlcpy(dispstr, "error", sizeof(dispstr));
|
|
|
|
DrawDisplay();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
numeric(int keynum)
|
|
{
|
|
char st[2];
|
|
|
|
flagINV=0;
|
|
|
|
if (rpn && (memop == kSTO || memop == kRCL || memop == kSUM)) {
|
|
int cell = 0;
|
|
|
|
switch (keynum) {
|
|
case kONE: cell = 1; break;
|
|
case kTWO: cell = 2; break;
|
|
case kTHREE: cell = 3; break;
|
|
case kFOUR: cell = 4; break;
|
|
case kFIVE: cell = 5; break;
|
|
case kSIX: cell = 6; break;
|
|
case kSEVEN: cell = 7; break;
|
|
case kEIGHT: cell = 8; break;
|
|
case kNINE: cell = 9; break;
|
|
case kZERO: cell = 0; break;
|
|
}
|
|
switch (memop) {
|
|
case kSTO:
|
|
mem[cell] = dnum;
|
|
lift_enabled = 1;
|
|
entered = 2;
|
|
clrdisp++;
|
|
break;
|
|
case kRCL:
|
|
PushNum(dnum);
|
|
dnum = mem[cell];
|
|
format_double(dnum);
|
|
lift_enabled = 1;
|
|
entered = 1;
|
|
clrdisp++;
|
|
break;
|
|
case kSUM:
|
|
mem[cell] += dnum;
|
|
lift_enabled = 1;
|
|
entered = 2;
|
|
clrdisp++;
|
|
break;
|
|
}
|
|
memop = kCLR;
|
|
DrawDisplay();
|
|
return;
|
|
}
|
|
|
|
if (clrdisp) {
|
|
dispstr[0]='\0';
|
|
exponent=Dpoint=0;
|
|
/* if (rpn && entered==2)
|
|
PushNum(dnum);
|
|
*/
|
|
if (rpn & lift_enabled)
|
|
PushNum(dnum);
|
|
}
|
|
if ((int) strlen(dispstr) >= MAXDISP)
|
|
return;
|
|
|
|
st[0] = '\0';
|
|
switch (keynum){
|
|
case kZERO: st[0] = '0'; break;
|
|
case kONE: st[0] = '1'; break;
|
|
case kTWO: st[0] = '2'; break;
|
|
case kTHREE: st[0] = '3'; break;
|
|
case kFOUR: st[0] = '4'; break;
|
|
case kFIVE: st[0] = '5'; break;
|
|
case kSIX: st[0] = '6'; break;
|
|
case kSEVEN: st[0] = '7'; break;
|
|
case kEIGHT: if (numbase > 8) st[0] = '8'; break;
|
|
case kNINE: if (numbase > 8) st[0] = '9'; break;
|
|
case kxA: if (numbase > 10) st[0] = 'A'; break;
|
|
case kxB: if (numbase > 10) st[0] = 'B'; break;
|
|
case kxC: if (numbase > 10) st[0] = 'C'; break;
|
|
case kxD: if (numbase > 10) st[0] = 'D'; break;
|
|
case kxE: if (numbase > 10) st[0] = 'E'; break;
|
|
case kxF: if (numbase > 10) st[0] = 'F'; break;
|
|
}
|
|
|
|
if (st[0] == '\0')
|
|
return;
|
|
st[1] = '\0';
|
|
strcat(dispstr,st);
|
|
|
|
DrawDisplay();
|
|
if (clrdisp && keynum != kZERO)
|
|
clrdisp=0; /*no leading 0s*/
|
|
memop = keynum;
|
|
entered=1;
|
|
lift_enabled = 0;
|
|
}
|
|
|
|
void
|
|
bkspf(void)
|
|
{
|
|
|
|
lift_enabled = 0;
|
|
|
|
if (! flagINV)
|
|
{
|
|
if (entered!=1) {
|
|
clearf();
|
|
return;
|
|
}
|
|
if (clrdisp)
|
|
return;
|
|
if ((int) strlen(dispstr) > 0) {
|
|
#ifndef X_LOCALE
|
|
const char *dp = localeconv()->decimal_point;
|
|
size_t dp_len = strlen(dp);
|
|
size_t ds_len = strlen(dispstr);
|
|
if (ds_len >= dp_len && strcmp(dispstr + ds_len - dp_len, dp) == 0)
|
|
Dpoint=0;
|
|
#else
|
|
if (dispstr[strlen(dispstr)-1] == '.')
|
|
Dpoint=0;
|
|
#endif
|
|
dispstr[strlen(dispstr)-1] = 0;
|
|
}
|
|
if (strlen(dispstr) == 0) {
|
|
strcat(dispstr, "0");
|
|
clrdisp++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strlcpy(dispstr, "0", sizeof(dispstr));
|
|
dnum = 0.0;
|
|
clrdisp++;
|
|
flagINV = 0;
|
|
}
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
decf(void)
|
|
{
|
|
flagINV=0;
|
|
if (clrdisp) {
|
|
if (rpn && lift_enabled)
|
|
PushNum(dnum);
|
|
strlcpy(dispstr, "0", sizeof(dispstr));
|
|
}
|
|
if (!Dpoint) {
|
|
#ifndef X_LOCALE
|
|
strcat(dispstr, localeconv()->decimal_point);
|
|
#else
|
|
strcat(dispstr, ".");
|
|
#endif
|
|
DrawDisplay();
|
|
Dpoint++;
|
|
}
|
|
clrdisp=0;
|
|
entered=1;
|
|
}
|
|
|
|
void
|
|
eef(void)
|
|
{
|
|
flagINV=0;
|
|
if (clrdisp) {
|
|
if (rpn && lift_enabled)
|
|
PushNum(dnum);
|
|
strlcpy(dispstr, rpn ? "1" : "0", sizeof(dispstr));
|
|
}
|
|
if (!exponent) {
|
|
strcat(dispstr,"E+");
|
|
DrawDisplay();
|
|
exponent=strlen(dispstr)-1; /* where the '-' goes */
|
|
}
|
|
clrdisp=0;
|
|
entered=1;
|
|
}
|
|
|
|
void
|
|
clearf(void)
|
|
{
|
|
flagINV=0;
|
|
if (CLR && !rpn) { /* clear all */
|
|
ClearStacks();
|
|
flagPAREN=0;
|
|
}
|
|
CLR++;
|
|
exponent=Dpoint=0;
|
|
clrdisp=1;
|
|
entered=1;
|
|
strlcpy(dispstr, "0", sizeof(dispstr));
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
negf(void)
|
|
{
|
|
flagINV=0;
|
|
if (exponent) { /* neg the exponent */
|
|
if (dispstr[exponent]=='-')
|
|
dispstr[exponent]='+';
|
|
else
|
|
dispstr[exponent]='-';
|
|
DrawDisplay();
|
|
return;
|
|
}
|
|
|
|
if (strcmp("0",dispstr)==0)
|
|
return; /* don't neg a zero */
|
|
if (dispstr[0]=='-') /* already neg-ed */
|
|
strcpy(dispstr,dispstr+1); /* move str left once */
|
|
else { /* not neg-ed. add a '-' */
|
|
char tmp[32];
|
|
snprintf(tmp, sizeof(tmp), "-%s", dispstr);
|
|
strlcpy(dispstr, tmp, sizeof(dispstr));
|
|
}
|
|
if (entered==2)
|
|
dnum = -1.0 * dnum;
|
|
DrawDisplay();
|
|
}
|
|
|
|
/* Two operand functions for infix calc */
|
|
void
|
|
twoop(int keynum)
|
|
{
|
|
if (flagINV) {
|
|
flagINV=0;
|
|
DrawDisplay();
|
|
}
|
|
|
|
if (!entered) { /* something like "5+*" */
|
|
if (!isopempty())
|
|
(void) PopOp(); /* replace the prev op */
|
|
PushOp(keynum); /* with the new one */
|
|
return;
|
|
}
|
|
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
|
|
clrdisp=CLR=1;
|
|
entered=Dpoint=exponent=0;
|
|
|
|
if (!isopempty()) { /* there was a previous op */
|
|
lastop=PopOp(); /* get it */
|
|
|
|
if (lastop==kLPAR) { /* put it back */
|
|
PushOp(kLPAR);
|
|
PushOp(keynum);
|
|
PushNum(dnum);
|
|
return;
|
|
}
|
|
|
|
/* now, if the current op (keynum) is of
|
|
higher priority than the lastop, the current
|
|
op and number are just pushed on top
|
|
Priorities: (Y^X) > *,/ > +,- > >>,<< > & > ^ > ~ */
|
|
|
|
if (priority(keynum) > priority(lastop)) {
|
|
PushNum(dnum);
|
|
PushOp(lastop);
|
|
PushOp(keynum);
|
|
} else { /* execute lastop on lastnum and dnum, push
|
|
result and current op on stack */
|
|
acc=PopNum();
|
|
switch (lastop) { /* perform the operation */
|
|
case kADD: acc += dnum; break;
|
|
case kSUB: acc -= dnum; break;
|
|
case kMUL: acc *= dnum; break;
|
|
case kDIV: acc /= dnum; break;
|
|
case kPOW: acc = pow(acc,dnum); break;
|
|
case kMOD: acc = (long)acc % (long)dnum; break;
|
|
case kAND: acc = (long)acc & (long)dnum; break;
|
|
case kOR: acc = (long)acc | (long)dnum; break;
|
|
case kXOR: acc = (long)acc ^ (long)dnum; break;
|
|
case kSHL: acc = (long)acc << (long)dnum; break;
|
|
case kSHR: acc = (long)acc >> (long)dnum; break;
|
|
}
|
|
|
|
PushNum(acc);
|
|
PushOp(keynum);
|
|
format_double(acc);
|
|
DrawDisplay();
|
|
dnum=acc;
|
|
}
|
|
}
|
|
else { /* op stack is empty, push op and num */
|
|
PushOp(keynum);
|
|
PushNum(dnum);
|
|
}
|
|
}
|
|
|
|
/* Two operand functions for rpn calc */
|
|
void
|
|
twof(int keynum)
|
|
{
|
|
if (flagINV) {
|
|
flagINV=0;
|
|
DrawDisplay();
|
|
}
|
|
if (!entered)
|
|
return;
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
acc = PopNum();
|
|
switch(keynum) {
|
|
case kADD: acc += dnum; break;
|
|
case kSUB: acc -= dnum; break;
|
|
case kMUL: acc *= dnum; break;
|
|
case kDIV: acc /= dnum; break;
|
|
case kPOW: acc = pow(acc,dnum); break;
|
|
case kXXY: PushNum(dnum); break;
|
|
case kMOD: acc = (long)acc % (long)dnum; break;
|
|
case kAND: acc = (long)acc & (long)dnum; break;
|
|
case kOR: acc = (long)acc | (long)dnum; break;
|
|
case kXOR: acc = (long)acc ^ (long)dnum; break;
|
|
case kSHL: acc = (long)acc << (long)dnum; break;
|
|
case kSHR: acc = (long)acc >> (long)dnum; break;
|
|
}
|
|
|
|
format_double(acc);
|
|
DrawDisplay();
|
|
clrdisp++;
|
|
Dpoint = exponent = 0;
|
|
entered = 2;
|
|
lift_enabled = 1;
|
|
dnum = acc;
|
|
}
|
|
|
|
void
|
|
entrf(void)
|
|
{
|
|
flagINV=0;
|
|
if (!entered)
|
|
return;
|
|
|
|
clrdisp=CLR=1;
|
|
Dpoint=exponent=0;
|
|
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
entered=2;
|
|
memop = kENTR;
|
|
PushNum(dnum);
|
|
lift_enabled = 0;
|
|
}
|
|
|
|
void
|
|
equf(void)
|
|
{
|
|
flagINV=0;
|
|
if (!entered)
|
|
return;
|
|
|
|
clrdisp=CLR=1;
|
|
Dpoint=exponent=0;
|
|
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
entered=2;
|
|
|
|
PushNum(dnum);
|
|
|
|
while (!isopempty()) { /* do all pending ops */
|
|
dnum=PopNum();
|
|
acc=PopNum();
|
|
lastop=PopOp();
|
|
switch (lastop) {
|
|
case kADD: acc += dnum;
|
|
break;
|
|
case kSUB: acc -= dnum;
|
|
break;
|
|
case kMUL: acc *= dnum;
|
|
break;
|
|
case kDIV: acc /= dnum;
|
|
break;
|
|
case kPOW: acc = pow(acc,dnum);
|
|
break;
|
|
case kLPAR: flagPAREN--;
|
|
PushNum(acc);
|
|
break;
|
|
case kMOD: acc = (long)acc % (long)dnum;
|
|
break;
|
|
case kAND: acc = (long)acc & (long)dnum;
|
|
break;
|
|
case kOR: acc = (long)acc | (long)dnum;
|
|
break;
|
|
case kXOR: acc = (long)acc ^ (long)dnum;
|
|
break;
|
|
case kSHL: acc = (long)acc << (long)dnum;
|
|
break;
|
|
case kSHR: acc = (long)acc >> (long)dnum;
|
|
break;
|
|
}
|
|
dnum=acc;
|
|
PushNum(dnum);
|
|
}
|
|
|
|
format_double(dnum);
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
lparf(void)
|
|
{
|
|
flagINV=0;
|
|
PushOp(kLPAR);
|
|
flagPAREN++;
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
rollf(void)
|
|
{
|
|
if (!entered)
|
|
return;
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
entered = 2;
|
|
lift_enabled = 1;
|
|
RollNum(flagINV);
|
|
flagINV=0;
|
|
clrdisp++;
|
|
format_double(dnum);
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
rparf(void)
|
|
{
|
|
flagINV=0;
|
|
if (!entered)
|
|
return;
|
|
|
|
if (!flagPAREN)
|
|
return;
|
|
|
|
clrdisp++;
|
|
Dpoint=exponent=0;
|
|
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
entered=2;
|
|
|
|
PushNum(dnum);
|
|
while (!isopempty() && (lastop=PopOp())!=kLPAR) {
|
|
/* do all pending ops, back to left paren */
|
|
dnum=PopNum();
|
|
acc=PopNum();
|
|
switch (lastop) {
|
|
case kADD: acc += dnum;
|
|
break;
|
|
case kSUB: acc -= dnum;
|
|
break;
|
|
case kMUL: acc *= dnum;
|
|
break;
|
|
case kDIV: acc /= dnum;
|
|
break;
|
|
case kPOW: acc = pow(acc,dnum);
|
|
break;
|
|
case kMOD: acc = (long)acc % (long)dnum;
|
|
break;
|
|
case kAND: acc = (long)acc & (long)dnum;
|
|
break;
|
|
case kOR: acc = (long)acc | (long)dnum;
|
|
break;
|
|
case kXOR: acc = (long)acc ^ (long)dnum;
|
|
break;
|
|
case kSHL: acc = (long)acc << (long)dnum;
|
|
break;
|
|
case kSHR: acc = (long)acc >> (long)dnum;
|
|
break;
|
|
}
|
|
dnum=acc;
|
|
PushNum(dnum);
|
|
}
|
|
(void) PopNum();
|
|
flagPAREN--;
|
|
entered=2;
|
|
format_double(dnum);
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
drgf(void)
|
|
{
|
|
if (flagINV) {
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
switch (drgmode) {
|
|
case DEG: dnum=dnum*M_PI/180.0; break;
|
|
case RAD: dnum=dnum*200.0/M_PI; break;
|
|
case GRAD: dnum=dnum*90.0/100.0; break;
|
|
}
|
|
entered=2;
|
|
clrdisp=1;
|
|
flagINV=0;
|
|
format_double(dnum);
|
|
}
|
|
|
|
flagINV=0;
|
|
drgmode = (drgmode + 1) % 3;
|
|
switch (drgmode) {
|
|
case DEG: drg2rad=M_PI / 180.0;
|
|
rad2drg=180.0 / M_PI;
|
|
break;
|
|
case RAD: drg2rad=1.0;
|
|
rad2drg=1.0;
|
|
break;
|
|
case GRAD: drg2rad=M_PI / 200.0;
|
|
rad2drg=200.0 / M_PI;
|
|
break;
|
|
}
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
invf(void)
|
|
{
|
|
flagINV = ~flagINV;
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
memf(int keynum)
|
|
{
|
|
memop = keynum;
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
entered = 2;
|
|
clrdisp++;
|
|
lift_enabled = 0;
|
|
}
|
|
|
|
void
|
|
oneop(int keynum)
|
|
{
|
|
int i,j;
|
|
double dtmp;
|
|
|
|
if (entered==1)
|
|
parse_double(&dnum);
|
|
entered = 2;
|
|
|
|
switch (keynum) { /* do the actual math fn. */
|
|
case kE: if (rpn && memop != kENTR) PushNum(dnum); dnum=M_E; break;
|
|
case kPI: if (rpn && memop != kENTR) PushNum(dnum); dnum=M_PI; break;
|
|
case kRECIP: dnum=1.0/dnum; break;
|
|
case kSQR: flagINV = !flagINV; /* fall through */
|
|
case kSQRT: if (flagINV) dnum=dnum*dnum;
|
|
else dnum=sqrt(dnum);
|
|
break;
|
|
case k10X: flagINV = !flagINV; /* fall through */
|
|
case kLOG: if (flagINV) dnum=pow(10.0,dnum);
|
|
else dnum=log10(dnum);
|
|
break;
|
|
case kEXP: flagINV = !flagINV; /* fall through */
|
|
case kLN: if (flagINV) dnum=exp(dnum);
|
|
else dnum=log(dnum);
|
|
break;
|
|
case kSIN: if (flagINV) dnum=asin(dnum)*rad2drg;
|
|
else dnum=sin(dnum*drg2rad);
|
|
break;
|
|
case kCOS: if (flagINV) dnum=acos(dnum)*rad2drg;
|
|
else dnum=cos(dnum*drg2rad);
|
|
break;
|
|
case kTAN: if (flagINV) dnum=atan(dnum)*rad2drg;
|
|
else dnum=tan(dnum*drg2rad);
|
|
break;
|
|
case kSTO: mem[0]=dnum; flagM=!(mem[0]==0.0); break;
|
|
case kRCL: if (rpn && lift_enabled) PushNum(dnum);
|
|
dnum=mem[0]; flagM=!(mem[0]==0.0); break;
|
|
case kSUM: mem[0]+=dnum; flagM=!(mem[0]==0.0); break;
|
|
case kEXC: dtmp=dnum; dnum=mem[0]; mem[0]=dtmp;
|
|
flagM=!(mem[0]==0.0); break;
|
|
case kFACT: if (floor(dnum)!=dnum || dnum<0.0 || dnum>500.0) {
|
|
strlcpy(dispstr, "error", sizeof(dispstr));
|
|
entered=3;
|
|
break;
|
|
}
|
|
dtmp = floor(dnum); i = dtmp;
|
|
for (j=1,dnum=1.0; j<=i; j++)
|
|
dnum*=(float) j;
|
|
break;
|
|
case kNOT: dnum = ~(long)dnum; break;
|
|
case kTRUNC: dnum = trunc(dnum); break;
|
|
}
|
|
|
|
if (entered==3) { /* error */
|
|
DrawDisplay();
|
|
return;
|
|
}
|
|
|
|
memop = keynum;
|
|
entered=2;
|
|
clrdisp=1;
|
|
flagINV=0;
|
|
lift_enabled = 1;
|
|
format_double(dnum);
|
|
DrawDisplay();
|
|
}
|
|
|
|
void
|
|
offf(void)
|
|
{
|
|
/* full reset */
|
|
ResetCalc();
|
|
entered=clrdisp=1;
|
|
lift_enabled = 0;
|
|
dnum=mem[0]=0.0;
|
|
if (rpn)
|
|
for (int i=1; i < XCALC_MEMORY; i++)
|
|
mem[i]=0.0;
|
|
exponent=Dpoint=0;
|
|
DrawDisplay();
|
|
}
|
|
|
|
|
|
#define STACKMAX 32
|
|
static int opstack[STACKMAX];
|
|
static int opsp;
|
|
static double numstack[STACKMAX];
|
|
static int numsp;
|
|
|
|
|
|
/*******/
|
|
static void
|
|
PushOp(int op)
|
|
/*******/
|
|
{
|
|
if (opsp==STACKMAX) {
|
|
strlcpy(dispstr, "stack error", sizeof(dispstr));
|
|
entered=3;
|
|
} else
|
|
opstack[opsp++]=op;
|
|
}
|
|
|
|
/*******/
|
|
static int
|
|
PopOp(void)
|
|
/*******/
|
|
{
|
|
if (opsp==0) {
|
|
strlcpy(dispstr, "stack error", sizeof(dispstr));
|
|
entered=3;
|
|
return(kNOP);
|
|
} else
|
|
return(opstack[--opsp]);
|
|
}
|
|
|
|
/*******/
|
|
static int
|
|
isopempty(void)
|
|
/*******/
|
|
{
|
|
return( opsp ? 0 : 1 );
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void
|
|
showstack(char *string)
|
|
{
|
|
fprintf(stderr, "%s: %lf %lf %lf\n", string, numstack[0], numstack[1],
|
|
numstack[2]);
|
|
}
|
|
#endif
|
|
|
|
/*******/
|
|
static void
|
|
PushNum(double num)
|
|
/*******/
|
|
{
|
|
if (rpn) {
|
|
numstack[2] = numstack[1];
|
|
numstack[1] = numstack[0];
|
|
numstack[0] = num;
|
|
return;
|
|
}
|
|
if (numsp==STACKMAX) {
|
|
strlcpy(dispstr, "stack error", sizeof(dispstr));
|
|
entered=3;
|
|
} else
|
|
numstack[numsp++]=num;
|
|
}
|
|
|
|
/*******/
|
|
static double
|
|
PopNum(void)
|
|
/*******/
|
|
{
|
|
if (rpn) {
|
|
double tmp = numstack[0];
|
|
numstack[0] = numstack[1];
|
|
numstack[1] = numstack[2];
|
|
return(tmp);
|
|
}
|
|
if (numsp==0) {
|
|
strlcpy(dispstr, "stack error", sizeof(dispstr));
|
|
entered=3;
|
|
return 0.0;
|
|
} else
|
|
return(numstack[--numsp]);
|
|
}
|
|
|
|
/*******/
|
|
static void
|
|
RollNum(int dir)
|
|
/*******/
|
|
{
|
|
double tmp;
|
|
|
|
if (dir) { /* roll up */
|
|
tmp = dnum;
|
|
dnum = numstack[2];
|
|
numstack[2] = numstack[1];
|
|
numstack[1] = numstack[0];
|
|
numstack[0] = tmp;
|
|
} else { /* roll down */
|
|
tmp = dnum;
|
|
dnum = numstack[0];
|
|
numstack[0] = numstack[1];
|
|
numstack[1] = numstack[2];
|
|
numstack[2] = tmp;
|
|
}
|
|
}
|
|
|
|
|
|
/*******/
|
|
static void
|
|
ClearStacks(void)
|
|
/*******/
|
|
{
|
|
if (rpn)
|
|
numstack[0] = numstack[1] = numstack[2] = 0.;
|
|
opsp=numsp=0;
|
|
}
|
|
|
|
|
|
/*******/
|
|
static int
|
|
priority(int op)
|
|
/*******/
|
|
{
|
|
switch (op) {
|
|
case kPOW: return(6);
|
|
case kMUL:
|
|
case kDIV:
|
|
case kMOD: return(5);
|
|
case kADD:
|
|
case kSUB: return(4);
|
|
case kSHL:
|
|
case kSHR: return(3);
|
|
case kAND: return(2);
|
|
case kXOR: return(1);
|
|
case kOR: return(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/********/
|
|
void
|
|
ResetCalc(void)
|
|
/********/
|
|
{
|
|
flagM=flagINV=flagPAREN=0; drgmode=DEG;
|
|
numbase=(!numbase ? 10 : numbase);
|
|
setflag(XCalc_MEMORY, False);
|
|
setflag(XCalc_INVERSE, False);
|
|
setflag(XCalc_PAREN, False);
|
|
setflag(XCalc_RADIAN, False);
|
|
setflag(XCalc_GRADAM, False);
|
|
setflag(XCalc_DEGREE, True);
|
|
setflag(XCalc_HEX, False);
|
|
setflag(XCalc_DEC, True);
|
|
setflag(XCalc_OCT, False);
|
|
strlcpy(dispstr, "0", sizeof(dispstr));
|
|
draw(dispstr);
|
|
ClearStacks();
|
|
drg2rad=M_PI/180.0;
|
|
rad2drg=180.0/M_PI;
|
|
}
|