Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  *  math.c  -  mathematics functions for a hand calculator under X
      3  *
      4  *  Author:    John H. Bradley, University of Pennsylvania
      5  *                (bradley (at) cis.upenn.edu)
      6  *                     March, 1987
      7  *
      8  *  RPN mode added and port to X11 by Mark Rosenstein, MIT Project Athena
      9  *
     10  *  Modified to be a client of the Xt toolkit and the Athena widget set by
     11  *  Donna Converse, MIT X Consortium.  This is all that remains of the
     12  *  original calculator, and it still needs to be rewritten.  The HP
     13  *  functionality should be separated from the TI functionality.
     14  *  Beware the HP functions: there are still errors here.
     15  *
     16  *  Geoffrey Coram fixed most of the HP mode bugs.
     17  */
     18 
     19 #include "xcalc.h"
     20 
     21 #ifndef M_PI        /* sometimes defined in math.h */
     22 #define M_PI        3.14159265358979323846
     23 #endif
     24 
     25 #ifndef M_E         /* sometimes defined in math.h */
     26 #define M_E           2.7182818284590452354
     27 #endif
     28 
     29 #define MAXDISP     11
     30 #define DEG 0		/* DRG mode.  used for trig calculations */
     31 #define RAD 1
     32 #define GRAD 2
     33 
     34 #define True	1
     35 #define False   0
     36 
     37 #ifndef IEEE
     38 jmp_buf env;
     39 #endif
     40 
     41 
     42 /* This section is all of the state machine that implements the calculator
     43  * functions.  Much of it is shared between the infix and rpn modes.
     44  */
     45 
     46 static int flagINV, flagPAREN, flagM, drgmode, numbase;	/* display flags */
     47 
     48 static double drg2rad=M_PI/180.0;  /* Conversion factors for trig funcs */
     49 static double rad2drg=180.0/M_PI;
     50 static int entered=1;  /* true if display contains a valid number.
     51                           if==2, then use 'dnum', rather than the string
     52                           stored in the display.  (for accuracy)
     53                           if==3, then error occurred, only CLR & AC work */
     54 /* entered seems to be overloaded - dmc */
     55 static int lift_enabled = 0;	/* for rpn mode only */
     56 
     57 static int CLR    =0;  /* CLR clears display.  if 1, clears acc, also */
     58 static int Dpoint=0;  /* to prevent using decimal pt twice in a # */
     59 static int clrdisp=1;  /* if true clears display before entering # */
     60 static int lastop =kCLR;
     61 static int memop  =kCLR;
     62 static int exponent=0;
     63 static double acc =0.0;
     64 static double dnum=0.0;
     65 #define XCALC_MEMORY 10
     66 static double mem[XCALC_MEMORY] = { 0.0 };
     67 
     68 static void   DrawDisplay(void);
     69 static void   PushOp(int op);
     70 static int    PopOp(void);
     71 static int    isopempty(void);
     72 #ifdef DEBUG
     73 static void   showstack(char *string);
     74 #endif
     75 static void   PushNum(double num);
     76 static double PopNum(void);
     77 static void   RollNum(int dir);
     78 static void   ClearStacks(void);
     79 static int    priority(int op);
     80 
     81 #ifndef HAVE_STRLCPY
     82 /* Close enough for the short strings copied in xcalc */
     83 static inline size_t
     84 strlcpy(char *dst, const char *src, size_t size)
     85 {
     86     strncpy(dst, src, size);
     87     dst[size - 1] = '\0';
     88     return strlen(src);
     89 }
     90 #endif
     91 
     92 /*
     93  * The following is to deal with the unfortunate assumption that if errno
     94  * is non-zero then an error has occurred.  On some systems (e.g. Ultrix),
     95  * sscanf will call lower level routines that will set errno.
     96  */
     97 static void
     98 parse_double(double *dp)
     99 {
    100     unsigned long n = 0;
    101     int olderrno = errno;
    102 
    103     switch (numbase) {
    104     case 8:
    105         sscanf(dispstr, "%lo", &n);
    106         *dp = (double)n;
    107     break;
    108     case 16:
    109         sscanf(dispstr, "%lX", &n);
    110         *dp = (double)n;
    111     break;
    112     default:
    113         sscanf(dispstr, "%lf", dp);
    114     }
    115 
    116     errno = olderrno;
    117     return;
    118 }
    119 
    120 /**
    121  * Format the given double according to the
    122  * selected number base.
    123  */
    124 static void
    125 format_double(double n)
    126 {
    127     switch (numbase) {
    128     case 8:
    129         snprintf(dispstr, sizeof(dispstr), "%lo", (long)n);
    130     break;
    131     case 16:
    132         snprintf(dispstr, sizeof(dispstr), "%lX", (long)n);
    133     break;
    134     default:
    135         snprintf(dispstr, sizeof(dispstr), "%.8g", n);
    136     }
    137 }
    138 
    139 /*********************************/
    140 int pre_op(int keynum)
    141 {
    142     if (keynum==-1) return(0);
    143 
    144     errno = 0;			/* for non-IEEE machines */
    145 
    146     if ( (entered==3) && !(keynum==kCLR || keynum==kOFF)) {
    147       if (rpn) {
    148 	clrdisp++;
    149       } else {
    150         ringbell();
    151         return(1);	/* the intent was probably not to do the operation */
    152       }
    153     }
    154 
    155     if (keynum != kCLR) CLR=0;
    156     return(0);
    157 }
    158 
    159 #ifndef IEEE
    160 
    161 /* cannot assign result of setjmp under ANSI C, use global instead */
    162 static volatile int SignalKind;
    163 
    164 void fail_op(void)
    165 {
    166     if (SignalKind == SIGFPE)
    167 	strlcpy(dispstr, "math error", sizeof(dispstr));
    168     else if (SignalKind == SIGILL)
    169 	strlcpy(dispstr, "illegal operand", sizeof(dispstr));
    170 
    171     entered=3;
    172     DrawDisplay();
    173     return;
    174 }
    175 
    176 /*ARGSUSED*/
    177 void fperr(int sig)
    178 {
    179 #if defined(SYSV) || defined(SVR4) || defined(linux)
    180     signal(SIGFPE, fperr);
    181 #endif
    182     SignalKind = sig;
    183     longjmp(env,1);
    184 }
    185 
    186 /* for VAX BSD4.3 */
    187 /*ARGSUSED*/
    188 void illerr(int sig)
    189 {
    190     /* not reset when caught? */
    191     signal(SIGILL, illerr);
    192 
    193     SignalKind = sig;
    194     longjmp(env,1);
    195 }
    196 
    197 #endif	/* not IEEE */
    198 
    199 
    200 void post_op(void)
    201 {
    202 #ifdef DEBUG
    203     showstack("\0");
    204 #endif
    205 #ifndef IEEE
    206     if (errno) {
    207         strlcpy(dispstr, "error", sizeof(dispstr));
    208         DrawDisplay();
    209         entered=3;
    210         errno=0;
    211         }
    212 #endif
    213 }
    214 
    215 /*-------------------------------------------------------------------------*/
    216 static void
    217 DrawDisplay(void)
    218 {
    219     if (strlen(dispstr) >= MAXDISP) { /* strip out some decimal digits */
    220         char *estr = index(dispstr,'e');  /* search for exponent part */
    221         if (!estr) dispstr[12]='\0';      /* no exp, just trunc. */
    222         else {
    223             char tmp[32];
    224             if (strlen(estr) <= 4)        /* leftmost 8 chars plus exponent */
    225                 snprintf(tmp, sizeof(tmp), "%.8s%s", dispstr, estr);
    226             else                          /* leftmost 7 chars plus exponent */
    227                 snprintf(tmp, sizeof(tmp), "%.7s%s", dispstr, estr);
    228             strlcpy(dispstr, tmp, sizeof(dispstr));
    229         }
    230     }
    231     draw(dispstr);
    232     setflag(XCalc_MEMORY, (flagM));
    233     setflag(XCalc_INVERSE, (flagINV));
    234     setflag(XCalc_DEGREE, (drgmode==DEG));
    235     setflag(XCalc_RADIAN, (drgmode==RAD));
    236     setflag(XCalc_GRADAM, (drgmode==GRAD));
    237     setflag(XCalc_PAREN, (flagPAREN));
    238     setflag(XCalc_HEX, (numbase==16));
    239     setflag(XCalc_DEC, (numbase==10));
    240     setflag(XCalc_OCT, (numbase==8));
    241 }
    242 
    243 /*-------------------------------------------------------------------------*/
    244 void
    245 change_base(void)
    246 {
    247 	parse_double(&dnum);
    248 
    249     if (dnum >= 0) {
    250         switch (numbase) {
    251         case 8:  numbase = 10;  break;
    252         case 10: numbase = 16;  break;
    253         case 16: numbase = 8;   break;
    254         }
    255 
    256         format_double(dnum);
    257     } else strlcpy(dispstr, "error", sizeof(dispstr));
    258 
    259     DrawDisplay();
    260 }
    261 
    262 /*-------------------------------------------------------------------------*/
    263 void
    264 numeric(int keynum)
    265 {
    266   char st[2];
    267 
    268   flagINV=0;
    269 
    270   if (rpn && (memop == kSTO || memop == kRCL || memop == kSUM)) {
    271       int cell = 0;
    272 
    273       switch (keynum) {
    274 	case kONE:	cell = 1; break;
    275 	case kTWO:	cell = 2; break;
    276 	case kTHREE:	cell = 3; break;
    277 	case kFOUR:	cell = 4; break;
    278 	case kFIVE:	cell = 5; break;
    279 	case kSIX:	cell = 6; break;
    280 	case kSEVEN:	cell = 7; break;
    281 	case kEIGHT:	cell = 8; break;
    282 	case kNINE:	cell = 9; break;
    283 	case kZERO:	cell = 0; break;
    284       }
    285       switch (memop) {
    286       case kSTO:
    287 	mem[cell] = dnum;
    288 	lift_enabled = 1;
    289 	entered = 2;
    290 	clrdisp++;
    291 	break;
    292       case kRCL:
    293 	PushNum(dnum);
    294 	dnum = mem[cell];
    295 	format_double(dnum);
    296 	lift_enabled = 1;
    297         entered = 1;
    298 	clrdisp++;
    299 	break;
    300       case kSUM:
    301 	mem[cell] += dnum;
    302 	lift_enabled = 1;
    303 	entered = 2;
    304 	clrdisp++;
    305 	break;
    306       }
    307       memop = kCLR;
    308       DrawDisplay();
    309       return;
    310   }
    311 
    312   if (clrdisp) {
    313     dispstr[0]='\0';
    314     exponent=Dpoint=0;
    315 /*    if (rpn && entered==2)
    316       PushNum(dnum);
    317  */
    318     if (rpn & lift_enabled)
    319 	PushNum(dnum);
    320   }
    321   if ((int) strlen(dispstr) >= MAXDISP)
    322     return;
    323 
    324   st[0] = '\0';
    325   switch (keynum){
    326       case kZERO:	st[0] = '0'; break;
    327       case kONE:	st[0] = '1'; break;
    328       case kTWO:	st[0] = '2'; break;
    329       case kTHREE:	st[0] = '3'; break;
    330       case kFOUR:	st[0] = '4'; break;
    331       case kFIVE:	st[0] = '5'; break;
    332       case kSIX:	st[0] = '6'; break;
    333       case kSEVEN:	st[0] = '7'; break;
    334       case kEIGHT:	if (numbase > 8)  st[0] = '8'; break;
    335       case kNINE:	if (numbase > 8)  st[0] = '9'; break;
    336       case kxA: 	if (numbase > 10) st[0] = 'A'; break;
    337       case kxB: 	if (numbase > 10) st[0] = 'B'; break;
    338       case kxC: 	if (numbase > 10) st[0] = 'C'; break;
    339       case kxD: 	if (numbase > 10) st[0] = 'D'; break;
    340       case kxE: 	if (numbase > 10) st[0] = 'E'; break;
    341       case kxF: 	if (numbase > 10) st[0] = 'F'; break;
    342     }
    343 
    344     if (st[0] == '\0')
    345         return;
    346     st[1] = '\0';
    347     strcat(dispstr,st);
    348 
    349   DrawDisplay();
    350   if (clrdisp && keynum != kZERO)
    351     clrdisp=0; /*no leading 0s*/
    352   memop = keynum;
    353   entered=1;
    354   lift_enabled = 0;
    355 }
    356 
    357 void
    358 bkspf(void)
    359 {
    360 
    361   lift_enabled = 0;
    362 
    363   if (! flagINV)
    364   {
    365       if (entered!=1) {
    366 	  clearf();
    367 	  return;
    368       }
    369       if (clrdisp)
    370 	  return;
    371       if ((int) strlen(dispstr) > 0) {
    372 #ifndef X_LOCALE
    373           const char *dp = localeconv()->decimal_point;
    374           size_t dp_len = strlen(dp);
    375           size_t ds_len = strlen(dispstr);
    376           if (ds_len >= dp_len && strcmp(dispstr + ds_len - dp_len, dp) == 0)
    377              Dpoint=0;
    378 #else
    379 	  if (dispstr[strlen(dispstr)-1] == '.')
    380              Dpoint=0;
    381 #endif
    382 	  dispstr[strlen(dispstr)-1] = 0;
    383       }
    384       if (strlen(dispstr) == 0) {
    385 	  strcat(dispstr, "0");
    386 	  clrdisp++;
    387       }
    388   }
    389   else
    390   {
    391       strlcpy(dispstr, "0", sizeof(dispstr));
    392       dnum = 0.0;
    393       clrdisp++;
    394       flagINV = 0;
    395   }
    396   DrawDisplay();
    397 }
    398 
    399 void
    400 decf(void)
    401 {
    402   flagINV=0;
    403   if (clrdisp) {
    404       if (rpn && lift_enabled)
    405 	PushNum(dnum);
    406       strlcpy(dispstr, "0", sizeof(dispstr));
    407   }
    408   if (!Dpoint) {
    409 #ifndef X_LOCALE
    410     strcat(dispstr, localeconv()->decimal_point);
    411 #else
    412     strcat(dispstr, ".");
    413 #endif
    414     DrawDisplay();
    415     Dpoint++;
    416   }
    417   clrdisp=0;
    418   entered=1;
    419 }
    420 
    421 void
    422 eef(void)
    423 {
    424   flagINV=0;
    425   if (clrdisp) {
    426       if (rpn && lift_enabled)
    427 	PushNum(dnum);
    428       strlcpy(dispstr, rpn ? "1" : "0", sizeof(dispstr));
    429   }
    430   if (!exponent) {
    431     strcat(dispstr,"E+");
    432     DrawDisplay();
    433     exponent=strlen(dispstr)-1;  /* where the '-' goes */
    434   }
    435   clrdisp=0;
    436   entered=1;
    437 }
    438 
    439 void
    440 clearf(void)
    441 {
    442   flagINV=0;
    443   if (CLR && !rpn) { /* clear all */
    444     ClearStacks();
    445     flagPAREN=0;
    446   }
    447   CLR++;
    448   exponent=Dpoint=0;
    449   clrdisp=1;
    450   entered=1;
    451   strlcpy(dispstr, "0", sizeof(dispstr));
    452   DrawDisplay();
    453 }
    454 
    455 void
    456 negf(void)
    457 {
    458   flagINV=0;
    459   if (exponent) {       /* neg the exponent */
    460     if (dispstr[exponent]=='-')
    461       dispstr[exponent]='+';
    462     else
    463       dispstr[exponent]='-';
    464     DrawDisplay();
    465     return;
    466   }
    467 
    468   if (strcmp("0",dispstr)==0)
    469     return;			/* don't neg a zero */
    470   if (dispstr[0]=='-')	 	/* already neg-ed */
    471     strcpy(dispstr,dispstr+1);  /* move str left once */
    472   else {			/* not neg-ed.  add a '-' */
    473     char tmp[32];
    474     snprintf(tmp, sizeof(tmp), "-%s", dispstr);
    475     strlcpy(dispstr, tmp, sizeof(dispstr));
    476   }
    477   if (entered==2)
    478     dnum = -1.0 * dnum;
    479   DrawDisplay();
    480 }
    481 
    482 /* Two operand functions for infix calc */
    483 void
    484 twoop(int keynum)
    485 {
    486   if (flagINV) {
    487     flagINV=0;
    488     DrawDisplay();
    489   }
    490 
    491   if (!entered) {		/* something like "5+*" */
    492     if (!isopempty())
    493       PopOp();			/* replace the prev op */
    494     PushOp(keynum);		/* with the new one */
    495     return;
    496   }
    497 
    498   if (entered==1)
    499     parse_double(&dnum);
    500 
    501   clrdisp=CLR=1;
    502   entered=Dpoint=exponent=0;
    503 
    504   if (!isopempty()) {  /* there was a previous op */
    505     lastop=PopOp();   /* get it */
    506 
    507     if (lastop==kLPAR) {  /* put it back */
    508       PushOp(kLPAR);
    509       PushOp(keynum);
    510       PushNum(dnum);
    511       return;
    512     }
    513 
    514     /* now, if the current op (keynum) is of
    515        higher priority than the lastop, the current
    516        op and number are just pushed on top
    517        Priorities:  (Y^X) > *,/ > +,- > >>,<< > & > ^ > ~ */
    518 
    519     if (priority(keynum) > priority(lastop)) {
    520       PushNum(dnum);
    521       PushOp(lastop);
    522       PushOp(keynum);
    523     } else {  /* execute lastop on lastnum and dnum, push
    524 	       result and current op on stack */
    525       acc=PopNum();
    526       switch (lastop) { /* perform the operation */
    527       case kADD: acc += dnum;  break;
    528       case kSUB: acc -= dnum;  break;
    529       case kMUL: acc *= dnum;  break;
    530       case kDIV: acc /= dnum;  break;
    531       case kPOW: acc =  pow(acc,dnum);  break;
    532       case kMOD: acc = (long)acc %  (long)dnum;  break;
    533       case kAND: acc = (long)acc &  (long)dnum;  break;
    534       case kOR:  acc = (long)acc |  (long)dnum;  break;
    535       case kXOR: acc = (long)acc ^  (long)dnum;  break;
    536       case kSHL: acc = (long)acc << (long)dnum;  break;
    537       case kSHR: acc = (long)acc >> (long)dnum;  break;
    538       }
    539 
    540       PushNum(acc);
    541       PushOp(keynum);
    542       format_double(acc);
    543       DrawDisplay();
    544       dnum=acc;
    545     }
    546   }
    547   else { /* op stack is empty, push op and num */
    548     PushOp(keynum);
    549     PushNum(dnum);
    550   }
    551 }
    552 
    553 /* Two operand functions for rpn calc */
    554 void
    555 twof(int keynum)
    556 {
    557   if (flagINV) {
    558     flagINV=0;
    559     DrawDisplay();
    560   }
    561   if (!entered)
    562     return;
    563   if (entered==1)
    564     parse_double(&dnum);
    565   acc = PopNum();
    566   switch(keynum) {
    567   case kADD: acc += dnum;  break;
    568   case kSUB: acc -= dnum;  break;
    569   case kMUL: acc *= dnum;  break;
    570   case kDIV: acc /= dnum;  break;
    571   case kPOW: acc =  pow(acc,dnum);  break;
    572   case kXXY: PushNum(dnum);  break;
    573   case kMOD: acc = (long)acc %  (long)dnum;  break;
    574   case kAND: acc = (long)acc &  (long)dnum;  break;
    575   case kOR:  acc = (long)acc |  (long)dnum;  break;
    576   case kXOR: acc = (long)acc ^  (long)dnum;  break;
    577   case kSHL: acc = (long)acc << (long)dnum;  break;
    578   case kSHR: acc = (long)acc >> (long)dnum;  break;
    579   }
    580 
    581   format_double(acc);
    582   DrawDisplay();
    583   clrdisp++;
    584   Dpoint = exponent = 0;
    585   entered = 2;
    586   lift_enabled = 1;
    587   dnum = acc;
    588 }
    589 
    590 void
    591 entrf(void)
    592 {
    593   flagINV=0;
    594   if (!entered)
    595     return;
    596 
    597   clrdisp=CLR=1;
    598   Dpoint=exponent=0;
    599 
    600   if (entered==1)
    601     parse_double(&dnum);
    602   entered=2;
    603   memop = kENTR;
    604   PushNum(dnum);
    605   lift_enabled = 0;
    606 }
    607 
    608 void
    609 equf(void)
    610 {
    611   flagINV=0;
    612   if (!entered)
    613     return;
    614 
    615   clrdisp=CLR=1;
    616   Dpoint=exponent=0;
    617 
    618   if (entered==1)
    619     parse_double(&dnum);
    620   entered=2;
    621 
    622   PushNum(dnum);
    623 
    624   while (!isopempty()) {  /* do all pending ops */
    625     dnum=PopNum();
    626     acc=PopNum();
    627     lastop=PopOp();
    628     switch (lastop) {
    629     case kADD:  acc += dnum;
    630 		break;
    631     case kSUB:  acc -= dnum;
    632 		break;
    633     case kMUL:  acc *= dnum;
    634 		break;
    635     case kDIV:  acc /= dnum;
    636 		break;
    637     case kPOW:  acc = pow(acc,dnum);
    638 		break;
    639     case kLPAR:	flagPAREN--;
    640 		PushNum(acc);
    641 		break;
    642     case kMOD:  acc = (long)acc % (long)dnum;
    643 		break;
    644     case kAND:  acc = (long)acc & (long)dnum;
    645 		break;
    646     case kOR:   acc = (long)acc | (long)dnum;
    647 		break;
    648     case kXOR:  acc = (long)acc ^ (long)dnum;
    649 		break;
    650     case kSHL:  acc = (long)acc << (long)dnum;
    651 		break;
    652     case kSHR:  acc = (long)acc >> (long)dnum;
    653 		break;
    654     }
    655     dnum=acc;
    656     PushNum(dnum);
    657   }
    658 
    659   format_double(dnum);
    660   DrawDisplay();
    661 }
    662 
    663 void
    664 lparf(void)
    665 {
    666   flagINV=0;
    667   PushOp(kLPAR);
    668   flagPAREN++;
    669   DrawDisplay();
    670 }
    671 
    672 void
    673 rollf(void)
    674 {
    675   if (!entered)
    676     return;
    677   if (entered==1)
    678     parse_double(&dnum);
    679   entered = 2;
    680   lift_enabled = 1;
    681   RollNum(flagINV);
    682   flagINV=0;
    683   clrdisp++;
    684   format_double(dnum);
    685   DrawDisplay();
    686 }
    687 
    688 void
    689 rparf(void)
    690 {
    691   flagINV=0;
    692   if (!entered)
    693     return;
    694 
    695   if (!flagPAREN)
    696     return;
    697 
    698   clrdisp++;
    699   Dpoint=exponent=0;
    700 
    701   if (entered==1)
    702     parse_double(&dnum);
    703   entered=2;
    704 
    705   PushNum(dnum);
    706   while (!isopempty() && (lastop=PopOp())!=kLPAR) {
    707     /* do all pending ops, back to left paren */
    708     dnum=PopNum();
    709     acc=PopNum();
    710     switch (lastop) {
    711     case kADD:  acc += dnum;
    712 		break;
    713     case kSUB:  acc -= dnum;
    714 		break;
    715     case kMUL:  acc *= dnum;
    716 		break;
    717     case kDIV:  acc /= dnum;
    718 		break;
    719     case kPOW:  acc = pow(acc,dnum);
    720 		break;
    721     case kMOD:  acc = (long)acc % (long)dnum;
    722 		break;
    723     case kAND:  acc = (long)acc & (long)dnum;
    724 		break;
    725     case kOR:   acc = (long)acc | (long)dnum;
    726 		break;
    727     case kXOR:  acc = (long)acc ^ (long)dnum;
    728 		break;
    729     case kSHL:  acc = (long)acc << (long)dnum;
    730 		break;
    731     case kSHR:  acc = (long)acc >> (long)dnum;
    732 		break;
    733     }
    734     dnum=acc;
    735     PushNum(dnum);
    736   }
    737   PopNum();
    738   flagPAREN--;
    739   entered=2;
    740   format_double(dnum);
    741   DrawDisplay();
    742 }
    743 
    744 void
    745 drgf(void)
    746 {
    747   if (flagINV) {
    748     if (entered==1)
    749       parse_double(&dnum);
    750     switch (drgmode) {
    751     case DEG:  dnum=dnum*M_PI/180.0;    break;
    752     case RAD:  dnum=dnum*200.0/M_PI;    break;
    753     case GRAD: dnum=dnum*90.0/100.0;  break;
    754     }
    755     entered=2;
    756     clrdisp=1;
    757     flagINV=0;
    758     format_double(dnum);
    759   }
    760 
    761   flagINV=0;
    762   drgmode = (drgmode + 1) % 3;
    763   switch (drgmode) {
    764   case DEG:  drg2rad=M_PI / 180.0;
    765 	     rad2drg=180.0 / M_PI;
    766 	     break;
    767   case RAD:  drg2rad=1.0;
    768 	     rad2drg=1.0;
    769 	     break;
    770   case GRAD: drg2rad=M_PI / 200.0;
    771 	     rad2drg=200.0 / M_PI;
    772 	     break;
    773   }
    774   DrawDisplay();
    775 }
    776 
    777 void
    778 invf(void)
    779 {
    780   flagINV = ~flagINV;
    781   DrawDisplay();
    782 }
    783 
    784 void
    785 memf(int keynum)
    786 {
    787     memop = keynum;
    788     if (entered==1)
    789       parse_double(&dnum);
    790     entered = 2;
    791     clrdisp++;
    792     lift_enabled = 0;
    793 }
    794 
    795 void
    796 oneop(int keynum)
    797 {
    798   int i,j;
    799   double dtmp;
    800 
    801   if (entered==1)
    802     parse_double(&dnum);
    803   entered = 2;
    804 
    805   switch (keynum) {  /* do the actual math fn. */
    806   case kE:     if (rpn && memop != kENTR) PushNum(dnum); dnum=M_E;  break;
    807   case kPI:    if (rpn && memop != kENTR) PushNum(dnum); dnum=M_PI;  break;
    808   case kRECIP: dnum=1.0/dnum;  break;
    809   case kSQR:   flagINV = !flagINV; /* fall through */
    810   case kSQRT:  if (flagINV) dnum=dnum*dnum;
    811 	       else dnum=sqrt(dnum);
    812 	       break;
    813   case k10X:   flagINV = !flagINV; /* fall through */
    814   case kLOG:   if (flagINV) dnum=pow(10.0,dnum);
    815   	       else dnum=log10(dnum);
    816 	       break;
    817   case kEXP:   flagINV = !flagINV; /* fall through */
    818   case kLN:    if (flagINV) dnum=exp(dnum);
    819 	       else dnum=log(dnum);
    820 	       break;
    821   case kSIN:   if (flagINV) dnum=asin(dnum)*rad2drg;
    822 	       else dnum=sin(dnum*drg2rad);
    823 	       break;
    824   case kCOS:   if (flagINV) dnum=acos(dnum)*rad2drg;
    825 	       else dnum=cos(dnum*drg2rad);
    826 	       break;
    827   case kTAN:   if (flagINV) dnum=atan(dnum)*rad2drg;
    828 	       else dnum=tan(dnum*drg2rad);
    829 	       break;
    830   case kSTO:   mem[0]=dnum;  flagM=!(mem[0]==0.0);  break;
    831   case kRCL:   if (rpn && lift_enabled) PushNum(dnum);
    832                dnum=mem[0];  flagM=!(mem[0]==0.0);  break;
    833   case kSUM:   mem[0]+=dnum; flagM=!(mem[0]==0.0);  break;
    834   case kEXC:   dtmp=dnum; dnum=mem[0];  mem[0]=dtmp;
    835 	       flagM=!(mem[0]==0.0);  break;
    836   case kFACT:  if (floor(dnum)!=dnum || dnum<0.0 || dnum>500.0) {
    837                  strlcpy(dispstr, "error", sizeof(dispstr));
    838 		 entered=3;
    839 		 break;
    840 	       }
    841 	       dtmp = floor(dnum); i = dtmp;
    842 	       for (j=1,dnum=1.0; j<=i; j++)
    843 		 dnum*=(float) j;
    844 	       break;
    845   case kNOT:   dnum = ~(long)dnum;  break;
    846   case kTRUNC: dnum = trunc(dnum);  break;
    847   }
    848 
    849   if (entered==3) {  /* error */
    850     DrawDisplay();
    851     return;
    852   }
    853 
    854   memop = keynum;
    855   entered=2;
    856   clrdisp=1;
    857   flagINV=0;
    858   lift_enabled = 1;
    859   format_double(dnum);
    860   DrawDisplay();
    861 }
    862 
    863 void
    864 offf(void)
    865 {
    866   /* full reset */
    867   ResetCalc();
    868   entered=clrdisp=1;
    869   lift_enabled = 0;
    870   dnum=mem[0]=0.0;
    871   if (rpn)
    872       for (int i=1; i < XCALC_MEMORY; i++)
    873 	  mem[i]=0.0;
    874   exponent=Dpoint=0;
    875   DrawDisplay();
    876 }
    877 
    878 
    879 #define STACKMAX 32
    880 static int opstack[STACKMAX];
    881 static int opsp;
    882 static double numstack[STACKMAX];
    883 static int numsp;
    884 
    885 
    886 /*******/
    887 static void
    888 PushOp(int op)
    889 /*******/
    890 {
    891   if (opsp==STACKMAX) {
    892       strlcpy(dispstr, "stack error", sizeof(dispstr));
    893       entered=3;
    894   } else
    895       opstack[opsp++]=op;
    896 }
    897 
    898 /*******/
    899 static int
    900 PopOp(void)
    901 /*******/
    902 {
    903   if (opsp==0) {
    904       strlcpy(dispstr, "stack error", sizeof(dispstr));
    905       entered=3;
    906       return(kNOP);
    907   } else
    908     return(opstack[--opsp]);
    909 }
    910 
    911 /*******/
    912 static int
    913 isopempty(void)
    914 /*******/
    915 {
    916   return( opsp ? 0 : 1 );
    917 }
    918 
    919 #ifdef DEBUG
    920 static void
    921 showstack(char *string)
    922 {
    923     fprintf(stderr, "%s: %lf %lf %lf\n", string, numstack[0], numstack[1],
    924 	    numstack[2]);
    925 }
    926 #endif
    927 
    928 /*******/
    929 static void
    930 PushNum(double num)
    931 /*******/
    932 {
    933   if (rpn) {
    934       numstack[2] = numstack[1];
    935       numstack[1] = numstack[0];
    936       numstack[0] = num;
    937       return;
    938   }
    939   if (numsp==STACKMAX) {
    940       strlcpy(dispstr, "stack error", sizeof(dispstr));
    941       entered=3;
    942   } else
    943     numstack[numsp++]=num;
    944 }
    945 
    946 /*******/
    947 static double
    948 PopNum(void)
    949 /*******/
    950 {
    951     if (rpn) {
    952 	double tmp = numstack[0];
    953 	numstack[0] = numstack[1];
    954 	numstack[1] = numstack[2];
    955 	return(tmp);
    956     }
    957     if (numsp==0) {
    958 	strlcpy(dispstr, "stack error", sizeof(dispstr));
    959 	entered=3;
    960 	return 0.0;
    961     } else
    962       return(numstack[--numsp]);
    963 }
    964 
    965 /*******/
    966 static void
    967 RollNum(int dir)
    968 /*******/
    969 {
    970     double tmp;
    971 
    972     if (dir) {				/* roll up */
    973 	tmp         = dnum;
    974 	dnum        = numstack[2];
    975 	numstack[2] = numstack[1];
    976 	numstack[1] = numstack[0];
    977 	numstack[0] = tmp;
    978     } else {				/* roll down */
    979 	tmp         = dnum;
    980 	dnum        = numstack[0];
    981 	numstack[0] = numstack[1];
    982 	numstack[1] = numstack[2];
    983 	numstack[2] = tmp;
    984     }
    985 }
    986 
    987 
    988 /*******/
    989 static void
    990 ClearStacks(void)
    991 /*******/
    992 {
    993     if (rpn)
    994       numstack[0] = numstack[1] = numstack[2] = 0.;
    995     opsp=numsp=0;
    996 }
    997 
    998 
    999 /*******/
   1000 static int
   1001 priority(int op)
   1002 /*******/
   1003 {
   1004     switch (op) {
   1005         case kPOW: return(6);
   1006         case kMUL:
   1007         case kDIV:
   1008         case kMOD: return(5);
   1009         case kADD:
   1010         case kSUB: return(4);
   1011         case kSHL:
   1012         case kSHR: return(3);
   1013         case kAND: return(2);
   1014         case kXOR: return(1);
   1015         case kOR:  return(0);
   1016     }
   1017     return 0;
   1018 }
   1019 
   1020 
   1021 /********/
   1022 void
   1023 ResetCalc(void)
   1024 /********/
   1025 {
   1026     flagM=flagINV=flagPAREN=0;  drgmode=DEG;
   1027     numbase=(!numbase ? 10 : numbase);
   1028     setflag(XCalc_MEMORY, False);
   1029     setflag(XCalc_INVERSE, False);
   1030     setflag(XCalc_PAREN, False);
   1031     setflag(XCalc_RADIAN, False);
   1032     setflag(XCalc_GRADAM, False);
   1033     setflag(XCalc_DEGREE, True);
   1034     setflag(XCalc_HEX, False);
   1035     setflag(XCalc_DEC, True);
   1036     setflag(XCalc_OCT, False);
   1037     strlcpy(dispstr, "0", sizeof(dispstr));
   1038     draw(dispstr);
   1039     ClearStacks();
   1040     drg2rad=M_PI/180.0;
   1041     rad2drg=180.0/M_PI;
   1042 }
   1043