home *** CD-ROM | disk | FTP | other *** search
- /*
-
- Plot3d -- calc.c, the function evaluation code
-
- By Karl Crary -- kc2m+@andrew.cmu.edu
-
-
- Copyright (c) 1991 by Karl Crary
-
- You may use and distribute this program as much as you like so long as you do
- not charge for this service. I am not liable for failure of this program to
- perform in any way.
-
- */
-
-
- #include "plot3d.h"
-
- int convert(char string[],char **errormsg,char stackno); /* convert infix to RPN in fstack */
- void process(char operator,unsigned char *findex,char cstack[],char *cstacktop,term fstack[]);
- char precedence(char operator, char push);
- void simplify(char stackno);
- double gamma(double arg);
-
- extern char stop;
- char ferr;
-
- double ff(double arg1, double arg2, char stackno)
- {
- static unsigned char begin = 0;
- long double stack[25], intermediate;
- unsigned char findex;
- char height, ii, recurse;
- if ((arg1 < -MAXDOUBLE) || (arg1 > MAXDOUBLE)) {
- ferr = 1;
- return(0);
- }
- if (range(-MINDOUBLE,arg1,MINDOUBLE)) arg1 = 0;
- ferr = 0;
- height = recurse = 0;
- if (begin) recurse = 1;
-
- for ((findex = begin),(begin = 0); fstack[stackno][findex].operator != '='; findex++) {
- if (!stop && kbhit()) {
- ii = getch();
- if (ii == ' ') stop = 2;
- if (ii == 24) terminate();
- if (ii == 27) {
- stop = 1;
- return(0);
- }
- }
- switch (fstack[stackno][findex].operator) {
- case '#' :
- if (height >= 25) {
- ferr = 1;
- break;
- }
- for (ii = height; ii > 0; ii--) stack[ii] = stack[ii-1];
- height++;
- stack[0] = fstack[stackno][findex].operand;
- break;
- case 'y' :
- if (height >= 25) {
- ferr = 1;
- break;
- }
- for (ii = height; ii > 0; ii--) stack[ii] = stack[ii-1];
- height++;
- stack[0] = arg2;
- break;
-
- case 'x' :
- if (height >= 25) {
- ferr = 1;
- break;
- }
- for (ii = height; ii > 0; ii--) stack[ii] = stack[ii-1];
- height++;
- stack[0] = arg1;
- break;
- case '^' :
- if (((stack[1] == 0) && (stack[0] <= 0)) || ((stack[1] < 0) && (stack[0] != floor(stack[0])))) ferr = 1;
- else intermediate = pow(stack[1],stack[0]);
- if (intermediate == HUGE_VAL) ferr = 1;
- else stack[0] = intermediate;
- goto popstack;
- case '\\' : /* root */
- if ((stack[0] == 0) && (stack[1] <= 0)) ferr = 1; /* division by zero */
- if (stack[1] == 0) ferr = 1; /* zeroth root */
- if ((stack[0] < 0) && (floor((stack[1]+1)/2) != (stack[1]+1)/2)) ferr = 1;
- if (!ferr) stack[0] = (stack[0] < 0 ? -1 : 1)*pow(fabs(stack[0]),1/stack[1]);
- goto popstack;
- case '*' :
- stack[0] = stack[0] * stack[1];
- goto popstack;
- case '/' :
- if (stack[0] == 0) ferr = 1;
- else stack[0] = stack[1] / stack[0];
- goto popstack;
- case '+' :
- stack[0] = stack[0] + stack[1];
- goto popstack;
- case '-' :
- stack[0] = stack[1] - stack[0];
- goto popstack;
- case 'm' :
- stack[0] = -stack[0];
- break;
- case 's' :
- stack[0] = sin(stack[0]);
- break;
- case 'c' :
- stack[0] = cos(stack[0]);
- break;
- case 't' :
- stack[0] = tan(stack[0]);
- break;
- case 'S' :
- if (!range(-1,stack[0],1)) ferr = 1;
- else stack[0] = asin(stack[0]);
- break;
- case 'C' :
- if (!range(-1,stack[0],1)) ferr = 1;
- else stack[0] = acos(stack[0]);
- break;
- case 'T' :
- stack[0] = atan(stack[0]);
- break;
- case 1 : /* sinh */
- stack[0] = sinh(stack[0]);
- break;
- case 2 : /* cosh */
- stack[0] = cosh(stack[0]);
- break;
- case 3 : /* tanh */
- stack[0] = tanh(stack[0]);
- break;
- case 4 : /* invsinh */
- stack[0] = log(stack[0] + sqrt(pow(stack[0],2) + 1));
- break;
- case 5 : /* invcosh */
- intermediate = pow(stack[0],2);
- if (intermediate < 1) ferr = 1;
- else {
- intermediate = stack[0] + sqrt(intermediate - 1);
- if (intermediate <= 0) ferr = 1;
- else stack[0] = log(stack[0] + sqrt(pow(stack[0],2) - 1));
- }
- break;
- case 6 : /* invtanh */
- if (stack[0] == 1) ferr = 1;
- else {
- intermediate = (1 + stack[0])/(1 - stack[0]);
- if (intermediate <= 0) ferr = 1;
- else stack[0] = log(intermediate)/2;
- }
- break;
- case 'l' :
- if (stack[0] <= 0) ferr = 1;
- else stack[0] = log10(stack[0]);
- break;
- case 'n' :
- if (stack[0] <= 0) ferr = 1;
- else stack[0] = log(stack[0]);
- break;
- case 'a' : /* absolute value */
- stack[0] = fabs(stack[0]);
- break;
- case 'g' : /* gamma */
- stack[0] = gamma(stack[0]);
- break;
- case 'f' : /* user function */
- stack[0] = ff(stack[0],0,1);
- break;
- popstack:
- height--;
- for (ii = 1; ii < height; ii++) stack[ii] = stack[ii+1];
- break;
- }
- if ((stack[0] < -2 * MAXDOUBLE) || (stack[0] > 2 * MAXDOUBLE)) ferr = 1; /* roundoff makes MAXDOUBLE > MAXDOUBLE */
- if (ferr) return(0);
- }
- if (recurse) begin = findex; /* return where ended */
- return(stack[0]);
- }
-
-
-
- int matherr(struct exception *e)
- {
- ferr = 1;
- return(1);
- }
-
-
-
- double gamma(double arg)
- {
- double xx, total;
- char ii;
- /* magic gamma generating coefficients */
- static double coef[7] = { 0.988205891,-0.897056937, 0.918206857,
- -0.756704078, 0.482199394,-0.193527818,
- 0.035868343};
-
- if (!stop && kbhit()) {
- ii = getch();
- if (ii == ' ') stop = 2;
- if (ii == 24) terminate();
- if (ii == 27) {
- stop = 1;
- return(0);
- }
- }
- if (arg <= 0) {
- if (arg == floor(arg)) {
- ferr = 1;
- return(0);
- }
- else {
- arg = -arg + 1; /* recurse if x < 0 */
- return(M_PI/(gamma(arg)*sin(M_PI*arg)));
- }
- }
- else if (arg < 1) return(gamma(arg+1)/arg); /* recurse if x < 1 */
- else if (arg <= 2) {
- arg--;
- total = 1 - 0.577191652 * arg;
- xx = arg;
- for (ii = 0; ii <= 6; ii++) {
- xx *= arg;
- total += coef[ii] * xx;
- }
- return(total);
- }
- else { /* arg > 2 */
- xx = arg * arg; /* another approximation for larger x */
- total = (arg - 0.5) * log(arg);
- total -= arg;
- total += log(2 * M_PI) / 2;
- total += 1 / (12 * arg);
- total -= 1 / (360 * arg * xx);
- total += 1 / (1260 * arg * xx * xx);
- total -= arg / (1680 * xx * xx * xx * xx);
- return(exp(total));
- }
- }
-
-
-
-
-
- /* errormessages */
- char errorlist[6][45] = {"Values must precede each binary operator.",\
- "Unmatched right parenthesis.",\
- "Expression must end with value.",\
- "Stack overflow.",\
- "Function ends in operator.",\
- "Unbalanced parentheses."};
-
-
-
- int convert(char string[], char **errormsg, char stackno)
- {
- unsigned char findex;
- char *cursor, value, inverse;
- char cstack[100], cstacktop;
- #define PROCESS(operator) process((operator),&findex,cstack,&cstacktop,fstack[stackno])
-
- cursor = string;
- findex = value = inverse = 0;
- cstack[0] = '('; /* initialize cstack */
- cstacktop = 0;
- while (*cursor != 0) {
- switch (*cursor) {
- case '=' : /* explicit definition */
- cursor++;
- findex = value = inverse = 0; /* discard everything and start over */
- cstack[0] = '(';
- cstacktop = 0;
- break;
- case 'h' : /* hyperbolics already taken care of */
- cursor++;
- break;
- case '0' : /* numbers */
- case '1' :
- case '2' :
- case '3' :
- case '4' :
- case '5' :
- case '6' :
- case '7' :
- case '8' :
- case '9' :
- case '.' :
- if (value) PROCESS('$'); /* process implicit multiplication */
- fstack[stackno][findex].operator = '#'; /* term is a number */
- fstack[stackno][findex].operand = strtod(cursor,&cursor); /* store number */
- findex++;
- value = 1;
- break;
- case 'x' : /* variables */
- case 'y' :
- case 'o' : /* theta */
- case 'r' :
- case 'u' :
- case 'p' : /* pi */
- case 'e' : /* e */
- case '!' : /* infinity */
- if (value) PROCESS('$');
- switch (*cursor) {
- case 'p' :
- fstack[stackno][findex].operator = '#';
- fstack[stackno][findex].operand = M_PI;
- break;
- case 'e' :
- fstack[stackno][findex].operator = '#';
- fstack[stackno][findex].operand = M_E;
- break;
- case '!' :
- fstack[stackno][findex].operator = '#';
- fstack[stackno][findex].operand = MAXDOUBLE;
- break;
- case 'y' :
- fstack[stackno][findex].operator = 'y';
- break;
- default : /* variables */
- fstack[stackno][findex].operator = 'x';
- break;
- }
- findex++;
- cursor++;
- value = 1;
- break;
- case '^' : /* operators */
- case '\\' :
- case '*' :
- case '/' :
- case '+' :
- case '-' :
- if (!value) { /* process unary operators */
- if (*cursor == '+') { /* unary plus, do nothing */
- cursor++;
- break;
- }
- else if (*cursor == '-') { /* unary minus */
- PROCESS('m');
- cursor++;
- break;
- }
- if (errormsg != NULL) (*errormsg) = errorlist[0];
- return(cursor-string); /* return location of error */
- }
- PROCESS(*cursor); /* process operator */
- cursor++;
- value = 0;
- break;
- case '(' :
- if (value) PROCESS('$'); /* process implicit multiplication */
- PROCESS('('); /* process paren */
- cursor++;
- value = 0;
- break;
- case ')' :
- if (!value) { /* expression ends in operator */
- if (errormsg != NULL) *errormsg = errorlist[2];
- return(cursor-string);
- }
- PROCESS(')');
- cstacktop--; /* zap open paren */
- if (cstacktop < 0) {
- if (errormsg != NULL) (*errormsg) = errorlist[1];
- return(cursor-string); /* unbalanced parens */
- }
- cursor++;
- value = 1;
- break;
- case 'd' :
- if (value) PROCESS('$'); /* process implicit multiplication */
- fstack[stackno][findex].operator = 'd'; /* mark start of derivative */
- findex++;
- PROCESS('='); /* process end of derivative */
- cursor++; /* will be followed by parenthesis */
- value = 0;
- break;
- case 'A' :
- cursor++;
- /* inverse of non-trig prevented in IO */
- inverse = 32;
- case 's' :
- case 'c' :
- case 't' :
- if (value) PROCESS('$'); /* process implicit multiplication */
- if (cursor[1] != 'h') PROCESS(*cursor - inverse); /* inverse is lowercase */
- else switch (*cursor) {
- case 's' :
- PROCESS(inverse ? 4 : 1);
- break;
- case 'c' :
- PROCESS(inverse ? 5 : 2);
- break;
- case 't' :
- PROCESS(inverse ? 6 : 3);
- break;
- } /* advance cursor on next pass */
- cursor++;
- value = inverse = 0;
- break;
- case ' ' :
- cursor++;
- break;
- default : /* functions */
- if (value) PROCESS('$'); /* process implicit multiplication */
- PROCESS(*cursor);
- cursor++;
- value = 0;
- break;
- }
- if ((cstacktop >= 100) || (findex >= 99)) {
- if (errormsg != NULL) *errormsg = errorlist[3];
- return(cursor-string); /* stack overflow */
- }
- }
- if (!value) {
- if (errormsg != NULL) *errormsg = errorlist[4];
- return(cursor-1-string); /* function ends with operator, return location error */
- }
- PROCESS(')'); /* flush conversion stack */
- if (cstacktop != 0) { /* unbalanced parens */
- if (errormsg != NULL) *errormsg = errorlist[5];
- return(cursor-string);
- }
- if (findex >= 99) {
- if (errormsg != NULL) *errormsg = errorlist[3];
- return(cursor-string); /* stack overflow or unbalanced parens */
- }
- fstack[stackno][findex].operator = '=';
- return(-1);
- }
-
- void process(char operator,unsigned char *findex,char cstack[],char *cstacktop,term fstack[])
- {
- char pushprec;
- pushprec = precedence(operator,1);
- while (precedence(cstack[*cstacktop],0) >= pushprec) {
- if (cstack[*cstacktop] == '$') cstack[*cstacktop] = '*';
- fstack[*findex].operator = cstack[*cstacktop];
- if (++(*findex) >= 99) break; /* stack overflow */
- (*cstacktop)--;
- }
- if ((operator != ')') && (*cstacktop < 99)) {
- (*cstacktop)++;
- cstack[*cstacktop] = operator;
- }
- }
-
- char precedence(char operator, char push)
- {
- switch (operator) {
- case '(' :
- return(push ? 10 : 0);
- case ')' :
- return(1);
- case '=' : /* end of derivative */
- return(9);
- case '$' : /* implicit multiplication */
- return(6);
- case '\\' :
- case '^' :
- return(push ? 7 : 5);
- case '*' :
- case '/' :
- return(3);
- case '+' :
- case '-' :
- return(2);
- default : /* functions */
- return(push ? 8 : 4);
- }
- }
-
- void simplify(char stackno)
- {
- int ii, jj, restart;
- char last, prevlast;
- restart = -1;
- last = prevlast = 0;
- for (ii = 0; fstack[stackno][ii].operator != '='; ii++) switch (fstack[stackno][ii].operator) {
- case '+' : /* binary operators */
- case '-' :
- case '/' :
- case '*' :
- case '\\' :
- case '^' :
- if (restart == ii - 1) {
- restart = ii;
- break;
- }
- if (last && prevlast) {
- /* load auxiliary function to calculate simplified value */
- fstack[maxffs][0].operator = fstack[stackno][ii-2].operator; /* might not be # */
- fstack[maxffs][0].operand = fstack[stackno][ii-2].operand;
- fstack[maxffs][1].operator = fstack[stackno][ii-1].operator; /* might not be # */
- fstack[maxffs][1].operand = fstack[stackno][ii-1].operand;
- fstack[maxffs][2].operator = fstack[stackno][ii].operator;
- fstack[maxffs][3].operator = '=';
- /* simplify stack */
- ii -= 2; /* update stack position */
- fstack[stackno][ii].operand = ff(0,0,maxffs); /* calculate new value */
- fstack[stackno][ii].operator = '#';
- for (jj = ii+1; fstack[stackno][jj+1].operator != '='; jj++) { /* shift stack left by 2 */
- fstack[stackno][jj].operator = fstack[stackno][jj+2].operator;
- fstack[stackno][jj].operand = fstack[stackno][jj+2].operand;
- }
- last = prevlast = 0;
- ii = restart; /* restart */
- continue;
- }
- else last = 0; /* prevlast irrelevant */
- break;
- case '#' :
- case 'p' :
- case 'e' :
- case '!' :
- prevlast = last;
- last = 1;
- break;
- case 'x' :
- case 'y' :
- restart = ii; /* no need to restart before unsimplifyable character */
- last = 0; /* prevlast irrelevant */
- break;
- case 'm' :
- if (fstack[stackno][ii-1].operator == 'm') { /* nuke double negate */
- for (jj = ii-1; fstack[stackno][jj+1].operator != '='; jj++) { /* shift stack left by 2 */
- fstack[stackno][jj].operator = fstack[stackno][jj+2].operator;
- fstack[stackno][jj].operand = fstack[stackno][jj+2].operand;
- }
- last = prevlast = 0;
- ii = restart;
- continue;
- }
- /* no break */
- default : /* unary operators */
- if (restart == ii - 1) {
- restart = ii;
- break;
- }
- if (last) {
- /* load auxiliary function to calculate simplified value */
- fstack[maxffs][0].operator = fstack[stackno][ii-1].operator; /* might not be # */
- fstack[maxffs][0].operand = fstack[stackno][ii-1].operand;
- fstack[maxffs][1].operator = fstack[stackno][ii].operator;
- fstack[maxffs][2].operator = '=';
- /* simplify stack */
- ii -= 1; /* update stack position */
- fstack[stackno][ii].operand = ff(0,0,maxffs); /* calculate new value */
- fstack[stackno][ii].operator = '#';
- for (jj = ii+1; fstack[stackno][jj].operator != '='; jj++) { /* shift stack left */
- fstack[stackno][jj].operator = fstack[stackno][jj+1].operator;
- fstack[stackno][jj].operand = fstack[stackno][jj+1].operand;
- }
- last = prevlast = 0;
- ii = restart; /* restart */
- continue;
- }
- else last = 0; /* prevlast irrelevant */
- break;
- }
- }
-