/* This file handles buit in operators & functions.
Copyright (C) 1990 Marty White
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; 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 "compiler.h"
#include "physcalc.h"
#include "physdecl.h"
#ifdef __PROTOTYPES__
LOCAL void change_type(NODEP n, int type);
LOCAL NODEP derivative(NODEP n);
#endif
/*----- Miscelaneous Math related routines -----*/
EXPORT int number(n) /* is n a number? */
NODEP n;
{
return n->type!=SNODE && n->type<LNODE;
}
LOCAL void change_type(n,type) /* Convert node N to type TYPE */
NODEP n; /* Assumes conversion is 'upward', but will also */
int type; /* force conversion from any type to a double */
{
int i;
double x;
switch (n->type) {
case INODE:
switch (type) {
case FNODE:
i = (int) n->data->lng;
reallocnode(n,FNODE,sizeof(struct fractstruct));
n->data->frt.numer = i;
n->data->frt.denom = 1;
break;
case DNODE:
x = (double) n->data->lng;
reallocnode(n,DNODE,sizeof(double));
n->data->dbl = x;
break;
case NNODE:
x = (double) n->data->lng;
reallocnode(n,NNODE,sizeof(DNUM));
erasednum(&n->data->dmn);
n->data->dmn.num = x;
break;
}
break;
case FNODE:
x = (double) n->data->frt.numer / (double) n->data->frt.denom;
if (type==DNODE) {
reallocnode(n,DNODE,sizeof(double));
n->data->dbl = x;
} else {
reallocnode(n,DNODE,sizeof(DNUM));
erasednum(&n->data->dmn);
n->data->dmn.num = x;
}
break;
case DNODE:
if (type==NNODE) {
x = n->data->dbl;
reallocnode(n,NNODE,sizeof(DNUM));
erasednum(&n->data->dmn);
n->data->dmn.num = x;
}
break;
case NNODE:
if (type==DNODE) {
x = n->data->dmn.num;
reallocnode(n,DNODE,sizeof(double));
n->data->dbl = x;
}
}
}
EXPORT void force_same_type(x,y) /* Change x & y to be the same type */
NODEP x,y;
{
int rtype=INODE;
/* Determine common type (assume integer) */
if (x->type==FNODE || y->type==FNODE)
rtype=FNODE;
if (x->type==DNODE || y->type==DNODE)
rtype=DNODE;
if (x->type==NNODE || y->type==NNODE)
rtype=NNODE;
/* Now convert each to rtype */
if (x->type != rtype)
change_type(x,rtype);
if (y->type != rtype)
change_type(y,rtype);
}
EXPORT void simplify_frt(n) /* Simplify a fraction */
NODEP n;
{
int a,b,r;
if (a = n->data->frt.numer) {
b = n->data->frt.denom;
if (a<0)
a *= -1;
if (a<b) { /* Find g.c.d. using Euclid's algorithm */
r = a; /* make (a) the larger */
a = b;
b = r;
}
do {
r = a % b; /* remainder after a/b */
a = b;
b = r;
} while (r); /* until 0 remainder */
if (a>1) {
n->data->frt.numer /= a;
n->data->frt.denom /= a;
}
} else
n->data->frt.denom = 1;
if (n->data->frt.denom==1) { /* Change to int if denominator is 1 */
a = n->data->frt.numer;
reallocnode(n,INODE,sizeof(long));
n->data->lng = (long) a;
}
}
EXPORT void factor(s) /* Output all integer factors of an expression */
char const *s;
{
NODEP n;
int i,root,prime;
long a;
n = solve(&s);
if (error) {
printf("To use FACTOR type:\n FACTOR <integer value>\n");
return;
}
if (n->type != INODE) {
printf("Non-integers are un-factorable.\n");
deallocnode(n);
return;
}
a = n->data->lng;
deallocnode(n);
if (!a) {
printf("The factors of zero are zero and anything!\n");
return;
}
printf("Factors are: 1,%d,",a);
prime = TRUE;
root = (int) sqrt((double) a);
for (i=2; i<=root; i++)
if ( (a/i)*i == a) {
printf("%d,%d,",i,a/i);
prime = FALSE;
}
printf("\b \n");
if (prime)
printf("This is a prime number.\n");
else {
printf("The prime factorization is: ");
for (i=2; i<=root; i++)
if ( (a/i)*i == a) {
printf("%d,",i);
a = a/i;
i=1;
root = sqrt((double) a);
}
printf("%d\n",a);
}
}
/*----- Built in functions -----*/
/* Caller is responsible for de-allocation of parameters */
LOCAL NODEP factorial(x) /* factorial operator/function */
NODEP x;
{
int i;
long n, old_n;
if (x->type != INODE) {
error = TYPERR;
return NULL;
}
n = 1;
old_n = 1;
for (i=2; i <= x->data->lng; i++) {
n *= i;
if (n <= old_n) {
error = MTHERR;
return NULL;
}
old_n = n;
}
return allocinode(n);
}
LOCAL NODEP integrate(f,a,b,p)
/* integrate f from a to b using trapezoidal rule */
NODEP f,a,b,p; /* f should be a string; p should be a positive int or NULL */
{
double deltax,a1,b1,x,y;
NODEP n,n2,n3,na,nb,np;
static double parts = 20.0;
if (!f || !a || !b)
return NULL;
if (p) {
np = evaluate(p);
if (np->type != INODE || np->data->lng < 1L || np->data->lng>32767L) {
printf("Partitions must be an integer from 1 to 32768.\n");
deallocnode(np);
return NULL;
}
parts = (double) np->data->lng;
deallocnode(np);
}
na = evaluate(a);
if (error) {
deallocnode(na);
return NULL;
}
nb = evaluate(b);
if (error) {
deallocnode(na);
deallocnode(nb);
return NULL;
}
change_type(na,DNODE);
change_type(nb,DNODE);
a1 = na->data->dbl;
b1 = nb->data->dbl;
deallocnode(na);
deallocnode(nb);
deltax = (b1-a1) / parts;
printf("Integrating...\n Delta = %lg\n",deltax);
if (fabs(deltax) == 0.0) {
printf("Cannot integrate on a non-existent interval!\n");
return NULL;
}
if (parts>30)
printf(" (Please be patient while I think...)\n");
n = alloclnode(LNODE); /* Create a node calling f with a */
linknode(n, copynode(f) );
n2 = allocdnode(a1);
linknode(n,n2);
n3 = evaluate(n);
change_type(n3,DNODE);
y = n3->data->dbl;
for (x = a1 + deltax; x <= b1 - deltax; x += deltax) {
n2->data->dbl = x;
deallocnode(n3);
n3 = evaluate(n);
change_type(n3,DNODE);
y += 2 * n3->data->dbl;
}
n2->data->dbl = b1;
deallocnode(n3);
n3 = evaluate(n);
change_type(n3,DNODE);
y += n3->data->dbl;
y *= deltax * 0.5;
deallocnode(n);
deallocnode(n3);
return allocdnode(y);
}
LOCAL NODEP derivative(n) /* derivative function (not too useful) */
NODEP n;
{
NODEP n2,n3,n4,n5;
switch (n->type) {
case SNODE: /* Derivative of a variable is 1 */
n2 = allocinode(1L);
break;
case 0 /*NULL*/:
n2 = NULL;
break;
case INODE:
case FNODE:
case DNODE:
case NNODE: /* derivative of a constant is 0 */
n2 = allocinode(0L);
break;
case LNODE:
n2 = derivative(n->data->list[1]);
break;
default: /* Implement other rules */
switch (n->type - LNODE) {
case 1: /* Addition */
case 2: /* Subtracton */
n2 = alloclnode2(n->type, derivative(n->data->list[0]), derivative(n->data->list[1]));
break;
case 3: /* multiplication */
n2 = alloclnode(LNODE + 1); /* create addition node */
n3 = alloclnode(LNODE + 3); /* mult node */
linknode(n2,n3);
linknode(n3, derivative(n->data->list[0]) );
linknode(n3, copynode(n->data->list[1]) );
n3 = alloclnode(LNODE + 3);
linknode(n2,n3);
linknode(n3, copynode(n->data->list[0]) );
linknode(n3, derivative(n->data->list[1]) );
break;
case 4: /* division */
n2 = alloclnode(LNODE + 4);
n3 = alloclnode(LNODE + 2);
linknode(n2,n3);
n4 = alloclnode(LNODE + 3);
linknode(n3,n4);
linknode(n4, derivative(n->data->list[0]) );
linknode(n4, copynode(n->data->list[1]) );
n4 = alloclnode(LNODE + 3);
linknode(n3,n4);
linknode(n4, copynode(n->data->list[0]) );
linknode(n4, derivative(n->data->list[1]) );
n3 = alloclnode(LNODE + 5);
linknode(n2,n3);
linknode(n3, copynode(n->data->list[1]) );
linknode(n3, allocinode(2L) );
break;
case 5: /* power rule */
n2 = alloclnode(LNODE + 3);
n3 = alloclnode(LNODE + 3); /* deriv. of the outside */
linknode(n2,n3);
linknode(n3, copynode(n->data->list[1]) );
n4 = alloclnode(LNODE + 5);
linknode(n3,n4);
linknode(n4, copynode(n->data->list[0]) );
n5 = alloclnode(LNODE + 2);
linknode(n4,n5);
linknode(n5, copynode(n->data->list[1]) );
linknode(n5, allocinode(1L) );
/* times the deriv. of the inside */
linknode(n2, derivative(n->data->list[0]) );
break;
default:
error = TYPERR;
n2 = NULL;
break;
}
break;
}
n3 = evaluate(n2);
deallocnode(n2);
return n3;
}
#ifndef __PROTOTYPES__
EXPORT double (*do_funct[FUNCS])()
#else
EXPORT double (*do_funct[FUNCS])(double parm)
#endif
= {
exp,log,sqrt,sin,cos,tan,asin,acos,atan,log10
};
EXPORT char const functions[FUNCS][FNLEN] = {
"EXP","LN","SQRT","SIN","COS","TAN","ARCSIN","ARCCOS","ARCTAN","LOG"
};
EXPORT NODEP (*do_funct2[FUNCS2])() = {
factorial,integrate,derivative
};
EXPORT char const functions2[FUNCS2][FNLEN] = {
"FACT","INTEGRATE","DERIVATIVE"
};
/*---- Arithmetic operator functions ----*/
LOCAL NODEP add(x,y)
NODEP x,y;
{
NODEP z;
switch (x->type) {
case INODE:
z = allocinode(x->data->lng + y->data->lng);
break;
case FNODE:
z = allocfnode(x->data->frt.numer * y->data->frt.denom +
y->data->frt.numer * x->data->frt.denom,
x->data->frt.denom * y->data->frt.denom);
break;
case DNODE:
z = allocdnode(x->data->dbl + y->data->dbl);
break;
case NNODE:
z = allocnnode();
if (!check_equ_dim(&x->data->dmn,&y->data->dmn))
printf("Adding unlike dimensions!\n");
else
copydnum(&z->data->dmn,&x->data->dmn);
z->data->dmn.num += y->data->dmn.num;
break;
}
return z;
}
LOCAL NODEP subtract(x,y)
NODEP x,y;
{
NODEP z;
switch (x->type) {
case INODE:
z = allocinode(x->data->lng - y->data->lng);
break;
case FNODE:
z = allocfnode(x->data->frt.numer * y->data->frt.denom -
y->data->frt.numer * x->data->frt.denom,
x->data->frt.denom * y->data->frt.denom);
break;
case DNODE:
z = allocdnode(x->data->dbl - y->data->dbl);
break;
case NNODE:
z = allocnnode();
if (!check_equ_dim(&x->data->dmn,&y->data->dmn))
printf("Subtracting unlike dimensions!\n");
else
copydnum(&z->data->dmn,&x->data->dmn);
z->data->dmn.num -= y->data->dmn.num;
break;
}
return z;
}
LOCAL NODEP multiply(x,y)
NODEP x,y;
{
NODEP z;
int i;
switch (x->type) {
case INODE:
z = allocinode(x->data->lng * y->data->lng);
break;
case FNODE:
z = allocfnode(x->data->frt.numer * y->data->frt.numer,
x->data->frt.denom * y->data->frt.denom);
break;
case DNODE:
z = allocdnode(x->data->dbl * y->data->dbl);
break;
case NNODE:
z = allocnnode();
z->data->dmn.num = x->data->dmn.num * y->data->dmn.num;
for (i=0; i<MAXDIM; i++)
z->data->dmn.di[i] = x->data->dmn.di[i] + y->data->dmn.di[i];
break;
}
return z;
}
LOCAL NODEP divide(x,y) /* Should not recieve a list or string node */
NODEP x,y; /* the node types of x & y must be the same */
{ /* This is the longest of the operator functions because it has to
cope with possible division by zero */
NODEP z;
int i;
switch (x->type) {
case INODE: /* division of two integers results in a double */
if (y->data->lng==0L) {
error = MTHERR;
return NULL;
}
z = allocdnode((double) x->data->lng / (double) y->data->lng );
break;
case FNODE:
if (y->data->frt.numer == 0) {
error = MTHERR;
return NULL;
}
z = allocfnode(x->data->frt.numer*y->data->frt.denom,
y->data->frt.numer*x->data->frt.denom);
break;
case DNODE:
if (y->data->dbl==0.0) {
error = MTHERR;
return NULL;
}
z = allocdnode(x->data->dbl / y->data->dbl);
break;
case NNODE:
if (y->data->dmn.num==0.0) {
error = MTHERR;
return NULL;
}
z = allocnnode();
z->data->dmn.num = x->data->dmn.num / y->data->dmn.num;
for (i=0; i<MAXDIM; i++)
z->data->dmn.di[i] = x->data->dmn.di[i] - y->data->dmn.di[i];
break;
}
return z; /* Calling procedure should deallocate x & y */
}
LOCAL NODEP power(x,y) /* y may not be dimensioned */
NODEP x,y;
{
NODEP z;
int i;
if (y->type==NNODE) {
error = TYPERR;
return NULL;
}
change_type(y,DNODE); /* promote y to dbl */
if (x->type!=NNODE) {
change_type(x,DNODE); /* promote x to dbl */
z = allocdnode(pow(x->data->dbl,y->data->dbl));
} else {
z = allocnnode();
z->data->dmn.num = pow(x->data->dmn.num,y->data->dbl);
for (i=0; i<MAXDIM; i++)
z->data->dmn.di[i] = (int) (x->data->dmn.di[i] * y->data->dbl);
}
return z;
}
LOCAL NODEP and(x,y)
NODEP x,y;
{
if (x->type!=INODE || y->type!=INODE) {
error = TYPERR;
return NULL;
}
return allocinode(x->data->lng && y->data->lng);
}
LOCAL NODEP logical_and(x,y)
NODEP x,y;
{
if (x->type!=INODE || y->type!=INODE) {
error = TYPERR;
return NULL;
}
return allocinode(x->data->lng & y->data->lng);
}
LOCAL NODEP not(x)
NODEP x;
{
if (x->type != INODE) {
error = TYPERR;
return NULL;
}
return allocinode(!x->data->lng);
}
LOCAL NODEP logical_not(x)
NODEP x;
{
if (x->type != INODE) {
error = TYPERR;
return NULL;
}
return allocinode(~x->data->lng);
}
LOCAL NODEP or(x,y)
NODEP x,y;
{
if (x->type != INODE) {
error = TYPERR;
return NULL;
}
return allocinode(x->data->lng || y->data->lng);
}
LOCAL NODEP logical_or(x,y)
NODEP x,y;
{
if (x->type != INODE) {
error = TYPERR;
return NULL;
}
return allocinode(x->data->lng | y->data->lng);
}
LOCAL NODEP modulo(x,y)
NODEP x,y;
{
if (x->type != INODE || y->type != INODE) {
error = TYPERR;
return NULL;
}
return allocinode(x->data->lng % y->data->lng);
}
LOCAL NODEP minus(x) /* Unary minus */
NODEP x;
{
NODEP z;
switch (x->type) {
case INODE:
return allocinode(- x->data->lng);
case FNODE:
return allocfnode(- x->data->frt.numer, x->data->frt.denom);
case DNODE:
return allocdnode(- x->data->dbl);
case NNODE:
z = allocnnode();
copydnum(&z->data->dmn,&x->data->dmn);
z->data->dmn.num = - z->data->dmn.num;
return z;
}
return NULL;
}
/*-------- Operator data --------*/
EXPORT char const operator[OPS][OPLEN] = {
"Null", /* cannot match anything because of mixed upper/lower case */
"+","-","*","/","^","AND","NOT","!","OR","MOD","-",
"&&","&","~","||","|"
};
EXPORT int const priority[OPS] = {
0,
120,120,130,130,135,50,140,132,40,130,140,
50,80,140,40,60
};
/* Operator type: 0=NULL, 1=infix (binary), 2=prefix, 3=postfix */
EXPORT int const optype[OPS] = {
0,
1,1,1,1,1,1,2,3,1,1,2,
1,1,2,1,1
};
EXPORT NODEP (*do_op[OPS])() = { /* array of pointers to functions */
add, /* filler for 0 index */
add,subtract,multiply,divide,power,and,not,factorial,or,modulo,minus,
and, logical_and, logical_not, or, logical_or
};
syntax highlighted by Code2HTML, v. 0.9.1