/* Platform independent parts of DCALC - Reverse Polish Notation (RPN)
* calculator.
Contact info:
bhepple@freeshell.org
http://bhepple.freeshell.org/dcalc/unix
$Id: dcalc.c,v 1.13 2006/12/30 22:46:07 bhepple Exp $
Copyright (C) 1999-2006 Bob Hepple
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h> /* for pipe() */
#include <sys/types.h> /* for wait */
#include <sys/wait.h> /* for wait */
#include <time.h> /* for localtime */
#include <float.h> /* for DBL_MAX */
extern int errno;
#ifdef HAVE_CONFIG_H /* gtk & wx */
# include "config.h"
#endif
#include "dcalc.h"
#ifdef HAS_ALGEBRAIC_MODE
/* for yacc parser: */
char *parserPointer;
extern void yyparse();
long xSave, ySave, zSave, tSave, uSave, lSave; /* Shadow of Integer stack */
double xfSave,yfSave,zfSave,tfSave,ufSave,lfSave; /* Shadow of floating point stack */
char last_eval[200];
int algebraic_mode = 0;
#endif
long xiReg, yiReg, ziReg, tiReg, liReg; /* Integer stack */
long reg[NUMREGS]; /* Integer registers */
double xfReg,yfReg,zfReg,tfReg,lfReg; /* Floating point stack */
double regf[NUMREGS], /* Floating point registers */
pi, /* The usual pi. */
d_to_r, /* Degrees to radians factor */
r_to_d; /* Radians to degrees factor */
/* */
BOOLEAN entering, /* True if we are still entering into X */
lift_needed, /* True if the stack needs lifting before */
/* more characters are put into X */
invert, /* Invert flag - causes the next operation */
/* to be inversed eg arcsin() instead of sin */
degree, /* True if numbers are in degrees; else */
/* radians */
was_initialised = 0;
/* To make sure program is only initialised once */
BOOLEAN
last_was_fin_key;/* True if last operation was financial */
BOOLEAN finPayAt0 = 0;
char inbuf[45], /* Where typed characters are put */
*inptr, /* Pointer into inbuf */
outbuf[45], /* General buffer used to format output */
zero_string[] = "0",
div_by_zero[] = "Division by 0!";
int decplaces, /* number of decimal places */
max_digits; /* Maximum number of digits displayable */
int mode, intmode, floatmode;
typedef unsigned char byte;
#ifdef EBOOKMAN
#define DIALOG_LETTERS
#define DIALOG_NUMBERS
#else
#define DIALOG_LETTERS "(Y or N)"
#define DIALOG_NUMBERS "[0-9]"
#endif
/* function prototypes: */
static void echo_char(byte c);
static void save_x(void );
#if 0
static long round(double f);
#endif
static void process_digit(COMMAND c);
static void factors(long orig);
int
isInverted() {
int retval = invert;
invert = FALSE;
print_inv();
clear_msg();
return retval;
}
static void fmt_ip(char *str, int x) {
int negative, i1,i2,i3,i4;
if (x < 0) {
x = x & 0x7fffffff;
negative = 1;
} else {
negative = 0;
}
i4 = x & 0xff;
x = x >> 8;
i3 = x & 0xff;
x = x >> 8;
i2 = x & 0xff;
x = x >> 8;
if (negative)
x = x | 0x80;
i1 = x & 0xff;
sprintf(str, "%03d.%03d.%03d.%03d", i1, i2, i3, i4);
}
void fmt_bin(char *str, long x) {
char inverted[50],
reverted[50],
*s, *t;
BOOLEAN negative;
if (x < 0) {
negative = TRUE;
x &= 0x7FFFFFFF;
} else
negative = FALSE;
s = inverted;
while (x != 0) {
*(s++) = '0' + (char) (x % 2);
x /= 2;
}
*s = ASCIIZ;
if (negative) {
while (strlen(inverted) < 31) {
*(s++) = '0';
*s = ASCIIZ;
}
*(s++) = '1';
}
if (s == inverted) /* x==0 */
strcpy(str, zero_string);
else {
t = reverted;
while (s != inverted)
*(t++) = *(--s);
*t = ASCIIZ;
sprintf(str, "%32.32s", reverted);
}
}
void fmt_base(char *s, long x, double xf) {
byte format[20];
byte *p;
union frig {
long j;
byte c[4];
} frigger;
int i, leading_zero;
if (mode == PROG) {
switch (intmode) {
case ASCIIM:
frigger.j = x;
for (i = 0; i < 4; i++) {
#if REVERSE_BYTES
format[3 - i] = frigger.c[i];
#else
format[i] = frigger.c[i];
#endif
}
format[4] = 0;
#if PCSPECIFIC
p = format;
for (i = 0; i < 4; i++, p++)
if (*p == 0)
*s++ = ' ';
else
*s++ = *p;
*s = 0;
#else
/* Convert to canonical form */
p = format;
leading_zero = 1;
for (i = 0; i < 4; i++, p++) {
if ((i < 3) && (*p == 0) && leading_zero)
continue;
leading_zero = 0;
if (*p & 0x80) {
strcpy(s, "a-");
s += 2;
*p &= 0x7f;
}
switch (*p) {
case 0:
strcpy(s, "^spc");
s += 4;
break;
case 7:
strcpy(s, "bell");
s += 4;
break;
case 8:
strcpy(s, "bs");
s += 2;
break;
case 9:
strcpy(s, "tab");
s += 3;
break;
case 10:
strcpy(s, "lf");
s += 2;
break;
case 13:
strcpy(s, "cr");
s += 2;
break;
case 27:
strcpy(s, "esc");
s += 3;
break;
case 32:
strcpy(s, "spc");
s += 3;
break;
case 0x7F:
strcpy(s, "del");
s += 3;
break;
default:
if (*p < 32) {
*s++ = '^';
*s++ = *p+0x60;
} else
*s++ = *p;
}
*s++ = ' ';
}
*s = 0;
#endif
break;
case BIN:
fmt_bin(s, x);
break;
case IP:
fmt_ip(s, x);
break;
case OCT:
if (x == 0L)
strcpy(s, zero_string);
else
sprintf(s, "%32.1lo", x);
break;
case DEC:
if (x == 0L)
strcpy(s, zero_string);
else
sprintf(s, "%32.1ld", x);
break;
case HEX:
if (x == 0L)
strcpy(s, zero_string);
else
sprintf(s, "%32.1lx", x);
break;
}
} else {
if (xf == 0.0) {
strcpy(s, "0.0");
return;
}
if (floatmode == SCI) {
sprintf(format, "%%32.%dE", decplaces);
sprintf(s, format, xf);
} else if (floatmode == ENG) {
double mant;
int isNeg = 0, exp;
char *e, outb[80];
if (xf < 0.0)
isNeg = 1;
mant = fabs(xf);
exp = log10(mant);
exp = exp / 3;
exp = exp * 3;
mant = mant * pow(0.1, (double) exp);
while (mant < 0.1) {
mant = mant * 1000.0;
exp = exp - 3;
}
if (isNeg)
mant = -mant;
sprintf(outb, "%32.2E", pow(10.0, exp));
e = strchr(outb, 'E');
if (e) {
sprintf(format, "%%32.%df%%s", decplaces);
sprintf(s, format, mant, e);
} else {
sprintf(format, "%%32.%dg", decplaces);
sprintf(s, format, xf);
}
} else { /* FIX */
double lowerLimit = pow(10.0, -decplaces);
#ifdef EBOOKMAN
double upperlimit = 1.0e15 / pow(10.0, decplaces);
#else
double upperlimit = 1.0e25 / pow(10.0, decplaces);
#endif
if ((fabs(xf) > upperlimit) || (fabs(xf) < lowerLimit)) {
sprintf(format, "%%32.%dE", decplaces);
} else {
sprintf(format, "%%32.%df", decplaces);
}
sprintf(s, format, xf);
}
}
}
void prep_for_output(char *string) {
/* Put suitable separators in string */
char *s, *d, sep = 0, *ft, rhs[45], lhs[45], scratch[45], prefix[3];
int len, interval = 0, count, negative;
prefix[0] = 0;
if (mode == PROG) {
switch (intmode) {
case ASCIIM:
return; /* Nasty to return here - its easy to miss it when reading! */
case IP:
return; /* all done by fmt_ip */
case BIN:
sep = SPACE;
interval = 8;
break;
case OCT:
sep = SPACE;
interval = 3;
strcpy(prefix, "0");
break;
case DEC:
sep = COMMA;
interval = 3;
break;
case HEX:
sep = SPACE;
interval = 2;
strcpy(prefix, "0x");
break;
}
} else { /* FIX or ENG */
sep = COMMA;
interval = 3;
}
/* Find first non-blank ... */
s = string;
while (*s && isspace(*s)) s++;
if (*s == '-') {
negative = TRUE;
*s++ = SPACE;
} else
negative = FALSE;
/* move rhs to safe keeping - fraction or exponent part */
ft = NULL;
*rhs = 0;
if ((mode != PROG) && ((ft = strchr(s, '.')) ||
(ft = strchr(s, 'E')) ||
(ft = strchr(s, 'e')))) {
strcpy(rhs, ft);
*ft = 0;
}
/* add separators to the integer part ... */
strcpy(lhs, s);
len = strlen(s);
if (len > interval) {
count = 0;
d = scratch;
s = s + len - 1;
while (--len >= 0) {
if (count++ == interval) {
*d++ = sep;
count = 1;
}
*d++ = *s--;
}
*d = 0;
s = scratch + strlen(scratch) - 1;
d = lhs;
while (s >= scratch)
*d++ = *s--;
*d = 0;
}
/* Now reassemble ... */
d = string;
if (negative) {
*d++='-';
}
*d = 0;
strcpy(d, prefix);
strcat(d, lhs);
strcat(d, rhs);
}
long
asc2int(char *s) {
union frig {
long j;
byte c[4];
} frigger;
int i;
for (i = 0; i < 4; i++)
#if REVERSE_BYTES
frigger.c[3 - i] = *s++;
#else
frigger.c[i] = *s++;
#endif
return(frigger.j);
}
#if 0
long
bin2int(char *s) {
char *t;
long accum;
accum = 0;
t = s;
while (*t)
accum = 2 * accum + (*(t++) - '0');
return(accum);
}
#endif
long
ip2int(char *s) {
int index, i[4], leftBitSet = 0;
long retval;
if (!s || !*s)
return 0;
for (index = 0; index < 4; index++) {
char *dot = strchr(s, '.');
i[index] = 0;
if (dot)
*dot = 0;
if (strlen(s)) {
i[index] = atoi(s);
i[index] &= 0xff;
s += strlen(s);
}
if (dot) {
*dot = '.';
s = dot + 1;
}
}
if (i[0] > 0x7f) {
leftBitSet = 1;
i[0] &= 0x7f;
}
retval = i[0];
retval = (retval << 8) | i[1];
retval = (retval << 8) | i[2];
retval = (retval << 8) | i[3];
if (leftBitSet)
retval |= 0x80000000;
return retval;
}
/* Note that strtol appears to have a bug - it cannot accept negative
* hex, octal or binary numbers e.g. 0xffffffff, which is -1, hence
* the special processing */
void
parseBuffer(char *buf, long *longResult, double *doubleResult) {
if (mode == PROG) {
long temp = 0;
int isNeg = 0;
char *s, *d;
/* remove leading 0's unless all are 0 */
d = buf;
s = buf;
while (*s == '0')
s++;
if (s != d) {
while (*s)
*d++ = *s++;
*d = 0;
}
switch (intmode) {
case ASCIIM:
temp = asc2int(buf);
break;
case IP:
temp = ip2int(buf);
break;
case BIN:
if (strlen(buf) == 32 && *buf == '1') {
d = buf; s= d + 1; while (*s) *d++=*s++; *d=0;
isNeg = 1;
}
temp = strtol(buf, NULL, 2);
break;
case OCT:
if (strlen(buf) == 11) {
if (*buf == '2') {
d = buf; s= d + 1; while (*s) *d++=*s++; *d=0;
isNeg = 1;
}
if (*buf == '3') {
*buf = '1';
isNeg = 1;
}
}
temp = strtol(buf, NULL, 8);
break;
case DEC:
temp = strtol(buf, NULL, 10);
break;
case HEX:
if (strlen(buf) == 8) {
char b[2];
int leftDigit;
b[0] = *buf;
b[1] = 0;
leftDigit = strtol(b, NULL, 16);
if (leftDigit & 8) {
*buf = (leftDigit & 7) + '0';
isNeg = 1;
}
}
temp = strtol(buf, NULL, 16);
break;
}
*longResult = isNeg? temp|0x80000000: temp;
} else
*doubleResult = strtod(buf, NULL);
}
void stop_entering() {
if (entering) {
entering = FALSE;
lift_needed = TRUE;
parseBuffer(inbuf, &xiReg, &xfReg);
inptr = inbuf;
*inptr = 0;
}
}
void push(long inx) {
stop_entering();
tiReg = ziReg;
ziReg = yiReg;
yiReg = xiReg;
xiReg = inx;
lift_needed = TRUE;
}
void pushf(double inxf) {
stop_entering();
tfReg = zfReg;
zfReg = yfReg;
yfReg = xfReg;
xfReg = inxf;
lift_needed = TRUE;
}
long pop() {
long retval;
stop_entering();
retval = xiReg;
xiReg = yiReg;
yiReg = ziReg;
ziReg = tiReg;
return retval;
}
double popf() {
double retval;
stop_entering();
retval = xfReg;
xfReg = yfReg;
yfReg = zfReg;
zfReg = tfReg;
return retval;
}
int
liftStack() {
long temp;
double tempf;
if (lift_needed) {
if (mode == PROG) {
temp = xiReg;
push(temp);
} else {
tempf = xfReg;
pushf(tempf);
}
dispnums();
lift_needed = FALSE;
return 1;
} else
return 0;
}
static void echo_char(byte c) {
/* echo 'c' back to the screen and log in the input buffer */
char *p;
if ((c == BACKSPACE) && (mode != PROG || intmode != ASCIIM)) {
if (entering && (inptr > inbuf)) {
inptr--;
*inptr = ASCIIZ;
} else {
if (mode == PROG)
xiReg = 0;
else
xfReg = 0.0;
inptr = inbuf;
*inptr = ASCIIZ;
entering = TRUE;
dispnums();
}
} else /* Neither backspace or ASCII mode */ {
if (!entering) {
liftStack();
entering = TRUE;
for (inptr = inbuf; inptr < inbuf + 4; inptr++)
*inptr = ASCIIZ;
inptr = inbuf;
}
if (inptr < inbuf + max_digits)
if (mode == PROG && intmode == ASCIIM) {
for (p = inbuf; p < inbuf + 3; p++)
*p = *(p+1);
inbuf[3] = c;
inptr++;
} else {
*inptr++ = c;
*inptr = ASCIIZ;
}
else {
put_a_char(BELL);
msg("Too many digits!");
}
}
if (mode == PROG && intmode == ASCIIM) {
xiReg = asc2int(inbuf);
fmt_base(outbuf, xiReg, xfReg);
} else {
strcpy(outbuf, inbuf);
}
prep_for_output(outbuf);
print_x(outbuf);
}
void display() {
prinbase();
print_deg();
dispnums();
dispregs();
invert = FALSE;
print_inv();
}
static void save_x() {
stop_entering();
liReg = xiReg;
lfReg = xfReg;
}
#if 0
static long round(double f) {
BOOLEAN negative;
long ret;
negative = FALSE;
if (f < 0.0) {
negative = TRUE;
f = -f;
}
ret = floor(f + 0.5);
if (negative)
ret = -ret;
return(ret);
}
#endif
int
exec_asciim() {
isInverted();
last_was_fin_key = 0;
if (mode != PROG || intmode != ASCIIM) {
long tempx;
tempx = pop();
push(tempx);
mode = PROG;
intmode = ASCIIM;
msg("ASCII mode");
os_raw_mode(1);
max_digits = 4;
dispnums();
dispregs();
prinbase();
}
return 0;
}
static
void change_floatmode(COMMAND c) {
last_was_fin_key = 0;
if (c != floatmode) {
char buf[80];
floatmode = (c == SCIFORMAT? SCI: c);
stop_entering();
os_raw_mode(0);
max_digits = 20;
sprintf(buf, "%s notation",
(c==ENG)? "Engineering":
(c==SCIFORMAT)? "Scientific":
"Fixed point");
msg(buf);
dispnums();
dispregs();
prinbase();
}
}
int
exec_eng() {
isInverted();
last_was_fin_key = 0;
change_floatmode(ENG);
return 0;
}
int
exec_fix() {
isInverted();
last_was_fin_key = 0;
change_floatmode(FIX);
return 0;
}
int
exec_sciformat() {
isInverted();
last_was_fin_key = 0;
change_floatmode(SCIFORMAT);
return 0;
}
int
exec_places() {
long tempx;
isInverted();
last_was_fin_key = 0;
if (((tempx = places("Enter number of decimal places")) >= 0) &&
(tempx != decplaces)) {
char buf[80];
stop_entering();
decplaces = tempx;
os_raw_mode(0);
max_digits = 20;
sprintf(buf, "Places set to %d", decplaces);
msg(buf);
dispnums();
dispregs();
prinbase();
}
return 0;
}
int
exec_ip() {
isInverted();
last_was_fin_key = 0;
if (mode != PROG || intmode != IP) {
long tempx;
tempx = pop();
push(tempx);
mode = PROG;
os_raw_mode(0);
max_digits = 15;
intmode = IP;
msg("IP address mode");
dispnums();
dispregs();
prinbase();
}
return 0;
}
int
exec_bin() {
isInverted();
last_was_fin_key = 0;
if (mode != PROG || intmode != BIN) {
long tempx;
tempx = pop();
push(tempx);
mode = PROG;
os_raw_mode(0);
max_digits = 32;
intmode = BIN;
msg("Binary mode");
dispnums();
dispregs();
prinbase();
}
return 0;
}
int
exec_oct() {
isInverted();
last_was_fin_key = 0;
if (mode != PROG || intmode != OCT) {
long tempx;
tempx = pop();
push(tempx);
intmode = OCT;
mode = PROG;
os_raw_mode(0);
max_digits = 11;
msg("Octal mode");
dispnums();
dispregs();
prinbase();
}
return 0;
}
int
exec_dec() {
isInverted();
last_was_fin_key = 0;
if (mode != PROG || intmode != DEC) {
long tempx;
tempx = pop();
push(tempx);
mode = PROG;
intmode = DEC;
os_raw_mode(0);
max_digits = 10;
msg("Decimal mode");
dispnums();
dispregs();
prinbase();
}
return 0;
}
int
exec_hex() {
isInverted();
last_was_fin_key = 0;
if (mode != PROG || intmode != HEX) {
long tempx;
tempx = pop();
push(tempx);
mode = PROG;
intmode = HEX;
os_raw_mode(0);
max_digits = 8;
msg("Hexadecimal mode");
dispnums();
dispregs();
prinbase();
}
return 0;
}
static
void change_fin_sci_stat(COMMAND c) {
last_was_fin_key = 0;
if (mode != c) {
char buf[80];
mode = c;
stop_entering();
os_raw_mode(0);
max_digits = 20;
sprintf(buf, "%s mode", (c==FIN)? "Financial": (c==SCI)? "Scientific": "Statistics");
if (c == FIN) {
if (floatmode != FIX)
exec_fix();
if (decplaces != 2)
decplaces = 2;
dispnums();
}
msg(buf);
dispnums();
dispregs();
prinbase();
}
}
int
exec_fin() {
isInverted();
change_fin_sci_stat(FIN);
return 0;
}
int
exec_sci() {
isInverted();
change_fin_sci_stat(SCI);
return 0;
}
int
exec_stat() {
isInverted();
change_fin_sci_stat(STAT);
return 0;
}
int
exec_prog() {
isInverted();
last_was_fin_key = 0;
if (mode != PROG) {
mode = PROG;
stop_entering();
os_raw_mode(0);
max_digits = 10;
msg("Programmers mode");
}
dispnums();
dispregs();
prinbase();
return 0;
}
static void addPrime(long cand, long *primes, int *primeMultiplier, int *numPrimes) {
if (*numPrimes == 0 ) {
*numPrimes = 1;
primes[0] = cand;
primeMultiplier[0] = 1;
} else {
if (primes[*numPrimes - 1] == cand) {
primeMultiplier[*numPrimes - 1] = primeMultiplier[*numPrimes - 1] + 1;
} else {
if (*numPrimes == KMaxPrimes) {
msg("Too many primes");
return;
}
primes[*numPrimes] = cand;
primeMultiplier[*numPrimes] = 1;
(*numPrimes)++;
}
}
}
static
void factors(long orig) {
long target;
int i, isNeg = 0;
char msgbuf[100];
long cand, rem, term;
long primes[KMaxPrimes];
int primeMultiplier[KMaxPrimes], numPrimes;
if (orig < 0) {
isNeg = 1;
orig = -orig;
}
if (orig < 2) {
msg("No prime factors");
return;
}
target = orig;
term = sqrt((double) target) + 1;
numPrimes = 0;
for (i = 0; i < KMaxPrimes; i++) {
primes[i] = 0;
primeMultiplier[i] = 0;
}
// check for 2's
cand = 2;
while (cand < term) {
rem = target / cand;
if (rem * cand == target) {
target = rem;
term = sqrt(target) + 1;
addPrime(cand, primes, primeMultiplier, &numPrimes);
} else
break;
}
// check odd numbers
cand = 3;
while (cand < term) {
rem = target / cand;
if (rem * cand == target) {
target = rem;
term = sqrt(target)+1;
addPrime(cand, primes, primeMultiplier, &numPrimes);
} else
cand += 2;
}
if (target > 1)
addPrime(target, primes, primeMultiplier, &numPrimes);
if (numPrimes == 1 && primeMultiplier[0] == 1) {
sprintf(msgbuf, "%ld is prime", orig);
} else {
#ifdef EBOOKMAN
msgbuf[0] = 0;
#else
sprintf(msgbuf,"%ld=", orig);
#endif
for (i=0, target = 1; i < numPrimes; i++) {
char *m = msgbuf;
m += strlen(msgbuf);
#ifdef DEBUG
{
int j;
for (j = 0; j < primeMultiplier[i]; j++)
target *= primes[i];
}
#endif
sprintf(m, "%ld", primes[i]);
if (primeMultiplier[i] > 1) {
m += strlen(m);
sprintf(m, "^%d", primeMultiplier[i]);
}
if (i < numPrimes - 1) {
strcat(m, "×");
}
}
}
msg(msgbuf);
#ifdef DEBUG
if (target != orig) { // ERRORCHECKING IN BETA ONLY!!!
msg("Error!");
}
#endif
}
static int power(int x, int n) {
int i, p;
switch (x) {
case 0:
return 0;
case 1:
return 1;
default:
switch (n) {
case 0:
return 1;
case 1:
return x;
default:
if (n < 0 || n > 32) {
msg("Overflow");
return 0;
}
p = 1;
for (i = 1; i <= n; ++i)
p *= x;
return p;
}
}
}
/* static void arith_int(COMMAND c) { */
int
exec_chs() {
long tempx;
double tempxf;
char *c_ptr, c_buf[45];
isInverted();
last_was_fin_key = 0;
if (mode == PROG) {
if (entering) {
c_ptr = inbuf;
strcpy(c_buf, c_ptr);
*c_ptr = ASCIIZ;
c_ptr = c_buf;
if (*c_ptr == '-')
c_ptr++;
else
strcat(inbuf, "-");
strcat(inbuf, c_ptr);
inptr = &inbuf[strlen(inbuf)];
} else {
tempx = pop();
push(-tempx);
}
} else {
if (entering) {
if ((c_ptr = strchr(inbuf, 'E')))
c_ptr++;
else
c_ptr = inbuf;
strcpy(c_buf, c_ptr);
*c_ptr = ASCIIZ;
c_ptr = c_buf;
if (*c_ptr == '-')
c_ptr++;
else
strcat(inbuf, "-");
strcat(inbuf, c_ptr);
inptr = &inbuf[strlen(inbuf)];
} else {
tempxf = popf();
pushf(-tempxf);
}
}
dispnums();
return 0;
}
/* operations on X only */
int
exec_not() {
long tempx;
double tempxf;
isInverted();
save_x();
last_was_fin_key = 0;
if (mode == PROG) {
tempx = pop();
tempx = ~tempx;
push(tempx);
} else {
tempxf = popf();
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_shiftl() {
long tempx;
double tempxf;
if (isInverted())
return exec_shiftr();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempx = tempx << 1;
push(tempx);
} else {
tempxf = popf();
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_shiftr() {
long tempx;
double tempxf;
if (isInverted())
return exec_shiftl();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempx = tempx >> 1;
push(tempx);
} else {
tempxf = popf();
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_primef() {
long tempx;
double tempxf;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
factors(tempx);
push(tempx);
} else {
tempxf = popf();
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_sqr() {
long tempx;
double tempxf;
if (isInverted())
return exec_root();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempx = tempx * tempx;
push(tempx);
} else {
tempxf = popf();
tempxf *= tempxf;
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_root() {
double tempxf;
if (isInverted())
return exec_sqr();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempxf = pop();
} else
tempxf = popf();
tempxf = sqrt(tempxf);
if (mode == PROG)
push((long)(tempxf+0.5));
else
pushf(tempxf);
dispnums();
return 0;
}
int
exec_cube() {
long tempx;
double tempxf;
if (isInverted())
return exec_croot();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempx = tempx * tempx * tempx;
push(tempx);
} else {
tempxf = popf();
tempxf = tempxf * tempxf * tempxf;
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_croot() {
double tempxf;
if (isInverted())
return exec_cube();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempxf = pop();
} else
tempxf = popf();
tempxf = pow(tempxf, 1.0 / 3.0);
if (mode == PROG)
push((long)(tempxf+0.5));
else
pushf(tempxf);
dispnums();
return 0;
}
int
exec_reci() {
long tempx;
double tempxf;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempx = 0;
push(tempx);
} else {
tempxf = popf();
if (tempxf == 0.0)
msg(div_by_zero);
else {
tempxf = 1.0 / tempxf;
}
pushf(tempxf);
}
dispnums();
return 0;
}
/* X & Y operations */
int
exec_plus() {
long tempx, tempy;
double tempxf, tempyf;
if (isInverted())
return exec_minus();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempx = tempx + tempy;
push(tempx);
} else {
tempxf = popf();
tempyf = popf();
tempxf = tempxf + tempyf;
pushf(tempxf);
}
dispnums();
return 0;
}
/* case '-': */
int
exec_minus() {
long tempx, tempy;
double tempxf, tempyf;
if (isInverted())
return exec_plus();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempx = tempy - tempx;
push(tempx);
} else {
tempxf = popf();
tempyf = popf();
tempxf = tempyf - tempxf;
pushf(tempxf);
}
dispnums();
return 0;
}
/* case '*': */
int
exec_mult() {
long tempx, tempy;
double tempxf, tempyf;
if (isInverted())
return exec_divide();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempx = tempx * tempy;
push(tempx);
} else {
tempxf = popf();
tempyf = popf();
tempxf = tempxf * tempyf;
pushf(tempxf);
}
dispnums();
return 0;
}
/* case '/': */
int
exec_divide() {
long tempx, tempy;
double tempxf, tempyf;
if (isInverted())
return exec_mult();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
if (tempx == 0) {
push(tempy);
msg(div_by_zero);
} else {
if (tempx != 0)
tempx = tempy / tempx;
else
tempx = 0;
}
push(tempx);
} else {
tempxf = popf();
tempyf = popf();
if (tempxf == 0.0) {
pushf(tempyf);
msg(div_by_zero);
} else {
tempxf = tempyf / tempxf;
}
pushf(tempxf);
}
dispnums();
return 0;
}
/* case '^': */
int
exec_xor() {
long tempx, tempy;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempx ^= tempy;
push(tempx);
} else {
}
dispnums();
return 0;
}
/* case '|': */
int
exec_or() {
long tempx, tempy;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempx |= tempy;
push(tempx);
} else {
}
dispnums();
return 0;
}
/* case '&': */
int
exec_and() {
long tempx, tempy;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempx &= tempy;
push(tempx);
} else {
}
dispnums();
return 0;
}
/* case '<': */
int
exec_shiftyl() {
long tempx, tempy;
if (isInverted())
return exec_shiftyr();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempy <<= tempx;
tempx = tempy;
push(tempx);
} else {
}
dispnums();
return 0;
}
/* case '>': */
int
exec_shiftyr() {
long tempx, tempy;
if (isInverted())
return exec_shiftyl();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
tempy >>= tempx;
tempx = tempy;
push(tempx);
} else {
}
dispnums();
return 0;
}
int
exec_modulus() {
long tempx, tempy;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
if (tempx == 0) {
push(tempx);
msg(div_by_zero);
} else {
tempy = pop();
tempy %= tempx;
tempx = tempy;
push(tempx);
}
} else {
}
dispnums();
return 0;
}
int
exec_ytox() {
long tempx, tempy;
double tempxf, tempyf;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
tempx = pop();
tempy = pop();
if (tempx < 0) {
msg("X cannot be < 0");
push(tempy);
} else
tempx = power(tempy, tempx);
push(tempx);
} else {
tempxf = popf();
tempyf = popf();
tempxf = pow(tempyf, tempxf);
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_percent() {
double tempxf, tempyf;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
} else {
tempxf = popf();
tempyf = popf();
pushf(tempyf); /* push y back on stack for future ops */
if (isInverted()) {
if (tempxf == 0.0) {
msg(div_by_zero);
pushf(tempxf);
} else {
tempxf = tempyf * 100.0 / tempxf;
}
} else {
tempxf = tempxf * tempyf / 100.0;
}
pushf(tempxf);
}
dispnums();
return 0;
}
int
exec_percentch() {
double tempxf, tempyf;
isInverted();
last_was_fin_key = 0;
save_x();
if (mode == PROG) {
} else {
tempxf = popf();
tempyf = popf();
pushf(tempyf); /* push y back on stack for future ops */
if (isInverted()) {
if (tempxf == 0.0) {
msg(div_by_zero);
pushf(tempxf);
} else {
tempxf = tempyf * (1.0 + tempxf / 100.0);
}
} else {
tempxf = 100.0 * (tempxf - tempyf) / tempyf;
}
pushf(tempxf);
}
dispnums();
return 0;
}
/* static void scientific(COMMAND c) { */
int
exec_sin() {
double tempxf;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (isInverted()) {
tempxf = asin(tempxf);
if (degree)
tempxf *= r_to_d;
} else {
if (degree)
tempxf = sin(d_to_r * tempxf);
else
tempxf = sin(tempxf);
}
pushf(tempxf);
dispnums();
return 0;
}
int
exec_asin() {
invert = TRUE;
return(exec_sin());
}
int
exec_cos() {
double tempxf;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (isInverted()) {
tempxf = acos(tempxf);
if (degree)
tempxf *= r_to_d;
} else {
if (degree)
tempxf = cos(d_to_r * tempxf);
else
tempxf = cos(tempxf);
}
pushf(tempxf);
dispnums();
return 0;
}
int
exec_acos() {
invert = TRUE;
return(exec_cos());
}
int
exec_tan() {
double tempxf;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (isInverted()) {
tempxf = atan(tempxf);
if (degree)
tempxf *= r_to_d;
} else {
if (degree)
tempxf = tan(d_to_r * tempxf);
else
tempxf = tan(tempxf);
}
pushf(tempxf);
dispnums();
return 0;
}
int
exec_atan() {
invert = TRUE;
return(exec_tan());
}
int
exec_sinh() {
double tempxf;
if (mode == PROG)
return 9;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (isInverted()) {
tempxf = log(tempxf+sqrt(tempxf*tempxf + 1));
} else {
tempxf = 0.5*(exp(tempxf) - exp(-tempxf));
}
pushf(tempxf);
dispnums();
return 0;
}
int
exec_asinh() {
invert = TRUE;
return(exec_sinh());
}
int
exec_cosh() {
double tempxf;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (isInverted()) {
if (tempxf < 1) {
msg("Illegal value");
pushf(tempxf);
} else {
tempxf = log(tempxf+sqrt(tempxf*tempxf - 1));
}
} else {
tempxf = 0.5*(exp(tempxf) + exp(-tempxf));
}
pushf(tempxf);
dispnums();
return 0;
}
int
exec_acosh() {
invert = TRUE;
return(exec_cosh());
}
int
exec_tanh() {
double tempxf;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (isInverted()) {
if (tempxf == 1) {
msg("Illegal value");
pushf(tempxf);
} else {
tempxf = 0.5*log((tempxf+1)/(1-tempxf));
}
} else {
double p;
p = exp(2*tempxf);
tempxf = (p-1)/(p+1);
}
pushf(tempxf);
dispnums();
return 0;
}
int
exec_atanh() {
invert = ~invert;
return(exec_tanh());
}
int
exec_etox() {
if (mode == PROG)
return 0;
if (isInverted())
return exec_loge();
last_was_fin_key = 0;
save_x();
pushf(exp(popf()));
dispnums();
return 0;
}
int
exec_loge() {
if (mode == PROG)
return 0;
if (isInverted())
return exec_etox();
last_was_fin_key = 0;
save_x();
pushf(log(popf()));
dispnums();
return 0;
}
int
exec_10tox() {
if (mode == PROG)
return 0;
if (isInverted())
return exec_log10();
last_was_fin_key = 0;
save_x();
pushf(pow(10.0, popf()));
dispnums();
return 0;
}
int
exec_log10() {
if (mode == PROG)
return 0;
if (isInverted())
return exec_10tox();
last_was_fin_key = 0;
save_x();
pushf(log10(popf()));
dispnums();
return 0;
}
int
exec_frc() {
double tempxf;
long tempy = 0;
isInverted();
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (tempxf < 0.0) {
tempy = 1;
tempxf = -tempxf;
}
tempxf = tempxf - floor(tempxf);
if (tempy)
tempxf = -tempxf;
pushf(tempxf);
dispnums();
return 0;
}
int
exec_int() {
double tempxf;
long tempy = 0;
isInverted();
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (tempxf < 0.0) {
tempy = 1;
tempxf = -tempxf;
}
tempxf = (double) floor(tempxf);
if (tempy)
tempxf = -tempxf;
pushf(tempxf);
dispnums();
return 0;
}
int
exec_hms() {
double tempxf;
int sign = 0;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (tempxf < 0.0) {
sign = 1;
tempxf = -tempxf;
}
if (isInverted()) { /* convert h.mmss to hours */
long h, m, s;
h = tempxf;
tempxf = (tempxf - h) * 100 + 1E-10;
m = tempxf;
tempxf = (tempxf - m) * 100 + 1E-10;
s = tempxf;
tempxf = h + m / 60.0 + s / 3600.0;
msg("HH.MMSS -> hours");
} else { /* convert hours to h.mmss */
long h, m, s;
h = tempxf;
tempxf = (tempxf - h) * 60 + 1E-10;
m = tempxf;
s = (tempxf - m) * 60 + 1E-10;
tempxf = h + m / 100.0 + s / 10000.0;
if (decplaces < 4)
decplaces = 4;
if (floatmode != FIX)
exec_fix();
msg("hours -> HH.MMSS");
}
if (sign)
tempxf = -tempxf;
pushf(tempxf);
dispnums();
return 0;
}
int
exec_dtor() {
double tempxf;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (isInverted()) {
tempxf *= r_to_d;
msg("radians to degrees");
} else {
tempxf *= d_to_r;
msg("degrees to radians");
}
pushf(tempxf);
dispnums();
return 0;
}
int
exec_rtod() {
invert = ~invert;
return(exec_dtor());
}
int
exec_rtop() {
double tempxf, tempyf;
if (mode == PROG)
return 0;
last_was_fin_key = 0;
save_x();
tempxf = popf();
tempyf = popf();
if (isInverted()) { /* convert (r, theta) to (x, y) */
double theta, r;
theta = tempyf;
if (degree)
theta = d_to_r * theta;
r = tempxf;
tempyf = r * sin(theta);
tempxf = r * cos(theta);
msg("polar to rectangular");
} else { /* convert (x, y) to (r, theta) */
double theta, r;
r = sqrt(tempxf * tempxf + tempyf * tempyf);
theta = atan2(tempyf, tempxf);
if (degree)
theta = r_to_d * theta;
tempyf = theta;
tempxf = r;
msg("rectangular to polar");
}
pushf(tempyf);
pushf(tempxf);
dispnums();
return 0;
}
int
exec_ptor() {
invert = ~invert;
return(exec_rtop());
}
static double fact(double from, double to) {
double total, n, m;
n = floor(from);
m = floor(to);
if (n <= 0 || m <= 0)
return(1.0);
total = 1.0;
while (n > m) {
total = total * n;
n = n - 1;
}
return total;
}
/* static void summation(COMMAND c) { */
int
exec_fact() {
double tempxf;
if (mode == PROG)
return 0;
isInverted();
last_was_fin_key = 0;
save_x();
tempxf = popf();
if (tempxf < 0) {
msg("Illegal value");
pushf(tempxf);
} else {
pushf(fact(tempxf, 1));
dispnums();
}
return 0;
}
int
exec_sum() {
double tempxf, tempyf;
if (isInverted())
return exec_sumr();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
save_x();
tempxf = popf();
tempyf = popf();
pushf(tempyf);
pushf(tempxf);
regf[STAT_NUM_REG]++;
regf[STAT_SUMX_REG] += tempxf;
regf[STAT_SUMX2_REG] += tempxf * tempxf;
regf[STAT_SUMY_REG] += tempyf;
regf[STAT_SUMY2_REG] += tempyf * tempyf;
regf[STAT_SUMXY_REG] += tempxf * tempyf;
pushf(regf[STAT_NUM_REG]);
dispreg(STAT_NUM_REG);
dispreg(STAT_SUMX_REG);
dispreg(STAT_SUMY_REG);
dispreg(STAT_SUMX2_REG);
dispreg(STAT_SUMY2_REG);
dispreg(STAT_SUMXY_REG);
dispnums();
msg("X & Y accumulated");
return 0;
}
int
exec_sumr() {
double tempxf, tempyf;
if (isInverted())
return exec_sum();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
if (regf[STAT_NUM_REG] > 0) {
save_x();
tempxf = popf();
tempyf = popf();
pushf(tempyf);
pushf(tempxf);
regf[STAT_NUM_REG]--;
regf[STAT_SUMX_REG] -= tempxf;
regf[STAT_SUMX2_REG] -= tempxf * tempxf;
regf[STAT_SUMY_REG] -= tempyf;
regf[STAT_SUMY2_REG] -= tempyf * tempyf;
regf[STAT_SUMXY_REG] -= tempxf * tempyf;
pushf(regf[STAT_NUM_REG]);
dispreg(STAT_NUM_REG);
dispreg(STAT_SUMX_REG);
dispreg(STAT_SUMY_REG);
dispreg(STAT_SUMX2_REG);
dispreg(STAT_SUMY2_REG);
dispreg(STAT_SUMXY_REG);
dispnums();
msg("X & Y decremented");
}
return 0;
}
int
exec_sum0() {
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
save_x();
regf[STAT_NUM_REG] = 0.0;
regf[STAT_SUMX_REG] = 0.0;
regf[STAT_SUMX2_REG] = 0.0;
regf[STAT_SUMY_REG] = 0.0;
regf[STAT_SUMY2_REG] = 0.0;
regf[STAT_SUMXY_REG] = 0.0;
dispreg(STAT_NUM_REG);
dispreg(STAT_SUMX_REG);
dispreg(STAT_SUMY_REG);
dispreg(STAT_SUMX2_REG);
dispreg(STAT_SUMY2_REG);
dispreg(STAT_SUMXY_REG);
dispnums();
msg("Accumulator registers cleared");
return 0;
}
int
exec_nstat() {
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
pushf(regf[STAT_NUM_REG]);
msg("Number of entries");
dispnums();
return 0;
}
int
exec_means() { /* mean of x & y */
double tempxf, tempyf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
if (regf[STAT_NUM_REG] == 0.0)
msg("Nothing accumulated yet!");
else {
tempyf = regf[STAT_SUMY_REG] / regf[STAT_NUM_REG];
pushf(tempyf);
tempxf = regf[STAT_SUMX_REG] / regf[STAT_NUM_REG];
pushf(tempxf);
dispnums();
msg("Mean of X's and of Y's");
}
return 0;
}
int
exec_mean() {
double tempxf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
if (regf[STAT_NUM_REG] == 0.0)
msg(div_by_zero);
else {
tempxf = regf[STAT_SUMX_REG] / regf[STAT_NUM_REG];
pushf(tempxf);
dispnums();
msg("Mean of X's");
}
return 0;
}
int
exec_meany() {
double tempyf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
if (regf[STAT_NUM_REG] == 0.0)
msg(div_by_zero);
else {
tempyf = regf[STAT_SUMY_REG] / regf[STAT_NUM_REG];
pushf(tempyf);
dispnums();
msg("Mean of Y's");
}
return 0;
}
int
exec_s_dev() {
double tempxf, tempyf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
/* s.dev = sqrt((sum:(x^2) - (sum:(x))^2 /n) / n-1) */
if (regf[STAT_NUM_REG] == 0.0)
msg(div_by_zero);
else {
tempyf = sqrt((regf[STAT_SUMY2_REG] - regf[STAT_SUMY_REG]*regf[STAT_SUMY_REG]/regf[STAT_NUM_REG])/(regf[STAT_NUM_REG] - 1));
pushf(tempyf);
tempxf = sqrt((regf[STAT_SUMX2_REG] - regf[STAT_SUMX_REG]*regf[STAT_SUMX_REG]/regf[STAT_NUM_REG])/(regf[STAT_NUM_REG] - 1));
pushf(tempxf);
dispnums();
msg("Std Dev of X's & Y's");
}
return 0;
}
int
exec_perm() {
double tempxf, tempyf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
save_x();
tempxf = popf();
tempyf = popf();
if (tempxf < 0 || tempyf < 0 || tempyf < tempxf) {
msg("Bad values");
pushf(tempyf);
pushf(tempxf);
} else {
char buf[80];
double n, r;
n = floor(tempyf+.5);
r = floor(tempxf+.5);
pushf(fact(n, n - r));
dispnums();
sprintf(buf, "Permutations (%.0f, %.0f)", r, n);
msg(buf);
}
return 0;
}
int
exec_comb() {
double tempxf, tempyf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
return 0;
save_x();
tempxf = popf();
tempyf = popf();
if (tempxf < 0 || tempyf < 0 || tempyf < tempxf) {
msg("Bad values");
pushf(tempyf);
pushf(tempxf);
} else {
double n, r;
char buf[80];
n = floor(tempyf+.5);
r = floor(tempxf+.5);
if (n-r > r)
pushf(tempxf = fact(n, n-r) / fact(r, 1));
else
pushf(tempxf = fact(n, r) / fact(n-r, 1));
dispnums();
sprintf(buf, "Combinations (%.0f, %.0f)", r, n);
msg(buf);
}
return 0;
}
int
exec_lr() {
last_was_fin_key = 0;
if (mode == PROG)
return 0;
if (isInverted()) {
char buf[80];
sprintf(buf, "Linear Regression formula is: Y= %f*X + %f",
regf[STAT_SLOPE], regf[STAT_Y_INT]);
msg(buf);
} else {
if (regf[STAT_NUM_REG] < 2)
msg("Not enough data points");
else {
double detx, dety, a, b, r2;
detx = regf[STAT_SUMX2_REG]*regf[STAT_NUM_REG] -
regf[STAT_SUMX_REG]*regf[STAT_SUMX_REG];
dety = regf[STAT_SUMY2_REG]*regf[STAT_NUM_REG] -
regf[STAT_SUMY_REG]*regf[STAT_SUMY_REG];
a = (regf[STAT_SUMXY_REG]*regf[STAT_NUM_REG] -
regf[STAT_SUMX_REG]*regf[STAT_SUMY_REG])/detx;
b = (regf[STAT_SUMX2_REG]*regf[STAT_SUMY_REG] -
regf[STAT_SUMX_REG]*regf[STAT_SUMXY_REG])/detx;
r2 = a*a*detx/dety;
regf[STAT_SLOPE] = a;
regf[STAT_Y_INT] = b;
regf[STAT_R2] = b;
pushf(r2);
pushf(b);
pushf(a);
dispnums();
msg("Slope (X), Y-intersect (Y) & correlation (Z) calculated");
}
}
return 0;
}
int
exec_calcy() {
double tempxf, tempyf;
last_was_fin_key = 0;
if (mode == PROG)
return 0;
if (isInverted()) {
tempyf = popf();
tempxf = (tempyf - regf[STAT_Y_INT]) / regf[STAT_SLOPE];
msg("X calculated from Y");
} else {
tempxf = popf();
tempyf = regf[STAT_SLOPE] * tempxf + regf[STAT_Y_INT];
msg("Y calculated from X");
}
pushf(tempyf);
pushf(tempxf);
dispnums();
return 0;
}
static int dayFunction(double f) {
long y, m, d, x, z;
f += 0.00005; /* round up */
y = f;
f = (f - y) * 100;
m = f;
f = (f - m) * 100;
d = f;
if (m <= 2) {
x = 0;
z = y - 1;
} else {
x = (long) (0.4*m + 2.3);
z = y;
}
return 365*y + 31 *(m - 1) + d + (int)(z/4) - x;
}
static int dayFunction30(double f, int i) {
static int was30or31;
int y, m, d, a1;
f += 0.00005; /* round up */
y = f;
f = (f - y) * 100;
m = f;
f = (f - m) * 100;
d = f;
if (i == 1) {
if (d == 31) {
a1 = 30;
} else {
a1 = d;
}
if (d == 30 || d == 31) {
was30or31 = 1;
} else {
was30or31 = 0;
}
} else {
if (d == 31) {
if (was30or31) {
a1 = 30;
} else {
a1 = d;
}
} else {
a1 = d;
}
}
return 360*y + 30*m + a1;
}
/* static void financial(COMMAND c) { */
/*
* Financial function register allocation ...
*
* 5 n
* 6 interest
* 7 present value
* 8 payment
* 9 future value
*
* Formulae are :
*
* 0 = PVAL + (1 + I.S) PMT (1 - (1+I)**-N ) / I + FVAL * (1+I)**-N
New Formula from psion version:
y=Fval + Pval*(1 + i)**n + (1 + s*i)*pmt*((1 + i)**n - 1)/i
y'=pmt*(1 + (1 + i)^(n-1) * n * (pval* i^2 / pmt - (1+i)/n + i + s*i^2)) / i^2
*
* where:
*
* N = number of periods
* PVAL = present value
* I = interest rate (as a fraction - user sees a percent)
* PMT = payment
* FVAL = future value
* S = payment mode factor. 0 for payment at end of period, 1 for start.
*/
static double
calc_fval(double pval,
double n,
double pmt,
double interest) {
double tempxf, tempyf;
if (fabs(interest) < 1.0e-13) {
tempxf = -(pval + (n + finPayAt0) * pmt);
} else {
tempyf = pow(1.0 + interest, n);
#if 0
if (errno) {
printf("Error in calc_fval: pow(): interest=%.4f%% n=%.2f\n", interest*100.0, n);
exit(1);
}
#endif
tempxf = -(pval*tempyf + (1 + finPayAt0*interest)*pmt*(tempyf - 1.0)/interest);
}
return tempxf;
}
static double
calc_pval(double fval,
double n,
double pmt,
double interest) {
double tempxf, tempyf;
if (fabs(interest) < 1.0e-13) {
tempxf = -(fval + (n + finPayAt0)*pmt);
} else {
tempyf = pow(1.0 + interest, -n);
#if 0
if (errno) {
printf("Error in calc_pval: pow(): interest=%.4f%% n=%.2f\n", interest*100.0, n);
exit(1);
}
#endif
tempxf = -(fval*tempyf + (1 + finPayAt0*interest)*pmt*(1.0 - tempyf)/interest);
}
return tempxf;
}
static double
calc_n(double fval,
double pval,
double pmt,
double interest) {
double tempxf, tempyf, den;
tempxf = 0.0;
if (fabs(interest) < 1.0e-13) {
if (fabs(pmt) < 1.0e-13) {
msg(div_by_zero);
} else {
tempxf = -(fval + pval)/pmt - finPayAt0;
}
} else {
tempyf = 1.0 + interest*finPayAt0;
den = pval + tempyf*pmt/interest;
if (fabs(pmt) < 1.0e-13) {
msg(div_by_zero);
} else {
tempxf = log((tempyf*pmt/interest - fval)/den)/log(1.0 + interest);
}
}
return tempxf;
}
static double
calc_pmt(double fval,
double pval,
double n,
double interest) {
double tempxf, tempyf;
if (fabs(interest) < 1.0e-13) {
tempxf = -(fval + pval)/(n + finPayAt0);
} else {
tempyf = pow(1.0 + interest, n);
#if 0
if (errno) {
printf("Error in calc_pmt: pow(): interest=%.4f%% n=%.2f\n", interest*100.0, n);
exit(1);
}
#endif
tempxf = -interest*(fval + pval*tempyf)/((1.0 + interest*finPayAt0)*(tempyf - 1.0));
}
return tempxf;
}
int
exec_pval() {
double tempxf;
double interest, n, pmt, fval;
if (mode == PROG)
return 0;
if (isInverted()) {
pushf(regf[FIN_PVAL_REG]);
dispnums();
msg("Present value recalled");
} else if (!last_was_fin_key) {
save_x();
tempxf = popf();
pushf(tempxf);
regf[FIN_PVAL_REG] = tempxf;
dispreg(FIN_PVAL_REG);
dispnums();
last_was_fin_key = TRUE;
msg("Present value entered");
} else {
n = regf[FIN_NUM_REG];
interest = regf[FIN_INT_REG] / 100.0;
if (n <= 0.0) {
msg("# payments must be > 0");
return 0;
}
if (interest < 0.0) {
msg("Interest must be >= 0");
return 0;
}
pmt = regf[FIN_PMT_REG];
fval = regf[FIN_FVAL_REG];
tempxf = calc_pval(fval, n, pmt, interest);
pushf(tempxf);
regf[FIN_PVAL_REG] = tempxf;
dispreg(FIN_PVAL_REG);
dispnums();
msg("Present value computed");
}
return 0;
}
int
exec_fval() {
double tempxf;
double interest, n, pval, pmt;
if (mode == PROG)
return 0;
if (isInverted()) {
pushf(regf[FIN_FVAL_REG]);
dispnums();
msg("Future value recalled");
} else if (!last_was_fin_key) {
save_x();
tempxf = popf();
pushf(tempxf);
regf[FIN_FVAL_REG] = tempxf;
dispreg(FIN_FVAL_REG);
dispnums();
last_was_fin_key = TRUE;
msg("Final value entered");
} else {
n = regf[FIN_NUM_REG];
interest = regf[FIN_INT_REG] / 100.0;
if (n <= 0.0) {
msg("# payments must be > 0");
return 0;
}
if (interest < 0.0) {
msg("Interest must be >= 0");
return 0;
}
pval = regf[FIN_PVAL_REG];
pmt = regf[FIN_PMT_REG];
tempxf = calc_fval(pval, n, pmt, interest);
pushf(tempxf);
regf[FIN_FVAL_REG] = tempxf;
dispreg(FIN_FVAL_REG);
dispnums();
msg("Final value calculated");
}
return 0;
}
int
exec_pmt() {
double tempxf;
double interest, n, pval, fval;
if (mode == PROG)
return 0;
if (isInverted()) {
pushf(regf[FIN_PMT_REG]);
dispnums();
msg("Payment recalled");
} else if (!last_was_fin_key) {
save_x();
tempxf = popf();
pushf(tempxf);
regf[FIN_PMT_REG] = tempxf;
dispreg(FIN_PMT_REG);
dispnums();
last_was_fin_key = TRUE;
msg("Payment entered");
} else {
n = regf[FIN_NUM_REG];
if (n <= 0.0) {
msg("# payments must be > 0");
return 0;
}
interest = regf[FIN_INT_REG] / 100.0;
if (interest < 0.0) {
msg("Interest must be >= 0");
return 0;
}
pval = regf[FIN_PVAL_REG];
fval = regf[FIN_FVAL_REG];
tempxf = calc_pmt(fval, pval, n, interest);
pushf(tempxf);
regf[FIN_PMT_REG] = tempxf;
dispreg(FIN_PMT_REG);
dispnums();
msg("Payment calculated");
}
return 0;
}
static double
newton_raphson_interest(double fval,
double pval,
double n,
double pmt,
double guess,
double max_guess,
int *converged) {
double epsilon, last_epsilon, fy, dy, tempyf, intst;
int count, epsilon_increasing;
count = epsilon_increasing = 0;
last_epsilon = epsilon = 1000.0;
intst = 0.0;
if (guess == 0.0)
guess = 0.0001;
if (fabs(fval - pval - n * pmt) > 1.0e-13) {
while ((count < 10000) && (fabs(epsilon) > 1.0e-15)) {
tempyf = pow(1.0 + guess, n);
#if 0
if (errno) {
printf("Error in newton_raphson_interest: pow(): guess=%.4f%% n=%.2f\n", guess*100.0, n);
exit(1);
}
#endif
fy = fval + pval*tempyf + (1.0 + finPayAt0*guess)*pmt*(tempyf - 1.0)/guess;
tempyf = pow(1.0 + guess, n-1);
#if 0
if (errno) {
printf("Error in newton_raphson_interest: dy: pow(): guess=%.4f%% n=%.2f\n", guess*100.0, n);
exit(1);
}
#endif
dy = (((pval + pmt*(double)finPayAt0)*n*guess*guess + (n - 1.0)*guess*pmt - pmt)*tempyf + pmt)/(guess*guess);
epsilon=fy/dy;
guess -= epsilon;
if (fabs(epsilon) > fabs(last_epsilon))
epsilon_increasing++;
else
epsilon_increasing = 0;
if (epsilon_increasing > 5) { /* give up */
*converged = 0;
return intst;
}
if (guess > max_guess) { /* give up */
converged = 0;
return intst;
}
last_epsilon = epsilon;
count++;
}
intst = guess;
}
*converged = 1;
return intst;
}
static double
init_guess(double fval, double pval, double n, double pmt) {
double guess;
if (fabs(pval) < 1.0e-11)
/* guess = -2.0*(fval + pmt*n)/(pmt*(n + 1.0)*n); */
guess = 2.0*(pow(-fval/(n*pmt),1.0/n) - 1.0);
else
guess = pow(-(fval + pmt*n)/pval, 1.0/n) - 1.0;
return(guess);
}
/*
* this is a financial calculation after all, so it is fair to assume
* that interest is 0-1000% and that either pval or fval is
* non-zero. Also n>=1.
*
* The initial guess must not blow up pow(1+guess,n) so initial guess < pow(L,1/n)-1
*/
static double
calc_intst(double fval,
double pval,
double n,
double pmt,
int *converged) {
int count = 0, watch_pval;
double guess, lower_guess, upper_guess, error, intst, max_guess, newguess;
double calculated_val, good_enough_guess, val;
#define MAX_GUESSES 50
#define MAX_INTEREST_RATE 1.0e10 /* financial calculation, right? */
lower_guess = intst = 0.0;
upper_guess = pow(DBL_MAX,1.0/n)-1.0;
if (upper_guess > MAX_INTEREST_RATE)
upper_guess = MAX_INTEREST_RATE;
max_guess = upper_guess;
if (fabs(pval) < 1.0e-11 && fabs(pmt) < 1.0e-11) {
converged = 0;
return intst;
}
/* keep an eye on pval if fval is zero: */
watch_pval = (fabs(fval) < 1.0e-11);
if (watch_pval && fabs(pval) < 1.0e-11) {
*converged = 0;
return intst;
}
val = watch_pval? pval: fval;
good_enough_guess = fabs(val) * 0.05;
/* guess high in the 'normal' case to avoid exploring the looney
* rates. This first recursion is from Steve Steps:
*/
guess = init_guess(fval, pval, n, pmt);
if (fabs(pval) > 1.0e-11) { /* tends to converge if pval != 0 */
int rounds = 20;
double newguess;
while (rounds--) {
if (guess > max_guess) {
break;
} else {
newguess = (1.0+guess*finPayAt0)*pmt*(1.0-pow(1.0+guess,n)) / (fval + pval*pow(1.0+guess,n));
}
if (newguess <= 0.0) { /* it failed */
/* printf("Negative guess. Reverting to initial guess\n"); */
guess = init_guess(fval, pval, n, pmt);
break;
}
calculated_val = watch_pval?
calc_pval(fval, n, pmt, guess):
calc_fval(pval, n, pmt, guess);
error = fabs(calculated_val - val);
if (error < good_enough_guess)
break; /* good enough */
guess = newguess;
}
}
if (guess > max_guess) {
/* printf("Guess too high (%f%%)\n", guess*100.0); */
guess = init_guess(fval, pval, n, pmt);
if (guess > max_guess)
guess = max_guess / 4.0;
}
/* Try and improve guess with a binary chop */
while (count < MAX_GUESSES) {
calculated_val = watch_pval?
calc_pval(fval, n, pmt, guess):
calc_fval(pval, n, pmt, guess);
error = fabs(calculated_val - val);
if (error < good_enough_guess) {
intst = newton_raphson_interest(fval, pval, n, pmt, guess, max_guess, converged);
if (!*converged) {
*converged = 0;
return intst;
}
calculated_val = watch_pval?
calc_pval(fval, n, pmt, intst):
calc_fval(pval, n, pmt, intst);
if (fabs(val - calculated_val) > (fabs(val) / 1000.0)) {
*converged = 0;
return intst;
}
*converged = 1;
return intst;
}
if (fabs(upper_guess - lower_guess) < 1.0e-15) {
*converged = 0;
break;
}
/* need a new guess ... */
if (calculated_val < val)
upper_guess = guess;
else
lower_guess = guess;
count++;
newguess = (upper_guess + lower_guess) / 2.0;
if (fabs(newguess - guess) < 0.000001)
break;
guess = newguess;
}
/* Binary chop failed, go back to initial guess in desperation */
guess = init_guess(fval, pval, n, pmt);
intst = newton_raphson_interest(fval, pval, n, pmt, guess, max_guess, converged);
if (!*converged) {
return intst;
}
calculated_val = watch_pval?
calc_pval(fval, n, pmt, intst):
calc_fval(pval, n, pmt, intst);
if (fabs(val - calculated_val) > (fabs(val) / 1000.0)) {
*converged = 0;
return intst;
}
*converged = 1;
return intst;
}
int
exec_intst() {
double tempxf;
double n, pval, pmt, fval, intst;
int converged;
if (mode == PROG)
return 0;
if (isInverted()) {
pushf(regf[FIN_INT_REG]);
dispnums();
msg("Interest recalled");
} else if (!last_was_fin_key) {
save_x();
tempxf = popf();
pushf(tempxf);
if (tempxf < 0.0) {
msg("Interest cannot be < 0");
return 0;
}
regf[FIN_INT_REG] = tempxf;
dispreg(FIN_INT_REG);
dispnums();
last_was_fin_key = TRUE;
msg("Interest (%) entered");
} else {
n = regf[FIN_NUM_REG];
if (n <= 0.0) {
msg("# payments must be > 0");
return 0;
}
pval = regf[FIN_PVAL_REG];
pmt = regf[FIN_PMT_REG];
fval = regf[FIN_FVAL_REG];
intst = 0.0;
if (fabs(fval + pval + (n + finPayAt0)*pmt) == 0.0) {
tempxf = 0.0;
converged = 1;
} else {
/* use Newton-Raphson iteration ...
if we are seeking roots of f(x)=0 then
x_n+1 = x_n - f(x_n) / f'(x_n)
The tricky part is the initial guess. The relationship
is very unstable in 'i' and you have to get quite close
to have a chance of converging.
*/
intst = calc_intst(fval, pval, n, pmt, &converged);
}
if (converged) {
tempxf = intst * 100.0; /* Convert to percent */
pushf(tempxf);
regf[FIN_INT_REG] = tempxf;
dispreg(FIN_INT_REG);
dispnums();
msg("Interest (%) computed");
} else
msg("Calculation does not converge!\007");
}
return 0;
}
int
exec_nfin() {
double tempxf;
double interest, pval, pmt, fval;
if (mode == PROG)
return 0;
if (isInverted()) {
pushf(regf[FIN_NUM_REG]);
dispnums();
msg("# payments recalled");
} else if (!last_was_fin_key) {
save_x();
tempxf = popf();
pushf(tempxf);
if (tempxf <= 0.0) {
msg("# payments must be > 0");
return 0;
}
regf[FIN_NUM_REG] = tempxf;
dispreg(FIN_NUM_REG);
dispnums();
last_was_fin_key = TRUE;
msg("# payments entered");
} else {
interest = regf[FIN_INT_REG] / 100.0;
if (interest < 0.0) {
msg("Interest must be >= 0");
return 0;
}
pval = regf[FIN_PVAL_REG];
pmt = regf[FIN_PMT_REG];
fval = regf[FIN_FVAL_REG];
tempxf = calc_n(fval, pval, pmt, interest);
if (tempxf > 0) {
pushf(tempxf);
regf[FIN_NUM_REG] = tempxf;
dispreg(FIN_NUM_REG);
dispnums();
msg("# payments computed");
}
}
return 0;
}
int
exec_fclr() {
int c;
isInverted();
if (mode == PROG)
return 0;
c = dialog("Clear all finance registers?" DIALOG_LETTERS);
if (toupper(c) == 'Y') {
int i;
for (i=5; i< 10; i++)
regf[i] = 0;
msg("Fin registers cleared");
}
return 0;
}
int
exec_begin() {
isInverted();
if (mode == PROG)
return 0;
if (finPayAt0) {
finPayAt0 = 0;
msg("Annuity in arrears");
} else {
finPayAt0 = 1;
msg("Annuity in advance");
}
return 0;
}
int
exec_dys() {
double tempxf, tempyf;
if (mode == PROG)
return 0;
save_x();
tempxf = popf();
tempyf = popf();
if (isInverted()) {
tempyf = dayFunction30(tempxf,1);
tempxf = dayFunction30(tempyf,2) - tempyf;
} else {
tempxf = dayFunction(tempyf) - dayFunction(tempxf);
}
msg("days between two dates calculated");
if (decplaces < 4)
decplaces = 4;
if (floatmode != FIX)
exec_fix();
pushf(tempxf);
dispnums();
return 0;
}
int
exec_tdy() {
double tempxf;
struct tm *tmbuf;
time_t t;
isInverted();
if (mode == PROG)
return 0;
save_x();
t = time(NULL);
tmbuf = localtime(&t);
tempxf = tmbuf->tm_year + 1900;
tempxf += (tmbuf->tm_mon + 1) / 100.0;
tempxf += tmbuf->tm_mday / 10000.0;
pushf(tempxf);
if (decplaces != 4)
decplaces = 4;
if (floatmode != FIX)
exec_fix();
dispnums();
msg("Today's date");
return 0;
}
int
exec_times12() {
double tempxf;
if (isInverted())
return exec_divide12();
if (mode == PROG)
return 0;
save_x();
tempxf = popf();
tempxf *= 12.0;
pushf(tempxf);
dispnums();
return 0;
}
int
exec_divide12() {
double tempxf;
if (isInverted())
return exec_times12();
if (mode == PROG)
return 0;
save_x();
tempxf = popf();
tempxf /= 12.0;
pushf(tempxf);
dispnums();
return 0;
}
static void process_digit(COMMAND c) {
switch (c) {
case BACKSPACE:
case '0':
case '1':
echo_char(c);
break;
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
if (mode == PROG && intmode == BIN)
put_a_char(BELL);
else
echo_char(c);
break;
case '8':
case '9':
if ((mode == PROG) && ((intmode == BIN) || (intmode == OCT)))
put_a_char(BELL);
else
echo_char(c);
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'F':
if (mode != PROG || intmode != HEX)
put_a_char(BELL);
else
echo_char(c);
break;
case 'E':
if (mode == PROG) {
if (intmode == HEX) {
echo_char(c);
} else {
put_a_char(BELL);
}
} else {
if (strchr(inbuf, 'E') == NULL) {
if (strlen(inbuf) == 0)
echo_char('1');
echo_char(c);
} else {
msg("Can't put another E in the number!");
put_a_char(BELL);
}
}
break;
case '.':
if (mode == PROG) { /* ASCIIM never reaches here! */
if (intmode == IP) {
echo_char('.');
} else {
put_a_char(BELL);
}
} else if (entering && strchr(inbuf, '.'))
put_a_char(BELL);
else
echo_char(c);
break;
default:
put_a_char(BELL);
break;
}
}
/* trim all white space from buf (for units) */
void trim(char *s) {
char *d = s;
while (*s) {
if (!isspace(*s))
*d++ = *s;
s++;
}
*d = 0;
}
void convertX(char *from, char *to) {
#ifdef unix
int fd[2], pid;
#endif
if (mode == PROG) {
msg("No conversions in PROG mode");
return;
}
trim(from);
trim(to);
stop_entering();
#ifdef unix
if (pipe(fd) < 0)
msg("Can't open a pipe.");
else {
double oldX = xfReg;
char *argv[5];
char fromarg[80];
if (fabs(oldX) < 0.0001)
oldX = 1.0;
sprintf(fromarg, "%f %s", oldX, from);
argv[0] = "gunits";
argv[1] = "-o %.20g";
argv[2] = fromarg;
argv[3] = to;
argv[4] = 0;
if ((pid = vfork()) == -1) {
msg("Fork failed.");
} else if (pid == 0) { /* child - put stdout on pipe */
close(fd[0]); /* child does not need to read from pipe */
close(1); /* set stdout to the pipe */
dup(fd[1]);
close(fd[1]);
execvp(argv[0], argv);
_exit(1); /* abandon hope all ye who get here */
} else { /* PARENT - read from pipe */
FILE *p;
close(fd[1]); /* parent does not write to pipe */
if ((p = fdopen(fd[0], "r")) == NULL) {
msg("Can't read from pipe");
} else {
double newX;
int numRead = 0, status, bytesRead = 0;
char inbuf[80];
while (fgets(inbuf, 80, p) != NULL) {
bytesRead += strlen(inbuf);
if (!numRead)
numRead = sscanf(inbuf, " * %lf", &newX);
}
wait(&status);
if (numRead == 1 &&
WIFEXITED(status) &&
WEXITSTATUS(status) == 0) {
double xf;
xf = popf();
pushf(newX);
if (fabs(xf) < 0.0001)
sprintf(inbuf, "Conversion factor %s to %s", from, to);
else
sprintf(inbuf, "Converted %s to %s", from, to);
msg(inbuf);
dispnums();
} else if (bytesRead)
/* Note: if you are tempted to catch units's error
message be aware that as of version 1.55 it
does not use stderr! */
msg("Bad units");
else
msg("Can't find the gunits program - please install it!");
fclose(p); /* no need to close fd[1] too */
}
}
}
#else
msg("Conversions not supported here");
#endif
}
/* void storeOp(int op, int i) { */
static void
stoplus(int i) {
if ((i >= 0) && (i < NUMREGS)) {
char buf[80], *s;
if (mode == PROG)
reg[i] += xiReg;
else
regf[i] += xfReg;
strcpy(buf, "Added to R");
dispreg(i);
s = buf + strlen(buf);
sprintf(s, "%d", i);
msg(buf);
}
}
static void
stominus(int i) {
if ((i >= 0) && (i < NUMREGS)) {
char buf[80], *s;
if (mode == PROG)
reg[i] -= xiReg;
else
regf[i] -= xfReg;
strcpy(buf, "Subtracted from R");
dispreg(i);
s = buf + strlen(buf);
sprintf(s, "%d", i);
msg(buf);
}
}
static void
stotimes(int i) {
if ((i >= 0) && (i < NUMREGS)) {
char buf[80], *s;
if (mode == PROG)
reg[i] *= xiReg;
else
regf[i] *= xfReg;
strcpy(buf, "Multiplied into R");
dispreg(i);
s = buf + strlen(buf);
sprintf(s, "%d", i);
msg(buf);
}
}
static void
stodivide(int i) {
if ((i >= 0) && (i < NUMREGS)) {
char buf[80], *s;
if (mode == PROG)
reg[i] /= xiReg;
else
regf[i] /= xfReg;
strcpy(buf, "Divided into R");
dispreg(i);
s = buf + strlen(buf);
sprintf(s, "%d", i);
msg(buf);
}
}
int
exec_store() {
int i, j;
isInverted();
last_was_fin_key = 0;
stop_entering();
i = store("Store in register [0-9] (or [+-*/] reg.)");
j = i;
if ((j=='+') || (j=='-') || (j=='*') || (j=='/')) {
char buf[80];
sprintf(buf,"Store %c in register [0-9]", j);
i = store(buf);
if (isdigit(i))
i -= '0';
else
return 0;
} else {
j = 0;
if (isdigit(i))
i -= '0';
else
return 0;
}
stop_entering();
if ((i >= 0) && (i < NUMREGS)) {
char buf[80], *s;
switch (j) {
case '+': stoplus(i); break;
case '-': stominus(i); break;
case '*': stotimes(i); break;
case '/': stodivide(i); break;
case 0:
if (mode == PROG)
reg[i] = xiReg;
else
regf[i] = xfReg;
strcpy(buf, "Stored in R");
dispreg(i);
s = buf + strlen(buf);
sprintf(s, "%d", i);
msg(buf);
break;
}
}
return 0;
}
int
exec_stoplus() {
int i;
isInverted();
last_was_fin_key = 0;
stop_entering();
i = store("Store + in register [0-9]");
if (isdigit(i))
i -= '0';
else
return 0;
stop_entering();
stoplus(i);
return 0;
}
int
exec_stominus() {
int i, j;
isInverted();
last_was_fin_key = 0;
stop_entering();
i = store("Store in register [0-9] (or [+-*/] reg.)");
j = i;
if ((j=='+') || (j=='-') || (j=='*') || (j=='/'))
i = store("Store in register [0-9]");
else {
j = 0;
if (isdigit(i))
i -= '0';
else
return 0;
}
stop_entering();
stominus(i);
return 0;
}
int
exec_stomultiply() {
int i, j;
isInverted();
last_was_fin_key = 0;
stop_entering();
i = store("Store in register [0-9] (or [+-*/] reg.)");
j = i;
if ((j=='+') || (j=='-') || (j=='*') || (j=='/'))
i = store("Store in register [0-9]");
else {
j = 0;
if (isdigit(i))
i -= '0';
else
return 0;
}
stop_entering();
stotimes(i);
return 0;
}
int
exec_stodivide() {
int i, j;
isInverted();
last_was_fin_key = 0;
stop_entering();
i = store("Store in register [0-9] (or [+-*/] reg.)");
j = i;
if ((j=='+') || (j=='-') || (j=='*') || (j=='/'))
i = store("Store in register [0-9]");
else {
j = 0;
if (isdigit(i))
i -= '0';
else
return 0;
}
stop_entering();
stodivide(i);
return 0;
}
int
exec_rolldown() {
long tempx;
double tempxf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG) {
tempx = pop();
tiReg = tempx;
} else {
tempxf = popf();
tfReg = tempxf;
}
dispnums();
return 0;
}
int
exec_clx() {
long tempx;
double tempxf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG) {
liReg = pop();
tempx = 0;
push(tempx);
} else {
lfReg = popf();
tempxf = 0.0;
pushf(tempxf);
}
lift_needed = FALSE;
dispnums();
return 0;
}
/* Clear stack (INV = clear Registers) */
int
exec_clr() {
int c;
if (isInverted()) {
c = dialog("Clear all registers?" DIALOG_LETTERS);
if (toupper(c) == 'Y') {
int i;
for (i=0; i< NUMREGS; i++)
if (mode == PROG)
reg[i] = 0;
else
regf[i] = 0;
msg("Registers cleared");
}
} else {
last_was_fin_key = 0;
if (mode == PROG) {
xiReg = yiReg = ziReg = tiReg = liReg = 0;
} else {
xfReg = yfReg = zfReg = tfReg = lfReg = 0;
}
lift_needed = FALSE;
dispnums();
}
return 0;
}
int
exec_toggle_deg() {
isInverted();
last_was_fin_key = 0;
degree = !degree;
print_deg();
return 0;
}
int
exec_degree() {
isInverted();
last_was_fin_key = 0;
degree = 1;
print_deg();
msg("Angles are in degrees");
return 0;
}
int
exec_radian() {
isInverted();
last_was_fin_key = 0;
degree = 0;
print_deg();
msg("Angles are in radians");
return 0;
}
int
exec_e() {
double tempxf;
isInverted();
last_was_fin_key = 0;
tempxf = exp(1.0);
pushf(tempxf);
dispnums();
return 0;
}
int
exec_pi() {
long tempx;
double tempxf;
isInverted();
last_was_fin_key = 0;
tempxf = pi;
tempx = tempxf;
pushf(tempxf);
dispnums();
return 0;
}
int
exec_enter() {
long tempx;
double tempxf;
isInverted();
last_was_fin_key = 0;
if (mode == PROG) {
tempx = pop();
push(tempx);
push(tempx);
} else {
tempxf = popf();
pushf(tempxf);
pushf(tempxf);
}
lift_needed = FALSE;
dispnums();
return 0;
}
#ifdef HAS_ALGEBRAIC_MODE
/* this is for algebraic method only - it all gets popped up one register */
static void
saveStack() {
xfSave = xfReg;
yfSave = xfReg;
zfSave = yfReg;
tfSave = zfReg;
ufSave = tfReg;
lfSave = lfReg;
xSave = xiReg;
ySave = xiReg;
zSave = yiReg;
tSave = ziReg;
uSave = tiReg;
lSave = liReg;
}
int
exec_rpn() {
isInverted();
last_was_fin_key = 0;
algebraic_mode = 0;
return 0;
}
int
exec_algebraic() {
isInverted();
last_was_fin_key = 0;
algebraic_mode = 1;
return 0;
}
int
exec_eval() {
char expr[80];
isInverted();
last_was_fin_key = 0;
if ((eval(last_eval, expr) == 0)) {
stop_entering();
strcpy(last_eval, expr);
parserPointer = last_eval;
saveStack();
yyparse();
dispnums();
}
return 0;
}
int
exec_equals() {
isInverted();
/* last_was_fin_key = 0; */
lift_needed = FALSE;
if (strlen(last_eval) == 0) {
return 0;
} else { /* add a missing closing brace: */
int leftBraces = 0, rightBraces = 0;
char *s = last_eval;
while (s && *s && (s = strchr(s, '('))) {
leftBraces++;
s++;
}
s = last_eval;
while (s && *s && (s = strchr(s, ')'))) {
rightBraces++;
s++;
}
while (rightBraces < leftBraces) {
strcat(last_eval, ")");
rightBraces++;
}
}
parserPointer = last_eval;
saveStack();
yyparse();
dispnums();
return 0;
}
int
exec_leftbrace() {
isInverted();
last_was_fin_key = 0;
lift_needed = FALSE;
dispnums();
return 0;
}
int
exec_rightbrace() {
isInverted();
last_was_fin_key = 0;
lift_needed = FALSE;
dispnums();
return 0;
}
#endif
int
exec_lastx() {
isInverted();
last_was_fin_key = 0;
if (mode == PROG)
push(liReg);
else
pushf(lfReg);
dispnums();
return 0;
}
int
exec_quit() {
extern int is_resident;
int c = QUIT;
isInverted();
last_was_fin_key = 0;
if (is_resident) {
c = QUIT;
} else {
#ifndef HAVE_CONFIG_H /* gtk & EBOOKMAN */
c = dialog("Really quit?" DIALOG_LETTERS);
if (toupper(c) == 'Y') {
c = QUIT;
terminate_dcalc();
} else
c = 0;
clear_msg();
#else
terminate_dcalc();
#endif
}
return c;
}
/* For evaluation in algebraic expressions */
int
exec_rcl() {
int i;
i = (mode == PROG)? pop(): popf();
(mode == PROG)? push(reg[i]): pushf(regf[i]);
return 0;
}
int
exec_recall() {
int i;
isInverted();
last_was_fin_key = 0;
i = recall("Recall from register" DIALOG_NUMBERS);
if (isdigit(i)) {
i -= '0';
if ((i >= 0) && (i < NUMREGS)) {
char buf[80];
#ifdef HAS_ALGEBRAIC_MODE
if (algebraic_mode) {
sprintf(buf, "RCL(%d)", i);
add_x(buf);
} else
#endif
{
(mode == PROG)? push(reg[i]): pushf(regf[i]);
dispnums();
}
sprintf(buf, "Recalled from R%d", i);
msg(buf);
} else
msg("index out of bounds");
}
return 0;
}
int
exec_xny() {
long tempx, tempy;
double tempxf, tempyf;
isInverted();
stop_entering();
last_was_fin_key = 0;
if (mode == PROG) {
tempx = pop();
tempy = pop();
push(tempx);
push(tempy);
} else {
tempxf = popf();
tempyf = popf();
pushf(tempxf);
pushf(tempyf);
}
dispnums();
return 0;
}
int
exec_xnl() {
long tempx;
double tempxf;
isInverted();
stop_entering();
last_was_fin_key = 0;
if (mode == PROG) {
tempx = xiReg;
xiReg = liReg;
liReg = tempx;
} else {
tempxf = xfReg;
xfReg = lfReg;
lfReg = tempxf;
}
dispnums();
return 0;
}
int
exec_xnt() {
long tempx;
double tempxf;
isInverted();
stop_entering();
last_was_fin_key = 0;
if (mode == PROG) {
tempx = xiReg;
xiReg = tiReg;
tiReg = tempx;
} else {
tempxf = xfReg;
xfReg = tfReg;
tfReg = tempxf;
}
dispnums();
return 0;
}
int
exec_xnz() {
long tempx;
double tempxf;
isInverted();
stop_entering();
last_was_fin_key = 0;
if (mode == PROG) {
tempx = xiReg;
xiReg = ziReg;
ziReg = tempx;
} else {
tempxf = xfReg;
xfReg = zfReg;
zfReg = tempxf;
}
dispnums();
return 0;
}
int
exec_help() {
isInverted();
last_was_fin_key = 0;
pop_up_help();
return 0;
}
int
exec_reg() {
isInverted();
last_was_fin_key = 0;
pop_up_reg();
return 0;
}
int
exec_inv() {
last_was_fin_key = 0;
invert = !invert;
print_inv();
clear_msg();
return 0;
}
int process(COMMAND c) {
clear_msg();
#ifdef HAS_ALGEBRAIC_MODE
if (algebraic_mode)
if (c >= ' ' && c <= '~') {
last_was_fin_key = 0;
echo_char(c);
return 0;
} /* else a command */
#endif
/* ASCII MODE - almost any character can be echo'ed */
if ((c < MIN_COMMAND) && (mode == PROG && intmode == ASCIIM)) {
last_was_fin_key = 0;
echo_char(c); /* An ordinary character was typed */
return 0;
}
if (c == INV) {
exec_inv();
return 0;
}
if (c == BACKSPACE && isInverted()) {
exec_clx();
return 0;
}
if (((c >= '0') && (c <= '9')) ||
((c >= 'A') && (c <= 'F')) ||
(c == BACKSPACE) ||
(c == '.')) {
process_digit(c);
last_was_fin_key = 0;
return 0;
}
switch (c) {
case CHS: exec_chs(); break;
case PLUS: exec_plus(); break;
case MINUS: exec_minus(); break;
case DIVIDE: exec_divide(); break;
case MULT: exec_mult(); break;
case dAND: exec_and(); break;
case dOR: exec_or(); break;
case dXOR: exec_xor(); break;
case dNOT: exec_not(); break;
case MODULUS: exec_modulus(); break;
case PERCENT: exec_percent(); break;
case PERCENTCH: exec_percentch(); break;
case YTOX: exec_ytox(); break;
case SHIFTL: exec_shiftl(); break;
case SHIFTR: exec_shiftr(); break;
case SHIFTYL: exec_shiftyl(); break;
case SHIFTYR: exec_shiftyr(); break;
case PRIMEF: exec_primef(); break;
case RECI: exec_reci(); break;
case SQR: exec_sqr(); break;
case ROOT: exec_root(); break;
case CUBE: exec_cube(); break;
case CROOT: exec_croot(); break;
case PVAL: exec_pval(); break;
case FVAL: exec_fval(); break;
case PMT: exec_pmt(); break;
case INTST: exec_intst(); break;
case FCLR: exec_fclr(); break;
case NFIN: exec_nfin(); break;
case TIMES12: exec_times12(); break;
case DIVIDE12: exec_divide12(); break;
case BEGIN: exec_begin(); break;
case DYS: exec_dys(); break;
case TDY: exec_tdy(); break;
case ASCIIM: exec_asciim(); break;
case BIN: exec_bin(); break;
case IP: exec_ip(); break;
case OCT: exec_oct(); break;
case DEC: exec_dec(); break;
case HEX: exec_hex(); break;
case FIX: exec_fix(); break;
case ENG: exec_eng(); break;
case SCIFORMAT: exec_sciformat(); break;
case PLACES: exec_places(); break;
case FIN: exec_fin(); break;
case STAT: exec_stat(); break;
case SCI: exec_sci(); break;
case PROG: exec_prog(); break;
case ROLLDOWN: exec_rolldown(); break;
case CLX: exec_clx(); break;
case CLR: exec_clr(); break;
case DEGREE: exec_degree(); break;
case RADIAN: exec_radian(); break;
case E: exec_e(); break;
case PI: exec_pi(); break;
case SIN: exec_sin(); break;
case COS: exec_cos(); break;
case TAN: exec_tan(); break;
case SINH: exec_sinh(); break;
case COSH: exec_cosh(); break;
case TANH: exec_tanh(); break;
case LOGE: exec_loge(); break;
case LOG10: exec_log10(); break;
case FRC: exec_frc(); break;
case INT: exec_int(); break;
case ETOX: exec_etox(); break;
case HMS: exec_hms(); break;
case RTOP: exec_rtop(); break;
case DTOR: exec_dtor(); break;
case SUMR: exec_sumr(); break;
case SUM0: exec_sum0(); break;
case NSTAT: exec_nstat(); break;
case MEAN: exec_mean(); break;
case MEANS: exec_means(); break;
case MEANY: exec_meany(); break;
case S_DEV: exec_s_dev(); break;
case SUM: exec_sum(); break;
case FACT: exec_fact(); break;
case COMB: exec_comb(); break;
case PERM: exec_perm(); break;
case LR: exec_lr(); break;
case CALCY: exec_calcy(); break;
case ENTER: exec_enter(); break;
case LASTX: exec_lastx(); break;
case QUIT: c = exec_quit(); break;
case RECALL: exec_recall(); break;
case STORE: exec_store(); break;
case STOPLUS: exec_stoplus(); break;
case STOMINUS: exec_stominus(); break;
case STOMULTIPLY: exec_stomultiply(); break;
case STODIVIDE: exec_stodivide(); break;
case XNY: exec_xny(); break;
case XNL: exec_xnl(); break;
case XNT: exec_xnt(); break;
case XNZ: exec_xnz(); break;
case HELP: exec_help(); break;
case REGISTER: exec_reg(); break;
#ifdef HAS_ALGEBRAIC_MODE
case RPN: exec_rpn(); break;
case ALGEBRAIC: exec_algebraic(); break;
case EVAL: exec_eval(); break;
case EQUALS: exec_equals(); break;
#endif
case NOP:
break;
default:
last_was_fin_key = 0;
put_a_char(BELL);
break;
}
return c;
}
#ifndef EBOOKMAN
static void getReg(char *b) {
long l;
int i;
if (sscanf(b, "%d %ld", &i, &l))
if (i >= 0 && i < NUMREGS)
reg[i] = l;
}
static void getRegf(char *b) {
double l;
int i;
if (sscanf(b, "%d %lg", &i, &l))
if (i >= 0 && i < NUMREGS)
regf[i] = l;
}
static void getMode(char *b) {
mode =
(strcmp(b, "sci")==0) ? SCI:
(strcmp(b, "fin")==0) ? FIN:
(strcmp(b, "stat")==0) ? STAT:
(strcmp(b, "prog")==0) ? PROG: SCI;
}
static void getIntmode(char *b) {
intmode =
strcmp(b, "asci")==0? ASCIIM:
strcmp(b, "ip")==0? IP:
strcmp(b, "bin")==0? BIN:
strcmp(b, "oct")==0? OCT:
strcmp(b, "dec")==0? DEC:
strcmp(b, "hex")==0? HEX: DEC;
}
static void getFloatmode(char *b) {
floatmode =
strcmp(b, "FIX")==0? FIX:
strcmp(b, "ENG")==0? ENG:
strcmp(b, "SCI")==0? SCI: FIX;
}
#endif
static void readSettings() {
#ifdef EBOOKMAN
readGuiSettings(NULL);
return;
#else
FILE *f = NULL;
char *homeDir = NULL;
char fileName[200], buf[80];
homeDir = getenv("HOME");
if (homeDir) {
strcpy(fileName, homeDir);
if (fileName[strlen(fileName) - 1] != '/')
strcat(fileName, "/");
strcat(fileName, ".dcalcrc");
if ((f = fopen(fileName, "r")) != NULL) {
while (fgets(buf, 80, f) != NULL) {
char *b = buf, *e;
if (*b && buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0;
if (*b == '#')
continue;
while (!isspace(*b))
b++;
*b++ = 0;
if (strcmp(buf, "x") == 0) xiReg = atol(b);
if (strcmp(buf, "y") == 0) yiReg = atol(b);
if (strcmp(buf, "z") == 0) ziReg = atol(b);
if (strcmp(buf, "t") == 0) tiReg = atol(b);
if (strcmp(buf, "l") == 0) liReg = atol(b);
if (strcmp(buf, "xf") == 0) xfReg = atof(b);
if (strcmp(buf, "yf") == 0) yfReg = atof(b);
if (strcmp(buf, "zf") == 0) zfReg = atof(b);
if (strcmp(buf, "tf") == 0) tfReg = atof(b);
if (strcmp(buf, "lf") == 0) lfReg = atof(b);
if (strcmp(buf, "reg") == 0) getReg(b);
if (strcmp(buf, "regf") == 0) getRegf(b);
if (strcmp(buf, "angularMode") == 0) degree = atoi(b);
if (strcmp(buf, "places") == 0) decplaces = atoi(b);
if (strcmp(buf, "mode") == 0) getMode(b);
if (strcmp(buf, "intmode") == 0) getIntmode(b);
if (strcmp(buf, "floatmode") == 0) getFloatmode(b);
if (strcmp(buf, "gui") == 0) readGuiSettings(b);
if (strcmp(buf, "annuitymode") == 0) finPayAt0 = atol(b);
#ifdef HAS_ALGEBRAIC_MODE
if (strcmp(buf, "algebraicmode") == 0) algebraic_mode = atol(b);
if (strcmp(buf, "lasteval") == 0) {
e = ++b;
while (*e && *e != '"')
e++;
*e = 0;
strcpy(last_eval, b);
}
#endif
}
fclose(f);
}
}
#endif
}
void saveSettings(void) {
#ifdef EBOOKMAN
stop_entering();
saveGuiSettings(NULL);
return;
#else
FILE *f = NULL;
char *homeDir = NULL;
char fileName[200], buf[80];
char lFormat[] = "%s %.25lg\n";
char dFormat[] = "%s %ld\n";
char iFormat[] = "%s %d\n";
int i;
stop_entering();
homeDir = getenv("HOME");
if (homeDir) {
strcpy(fileName, homeDir);
if (fileName[strlen(fileName) - 1] != '/')
strcat(fileName, "/");
strcat(fileName, ".dcalcrc");
if ((f = fopen(fileName, "w")) != NULL) {
fprintf(f, "# %s\n", "DCALC Version " VERSION " - by Bob Hepple");
fprintf(f, "# don't edit this file - dcalc will overwrite it\n");
fprintf(f, dFormat, "x", xiReg);
fprintf(f, dFormat, "y", yiReg);
fprintf(f, dFormat, "z", ziReg);
fprintf(f, dFormat, "t", tiReg);
fprintf(f, dFormat, "l", liReg);
fprintf(f, lFormat, "xf", xfReg);
fprintf(f, lFormat, "yf", yfReg);
fprintf(f, lFormat, "zf", zfReg);
fprintf(f, lFormat, "tf", tfReg);
fprintf(f, lFormat, "lf", lfReg);
for (i = 0; i < NUMREGS; i++) {
sprintf(buf, "reg %d", i);
fprintf(f, dFormat, buf, reg[i]);
}
for (i = 0; i < NUMREGS; i++) {
sprintf(buf, "regf %d", i);
fprintf(f, lFormat, buf, regf[i]);
}
fprintf(f, iFormat, "angularMode", degree);
fprintf(f, iFormat, "places", decplaces);
fprintf(f, "mode %s\n",
mode==SCI? "sci":
mode==FIN? "fin":
mode==STAT? "stat":
mode==PROG? "prog":"sci");
fprintf(f, "intmode %s\n",
intmode==ASCIIM? "asci":
intmode==IP? "ip":
intmode==BIN? "bin":
intmode==OCT? "oct":
intmode==DEC? "dec":
intmode==HEX? "hex": "dec");
fprintf(f, "floatmode %s\n",
floatmode==FIX? "FIX": "ENG");
saveGuiSettings(f);
/* Rev 2.7 on: */
fprintf(f, "annuitymode %d\n", finPayAt0? 1: 0);
/* Rev 2.8 on: */
#ifdef HAS_ALGEBRAIC_MODE
fprintf(f, "algebraicmode %d\n", algebraic_mode? 1: 0);
fprintf(f, "lasteval \"%s\"\n", last_eval);
#endif
fclose(f);
}
}
#endif
}
void initialise(void) {
int i;
if (!was_initialised) {
was_initialised = 1;
os_init();
xiReg = yiReg = ziReg = tiReg = liReg = 0;
xfReg = yfReg = zfReg = tfReg = lfReg = 0.0;
decplaces = 2;
#if EBOOKMAN
mode = FIN;
#else
mode = SCI;
#endif
intmode = DEC;
floatmode = FIX;
entering = FALSE;
lift_needed = TRUE;
invert = FALSE;
degree = TRUE;
last_was_fin_key = FALSE;
inptr = inbuf;
*inptr = ASCIIZ;
max_digits = 20;
pi = 4.0 * atan(1.0);
d_to_r = pi / 180.0;
r_to_d = 180.0 / pi;
finPayAt0 = 0;
#ifdef HAS_ALGEBRAIC_MODE
last_eval[0] = 0;
#endif
for (i = 0; i < NUMREGS; i++) {
reg[i] = 0;
regf[i] = 0.0;
}
readSettings();
if ((mode == PROG) && (intmode == ASCIIM))
os_raw_mode(1);
}
}
extern void terminate_dcalc() {
saveSettings();
os_term();
}
/* For emacs: */
/* Local Variables: */
/* eval:(setq tab-width 8) */
/* End: */
syntax highlighted by Code2HTML, v. 0.9.1