1/*
2 *  math.c  -  mathematics functions for a hand calculator under X
3 *
4 *  Author:    John H. Bradley, University of Pennsylvania
5 *                (bradley@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
38jmp_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
46static int flagINV, flagPAREN, flagM, drgmode, numbase;	/* display flags */
47
48static double drg2rad=M_PI/180.0;  /* Conversion factors for trig funcs */
49static double rad2drg=180.0/M_PI;
50static 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 */
55static int lift_enabled = 0;	/* for rpn mode only */
56
57static int CLR    =0;  /* CLR clears display.  if 1, clears acc, also */
58static int Dpoint=0;  /* to prevent using decimal pt twice in a # */
59static int clrdisp=1;  /* if true clears display before entering # */
60static int lastop =kCLR;
61static int memop  =kCLR;
62static int exponent=0;
63static double acc =0.0;
64static double dnum=0.0;
65#define XCALC_MEMORY 10
66static double mem[XCALC_MEMORY] = { 0.0 };
67
68static void   DrawDisplay(void);
69static void   PushOp(int op);
70static int    PopOp(void);
71static int    isopempty(void);
72#ifdef DEBUG
73static void   showstack(char *string);
74#endif
75static void   PushNum(double num);
76static double PopNum(void);
77static void   RollNum(int dir);
78static void   ClearStacks(void);
79static int    priority(int op);
80
81#ifndef HAVE_STRLCPY
82/* Close enough for the short strings copied in xcalc */
83static inline size_t
84strlcpy(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 */
97static void
98parse_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 */
124static void
125format_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/*********************************/
140int 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 */
162static volatile int SignalKind;
163
164void 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*/
177void 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*/
188void 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
200void 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/*-------------------------------------------------------------------------*/
216static void
217DrawDisplay(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/*-------------------------------------------------------------------------*/
244void
245change_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/*-------------------------------------------------------------------------*/
263void
264numeric(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
357void
358bkspf(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
399void
400decf(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
421void
422eef(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
439void
440clearf(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
455void
456negf(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 */
483void
484twoop(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 */
554void
555twof(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
590void
591entrf(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
608void
609equf(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
663void
664lparf(void)
665{
666  flagINV=0;
667  PushOp(kLPAR);
668  flagPAREN++;
669  DrawDisplay();
670}
671
672void
673rollf(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
688void
689rparf(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
744void
745drgf(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
777void
778invf(void)
779{
780  flagINV = ~flagINV;
781  DrawDisplay();
782}
783
784void
785memf(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
795void
796oneop(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
863void
864offf(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
880static int opstack[STACKMAX];
881static int opsp;
882static double numstack[STACKMAX];
883static int numsp;
884
885
886/*******/
887static void
888PushOp(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/*******/
899static int
900PopOp(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/*******/
912static int
913isopempty(void)
914/*******/
915{
916  return( opsp ? 0 : 1 );
917}
918
919#ifdef DEBUG
920static void
921showstack(char *string)
922{
923    fprintf(stderr, "%s: %lf %lf %lf\n", string, numstack[0], numstack[1],
924	    numstack[2]);
925}
926#endif
927
928/*******/
929static void
930PushNum(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/*******/
947static double
948PopNum(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/*******/
966static void
967RollNum(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/*******/
989static void
990ClearStacks(void)
991/*******/
992{
993    if (rpn)
994      numstack[0] = numstack[1] = numstack[2] = 0.;
995    opsp=numsp=0;
996}
997
998
999/*******/
1000static int
1001priority(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/********/
1022void
1023ResetCalc(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