home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Troubleshooting Netware Systems
/
CSTRIAL0196.BIN
/
attach
/
pcc
/
v08n03
/
math.exe
/
ALGED21.ZIP
/
ALGED.C
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-26
|
78KB
|
2,876 lines
/*--------------------------------------------------------------------
Alged: Algebra Editor
Copyright (c) 1994 John Henckel
Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies.
Notes;
This was written using the excellent Borland Turbo C++ 3.0 Compiler.
One of the concepts that occurs regularly in this program is the clag.
A clag is a commutative-left-associative-group. For instance,
x+y+z is a clag, but x-y-z is not, because SUB doesn't commute.
And x+(y+z) is not a clag because it is right associative. Clags are
either additive or multiplicative. You can sort, cancel, and combine
elements of a clag. You can move numbers to the bottom (front) or
top (end) of a clag. You can split and join clags by converting
MUL and ADD to/from DIV and SUB. I think most people think in terms
of clags, though they don't call them that.
Notes: The following compiler flags are required:
word alignment off, memory large, signed chars, enums as int.
These are recommended: floating point emulation, fast float, 8086 inst.
If you make any enhancements to this code, please comment them well and
make a note below. I would appreciate it if you send me your enhancements
also! There are a number of utility function you should be aware of,
look at cons, newoper, newnode, freenode, freetree, debug, dumpnode.
I added cons just lately so you may see cases where I should have used
cons but didn't.
Change log:
12/94 JDH first version henckel@vnet.ibm.com
1/95 JDH second version
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <conio.h>
#include <time.h>
#include "mouse.h"
#define xx2
#define MAXP 5
typedef unsigned char uchar;
/* Note, the order and spacing of these is very important. see Bisect */
enum KIND { EQU,FUN,ADD,SUB,MUL,DIV,EXP,VAR,NUM,BAD };
/* operator precedence (for display only) */
int pr[20]={ 1 , 2 , 3 , 3 , 4 , 5 , 6 , 9 , 9 , 9 };
uchar kname[20][3] =
{"=","?","+","-","·","/","^","?","?","?" };
uchar piname[10] = {227,0};
uchar ename[10] = "e";
uchar iname[10] = "i";
uchar errname[10] = "BAD"; /* for divide by zero etc */
uchar infname[10] = "INF";
uchar halfname[10] = {171,0};
uchar qtrname[10] = {172,0};
uchar hline = 196;
uchar vline = 179;
uchar urc = 191;
uchar llc = 192;
uchar lrc = 217;
uchar ulc = 218;
uchar comma = ',';
#ifndef __IBMC__
extern void _floatconvert(); /* force load float libs */
#pragma extref _floatconvert
#endif
/*-----------------------------------------------------------------
The following is the fundamental data structure for alged.
>> when you create a new node you need to set kind,nump,parm,
and name (or value for NUMbers).
>> tag is used to debug memory problems, if the node is allocated
then it should be 12345, else it should not be.
>> sx,sy,px,py,ay are set by the display functions and used by the
findnode. name is set for numbers only.
>> ay is used to adjust the visual appearance of vertically stacked
operations like EXP and DIV.
*/
typedef struct node {
char name[25];
int tag;
double value;
int sx,sy; /* size */
int px,py; /* location on scrn */
int ay; /* adjust y */
int kind;
int nump;
struct node *parm[MAXP],*next; /* next must be last */
} node;
/*-----------------------------------------------------------------
GLOBAL VARIABLES
There is an array of formulas read from the file. numform is the
number of them, curform is the current formula being edited.
src and tgt are two selected nodes within the curform.
*/
node *firf,*curf;
node *src,*tgt;
int sigdig = 13; /* doubles have at most 16 sig dig. */
double maxrat = 1000;
int bold1=14,bold2=13,norm=7,mcolor=15+16;
int panx=0,pany=0,yadj=1;
int maxpow=100;
int numsee = 0; /* number of formulas currently visible */
int ch8 = 1; /* allow 8-bit characters (graphical) */
struct text_info ti; /* text mode information */
enum menui {
SIM, ASS, PCO, PLY, SBS, ADZ, SUZ, CLR,
DIS, COD, PRV, QUA, EXX, MUZ, DEL, LOD,
CAL, CH8, NXT, FAP, EXJ, DIZ, INK, SAV,
RAT, PPL, PP0, PPR, EQK, EXZ, ENT, WRI, numm };
char menu[numm][10] = {
"Simplify", "Associate", "Poly Coef", "Poly Div",
"Substitut", "Add Key", "Subtr Key", "Erase All",
"Distribut", "Comm Deno", "Prev", "FactrQuad",
"^N expand", "Mult Key ", "DeleteTop", "Load",
"Calculate", "Char Mode", "Next", "FactrPoly",
"Exp Join", "Div Key ", "Ins Key", "Save",
"Integer", "<<Left", "Center", "Right>>",
"Equal Key", "Exp Key", "Enter Key", "Write" };
/* . . . . */
char hotkey[numm+1] = " ap\\u+-\x12" "dmHqn*SlcgPfj/RsiKGM=ekw";
int mwidth = 10; /* max menu item width */
int mheight = 5; /* see show_menu for calc */
/*--------------------------------------------------------------------
MACROS
*/
#define setmax(x,y) if ((x)<(y)) x=(y)
#define relxy(x,y) gotoxy(wherex()+(x),wherey()+(y))
#define PI 3.14159265358979292
#define E 2.71828182845904509
#define lf parm[0]
#define rt parm[1]
#define pause delay(500)
void debug(node*);
void simplify(node *p);
void twirl(void);
/*-----------------------------------------------------------------
memory allocation
*/
void checknull(void *p) {
if (!p) {
printf("\n*********************************************\n");
printf( "* Alged Error - heap memory is all used up. *\n");
printf( "*********************************************\n");
delay(2000);
exit(1);
}
}
node *newnode(void) {
node *p;
p = malloc(sizeof *p); checknull(p);
p->tag = 12345;
p->nump = 0;
p->kind = VAR;
strcpy(p->name,"??");
return p;
}
node *newnum(double val) {
node *p;
p = malloc(sizeof *p); checknull(p);
p->tag = 12345;
p->nump = 0;
p->kind = NUM;
p->value = val;
return p;
}
node *newoper(int kind) {
node *p;
p = malloc(sizeof *p); checknull(p);
p->tag = 12345;
p->nump = 2;
p->kind = kind;
p->lf = p->rt = NULL;
strcpy(p->name,kname[kind]);
return p;
}
void freenode(node *p) {
if (!p || p->tag != 12345) {
printf("Error in freenode");
pause; return;
}
p->tag = 0;
p->nump = 0;
p->kind = VAR;
strcpy(p->name,"FREE");
free(p);
}
void freetree(node *p) {
int i,n;
if (!p || p->tag != 12345) {
printf("Error in freetree");
pause; return;
}
n = p->nump;
p->tag = 0;
p->nump = 0;
p->kind = VAR;
strcpy(p->name,"FREE");
for (i=0; i<n; ++i)
freetree(p->parm[i]);
free(p);
}
/*-----------------------------------------------------------------
nodecpy, use this to copy one node over another. The only
thing is that "next" is preserved to not disrupt the main
node linked list.
*/
void nodecpy(node *a, node *b) {
node *nx;
nx = a->next;
memcpy(a,b,sizeof(*a));
a->next = nx;
}
/*-----------------------------------------------------------------
set a=b,
frees a, then copies b into it and frees b.
*/
void movenode(node *a, node *b) {
int i;
for (i=0; i<a->nump; ++i)
freetree(a->parm[i]); /* free children */
nodecpy(a,b);
freenode(b);
}
/*-----------------------------------------------------------------
cons -- create a new node with left,opr,right or if left is
null, just return right.
*/
node *cons(node *left,int opr,node *right) {
node *a;
if (!left) return right; /* empty list */
a = newoper(opr);
a->lf = left;
a->rt = right;
return a;
}
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The following functions are related to the manipulation of
the algebraic expressions. They fall roughly into the following
categories.
Sorting b + a ==> a + b
Factoring ac + ab ==> a(c + b)
Distributing a(b + c) ==> ac + ab
Calculating b/1 ==> b
Collecting a*a*a ==> a^3
Other
*/
/*-----------------------------------------------------------------
some macros used by simplify
*/
#define swingb do { \
p->rt = b->rt; \
b->rt = b->lf; \
b->lf = p->lf; \
p->lf = b; } while(0)
#define swinga do { \
p->lf = a->lf; \
a->lf = a->rt; \
a->rt = p->rt; \
p->rt = a; } while(0)
#define aop(x) ((x)==ADD || (x)==SUB)
#define whole(x) (fmod((x),1)==0)
/*-----------------------------------------------------------------
deepcopy a node
*/
node *deepcopy(node *p) {
int j;
node *t;
t = newnode();
nodecpy(t,p);
for (j=0; j<p->nump; ++j)
t->parm[j] = deepcopy(p->parm[j]);
return t;
}
/*-----------------------------------------------------------------
compare two nodes, if they are equal return 1, else 0.
*/
int equal(node *p,node *q) {
int i;
if (p->kind != q->kind) return 0;
if (p->nump != q->nump) return 0;
if (p->kind==NUM) return p->value==q->value;
if (p->kind==VAR) return !strcmp(p->name,q->name);
if (p->kind==FUN && strcmp(p->name,q->name)) return 0;
for (i=0; i<p->nump; ++i)
if (!equal(p->parm[i],q->parm[i])) return 0;
return 1; /* identical */
}
/*-----------------------------------------------------------------
lastnode
*/
node *lastnode(node *p) {
while (p && p->next) p=p->next;
return p;
}
/*-----------------------------------------------------------------
prevnode
*/
node *prevnode(node *p) {
node *t;
for (t=firf; t && t->next!=p; ) t=t->next;
return t;
}
/*-----------------------------------------------------------------
compare two nodes, return -1 if p<q ,0 if p==q ,1 if p>q.
1. anything < NUM
2. x < y
3. x^3 < x^2 < x < y^3
note: 3 < 2
other operators have same order as left operand.
Historical note... this function WAS used for both sorting and
testing for equality. However, for maintenance and efficiency
reasons I found it better to make a separate "equal" function.
*/
int cmp(node *p,node *q) {
int r,i,j,k;
if (p->kind==NUM) /* NUMERIC */
if (q->kind==NUM)
return p->value==q->value ? 0 :
p->value < q->value ? 1 : -1; /* hi to low */
else return 1;
else if (q->kind==NUM)
return -1;
j = (p->kind==MUL && p->rt->kind==NUM);
k = (q->kind==MUL && q->rt->kind==NUM);
if (j && !k) {
r = cmp(p->lf,q); /* IGNORE COEFFICIENTS */
if (r) return r;
return 1;
}
if (k && !j) {
r = cmp(p,q->lf);
if (r) return r;
return -1;
}
if (p->kind==VAR) { /* VARIABLES */
if (q->kind==VAR)
return strcmp(p->name,q->name);
else if (q->nump) {
r = cmp(p,q->lf);
if (r) return r;
}
return 1;
}
else if (q->kind==VAR) {
if (p->nump) {
r = cmp(p->lf,q);
if (r) return r;
}
return -1;
}
if (p->kind==FUN && q->kind==FUN) { /* FUNCTIONS */
r = strcmp(p->name,q->name);
if (r) return r;
}
k = q->kind-p->kind;
if (!k) k=p->nump-q->nump;
if (!k) { /* ARE THEY IDENTICAL? */
for (i=0; i<p->nump; ++i) {
r = cmp(p->parm[i],q->parm[i]);
if (r) return r;
}
return 0; /* identical */
}
if (p->nump && q->nump) { /* DIFFERENT OPERATORS */
if (k>0) r = cmp(p->lf,q);
else r = cmp(p,q->lf);
if (r) return r;
}
return k;
}
/*-----------------------------------------------------------------
sort over operator. oper = ADD or MUL.
This uses bubble sort. It assumes that the nodes have been
adjusted to left association. For best results, call bisect
before calling this.
*/
int sortnode(node *p,int oper) {
int i,r=0;
node *s,**t;
if (p->kind!=oper) {
for (i=0; i<p->nump; ++i)
r+=sortnode(p->parm[i],oper);
return r;
}
while (p->kind==oper) {
s=p;
t=p->parm+1;
while (s->lf->kind==oper) {
s = s->lf;
if (cmp(s->rt,*t)>0) t=s->parm+1;
}
if (cmp(s->lf,*t)>0) t=s->parm;
if (*t != p->rt) { /* too much indirection? */
s = *t;
*t = p->rt;
p->rt = s;
++r;
}
r+=sortnode(p->rt,oper);
p = p->lf;
}
r+=sortnode(p,oper);
return r;
}
/*-----------------------------------------------------------------
prime factor - replace a number with prime factors
*/
void primefact(node *p) {
int i;
double v,f;
for (i=0; i<p->nump; ++i)
primefact(p->parm[i]);
if (p->kind==NUM && !!(v=fabs(p->value)) && whole(v)) {
if (p->value<0) {
p->kind = MUL;
p->nump = 2;
p->lf = newnum(v);
p->rt = newnum(-1);
p = p->lf;
}
for (f=2; f<=maxrat && f<v; ++f)
if (whole(v/f)) {
v /= f;
p->kind = MUL;
p->nump = 2;
p->lf = newnum(v);
p->rt = newnum(f);
p = p->lf;
f = 1; /* start loop at the beginning */
}
}
}
/*--------------------------------------------------------------------
rational search - this searches for a denominator to a number.
*/
int rational_search(node *p) {
int i,r=0,s=1;
static double v,n,x,d,n1,d1,x1;
double err = pow(10,-sigdig);
twirl();
if (p->kind==NUM) {
v = fabs(p->value);
if (p->value<0) s=-1; /* sign */
x = modf(v,&n);
if (x > err) { /* v is not already an integer */
x1=5;
for (d=2; d<=maxrat; ++d) {
modf(d*v+0.5,&n); /* n is the round(d*v) */
x = fabs(n/d - v); /* x is the error */
if (x < x1) {
n1 = n; d1 = d; x1 = x;
}
}
if (x1 < err) { /* convert p to a ratio of n/d */
if (n1==0) {
p->value = 0;
}
else {
p->kind = DIV;
p->nump = 2;
p->lf = newnum(n1*s);
p->rt = newnum(d1);
}
++r;
}
}
else { /* throw away the very small part */
p->value = n*s;
++r;
}
}
return r;
}
/*-----------------------------------------------------------------
reduce a/b to lowest terms (with positive denominator)
*/
int reduce(double *a,double *b) {
int r=0;
double i;
if (*b<0) { *a=-*a; *b=-*b; }
for (i=2; i<=maxrat && i<=fabs(*a) && i<=*b; ++i)
if (whole(*a/i) && whole(*b/i)) {
*a /= i; *b /= i;
++r; i=1;
}
return r;
}
/*---------------------------------------------------------------------------
ration - this converts all the numbers to ratios of integers if
possible.
*/
int ration(node *p) {
static double v,u,h,n1,d1,w9,pf,pp,rp;
static int m,j,k,w,n,f;
int i,r=0;
char s[30];
for (i=0; i<p->nump; ++i)
r+=ration(p->parm[i]);
if (p->kind==NUM && !whole(p->value)) {
u = fabs(p->value);
v = frexp(u,&m); /* u ==> v * 2**m */
if (m > 0) {
n = sigdig - m*log10(2); /* convert m to base 10 logarithm */
if (n < 1) {
modf(p->value,&p->value); /* truncate to integer */
return r+1;
}
v = modf(u,&h);
m = 0;
}
else { /* u is a small number */
h = 0;
v = modf(u,&h); m=0; /* suppress tiny fractions */
n = sigdig;
}
sprintf(s,"%1.18f",v); /* we know 0 < v < 1 */
/* Look for repeating pattern in s */
for (f=0; f<n; ++f) {
k = n-f-1;
for (w=1; w<k; ++w) {
for (i=0; i<w; ++i) {
for (j=f+i+w; j<n && s[j+2]==s[f+i+2]; ) j+=w;
if (j<n) break; /* failed */
}
if (i==w) break; /* success */
}
if (w<k) break; /* success */
}
if (w<k) { /* success */
s[f+w+2] = 0;
sscanf(s+f+2,"%lf",&rp); /* repeating part */
s[f+2] = 0;
s[1] = '0';
sscanf(s+1,"%lf",&pp); /* prefix part */
w9 = pow(10,w)-1; /* w nines */
pf = pow(10,f);
n1 = (pf*h + pp)*w9 + rp;
d1 = w9*pf*pow(2,-m);
if (p->value<0) n1 = -n1;
reduce(&n1,&d1);
if (n1==0) p->value = 0;
else {
p->kind = DIV;
p->nump = 2;
p->lf = newnum(n1);
p->rt = newnum(d1);
}
++r;
}
else /* try to convert using the search method */
r += rational_search(p);
}
return r;
}
/*--------------------------------------------------------------------
associate
rotate the association on add,sub,mul,div
a+b-c => a-c+b => b+a-c => b-c+a
note, we make use of the fact that ADD+1 = SUB and ADD is even.
*/
void associate(node *p) {
node *a,*b;
int opr;
opr = p->kind;
b = p->rt;
if (opr==DIV && b->kind==MUL) { /* special handle for bisected */
p->rt = b->rt;
b->rt = b->lf;
b->lf = p->lf;
p->lf = b;
b->kind = DIV;
b = p->rt;
}
if (opr==ADD || opr==MUL || opr==SUB || opr==DIV) {
a = p;
while ((a->lf->kind|1) == (opr|1)) {
a->kind = a->lf->kind;
a->rt = a->lf->rt;
a = a->lf;
}
a->kind = opr;
if (opr&1) { /* subtr or divide */
a->rt = b;
}
else {
a->rt = a->lf;
a->lf = b;
}
}
else if (opr==EQU) { /* commute equality */
p->rt = p->lf;
p->lf = b;
}
}
/*--------------------------------------------------------------------
commute
commute on add,sub,mul,div
e.g. a/b ==> b^(-1)/a^(-1)
*/
void commuteNOTUSED(node *p) {
node *a,*b;
a = p->lf;
b = p->rt;
if (p->kind==MUL || p->kind==ADD || p->kind==EQU) {
p->lf=b; p->rt=a;
}
else if (p->kind==DIV || p->kind==SUB) { /* non-commutative */
if (a->kind==p->kind+1 &&
b->kind==p->kind+1 &&
a->rt->kind==NUM &&
b->rt->kind==NUM) { /* has coefficients */
p->lf=b; p->rt=a;
a->rt->value = -a->rt->value;
b->rt->value = -b->rt->value;
}
else { /* need to make coefficients */
p->lf = newoper(p->kind+1);
p->rt = newoper(p->kind+1);
p->lf->lf = b;
p->lf->rt = newnum(-1);
p->rt->lf = a;
p->rt->rt = newnum(-1);
}
}
}
/*--------------------------------------------------------------------
a^x * a^y ==> a^(x+y)
x^a * y^a ==> (xy)^a
*/
int expjoin(node *p) {
int i,r=0;
node *a,*b;
for (i=0; i<p->nump; ++i)
r+=expjoin(p->parm[i]);
a = p->lf;
b = p->rt;
if (p->kind==MUL || p->kind==DIV) {
if (a->kind==EXP && b->kind==EXP &&
equal(a->lf,b->lf)) { /* a^x * a^y = a^(x+y) */
freetree(a->lf);
a = a->rt;
freenode(p->lf);
b->kind = p->kind-2;
p->kind = EXP;
p->lf = b->lf;
b->lf = a;
}
else if (b->kind==EXP &&
equal(a,b->lf)) { /* a * a^y = a^(1+y) */
freetree(a);
a = newnum(1);
b->kind = p->kind-2;
p->kind = EXP;
p->lf = b->lf;
b->lf = a;
}
else if (a->kind==EXP &&
equal(a->lf,b)) { /* a^x * a = a^(x+1) */
freetree(b);
b = newnum(1);
a->kind = p->kind-2;
p->kind = EXP;
p->lf = a->lf;
a->lf = a->rt;
a->rt = b;
p->rt = a;
}
else if (a->kind==EXP && b->kind==EXP &&
equal(a->rt,b->rt)) { /* x^a * y^a = (xy)^a */
freetree(b->rt);
p->rt = a->rt;
a->rt = b->lf;
freenode(b);
a->kind = p->kind;
p->kind = EXP;
}
else return r;
++r;
}
return r;
}
/*--------------------------------------------------------------------
remove sub and div
x/y = x*y^-1
x-y = x+y*-1
*/
int nosubdiv(node *p) {
int i,r=0;
node *a,*b;
for (i=0; i<p->nump; ++i)
r+=nosubdiv(p->parm[i]);
a = p->lf;
b = p->rt;
if (p->kind==SUB || p->kind==DIV) {
++r;
--p->kind;
if (b->kind==NUM && p->kind==ADD) /* a-2 = a+(-2) */
b->value = -b->value;
else if (b->kind==NUM && !whole(b->value))
b->value = 1.0/b->value;
else {
a = newoper(p->kind+2); /* MUL or EXP */
a->lf = b;
a->rt = newnum(-1);
p->rt = a;
}
}
return r;
}
/*--------------------------------------------------------------------
bisect node
This converts expressions to canonical form in which
1. + * are left assoc, ^ is right assoc.
2. scope of / is increased, e.g. (a/b)*c ==> ac/b,
3. x+y*-2 is changed to x-y*2
3' x+(-2) is changed to x-2
4. x*y^-2 is changed to x/y^2
4' x*(0.5) is changed to x/2
5. scope of ^ is reduced
The name "bisect" means that each MUL clag is broken into two pieces the
numerator elements and the denominator ones.
*/
int bisect(node *p) {
int i,r=0;
node *a,*b;
for (i=0; i<p->nump; ++i)
r+=bisect(p->parm[i]);
a = p->lf;
b = p->rt;
switch (p->kind) {
case ADD:
case MUL:
/*--------------------------------------------------------------------
Add or Multiply
*/
if (b->kind==p->kind || /* a+(b+c) = a+b+c */
b->kind==p->kind+1) { /* a+(b-c) = a+b-c */
swingb;
i = b->kind;
b->kind = p->kind;
p->kind = i;
}
else if (p->kind==ADD && /* a+(-2) = a-2 */
b->kind==NUM &&
b->value < 0) {
p->kind = SUB;
b->value = -b->value;
}
else if (b->kind==p->kind+2 && /* a+b*-2 = a-b*2 */
b->rt->kind==NUM &&
b->rt->value < 0) {
++p->kind;
b->rt->value = -b->rt->value;
}
/*--------------------------------------------------------------------
Add only
*/
else if (p->kind==ADD &&
aop(b->kind)) { /* a+(b-c) = (a+b)-c */
swingb;
p->kind = b->kind;
b->kind = ADD;
}
/*--------------------------------------------------------------------
Multiply only
*/
else if (p->kind==MUL &&
a->kind==p->kind+1) { /* a-b+c = a+c-b */
p->rt = a->rt;
a->rt = b;
++p->kind;
--a->kind;
}
else if (p->kind==MUL &&
a->kind==p->kind+2 && /* b*-2+a = a-b*2 */
a->rt->kind==NUM &&
a->rt->value < 0) {
++p->kind;
p->lf = b;
p->rt = a;
a->rt->value = -a->rt->value;
}
else break;
++r; break;
case SUB:
case DIV:
/*--------------------------------------------------------------------
Subtract or divide
*/
if (b->kind==p->kind+1 && /* a-b*-2 = a+b*2 NOT NECES. */
b->rt->kind==NUM &&
b->rt->value < 0) {
--p->kind;
b->rt->value = -b->rt->value;
}
/*--------------------------------------------------------------------
Subtract only
*/
else if (p->kind==SUB && /* a-(-2) = a+2 */
b->kind==NUM &&
b->value < 0) {
p->kind = ADD;
b->value = -b->value;
}
else if (p->kind==SUB &&
aop(b->kind)) { /* a-(b+c) = (a-b)-c */
swingb;
p->kind = (ADD+SUB) - b->kind;
b->kind = SUB;
}
/*--------------------------------------------------------------------
Divide only
*/
else if (p->kind==DIV &&
b->kind==p->kind) { /* a-(b-c) = a+c-b */
p->lf = b;
p->rt = b->lf;
b->lf = a;
--b->kind;
}
else if (p->kind==DIV &&
a->kind==p->kind) { /* a-b-c = a-(b+c) */
swinga;
--a->kind;
}
else break;
++r; break;
case EXP:
/*--------------------------------------------------------------------
exponent
*/
if (a->kind==EXP) { /* (x^y)^z = x^(y*z) */
swinga;
a->kind = MUL;
}
else if (b->kind==NUM && /* a^(-2) = 1/a^2 */
b->value < 0) {
p->kind = DIV;
p->lf = newnum(1);
p->rt = newoper(EXP);
p->rt->lf = a;
p->rt->rt = b;
b->value = -b->value;
}
else break;
++r; break;
}
return r;
}
/*--------------------------------------------------------------------
exponent expand - expand any integer exponents less than 100.
*/
int exexpand(node *p) {
int i,r=0;
node *a,*b;
for (i=0; i<p->nump; ++i)
r+=exexpand(p->parm[i]);
a = p->lf;
b = p->rt;
if (p->kind==EXP &&
b->kind==NUM && /* a^n = a^(n-1)*a */
b->value > 1 &&
b->value <= maxpow &&
whole(b->value)) {
if (--b->value == 1) {
p->lf = deepcopy(a);
freenode(b);
}
else {
b = newoper(EXP);
b->lf = deepcopy(a);
b->rt = p->rt;
p->lf = b;
}
p->rt = a;
p->kind = MUL;
++r;
}
return r;
}
/*--------------------------------------------------------------------
ComDeno - find common denominators.
*/
int comdeno(node *p) {
int i,r=0;
double x;
node *a,*b,*nu,*de;
for (i=0; i<p->nump; ++i)
r+=comdeno(p->parm[i]);
a = p->lf;
b = p->rt;
if (aop(p->kind) && (a->kind==DIV || b->kind==DIV)) {
if (a->kind==DIV && b->kind==DIV) {
if (!equal(a->rt,b->rt)) { /* a/c + b/d = (ad+bc)/cd */
a->kind = b->kind = MUL;
de = b->rt;
b->rt = a->rt;
a->rt = de;
de = newoper(MUL);
de->lf = deepcopy(b->rt);
de->rt = deepcopy(a->rt);
}
else { /* a/x + b/x = (a+b)/x */
freetree(b->rt);
de = a->rt;
a = a->lf;
b = b->lf;
freenode(p->lf);
freenode(p->rt);
}
}
else if (a->kind==DIV) { /* a/b + c = (a + bc)/b */
a->kind = MUL;
de = a->lf;
a->lf = b;
b = a;
a = de;
de = deepcopy(b->rt);
}
else { /* a + b/c = (ac + b)/c */
b->kind = MUL;
de = b->lf;
b->lf = a;
a = b;
b = de;
de = deepcopy(a->rt);
}
nu = newoper(p->kind);
nu->lf = a;
nu->rt = b;
p->kind = DIV;
p->lf = nu;
p->rt = de;
++r;
}
return r;
}
/*-----------------------------------------------------------------
distribute2
This applies the distributive laws
1. division over addition.
*/
int distribute2(node *p) {
node *a,*b,*c;
int i,r=0;
for (i=0; i<p->nump; ++i)
r+=distribute2(p->parm[i]);
i=0;
c = p->parm[i];
if (p->kind==DIV && aop(c->kind)) {
b = deepcopy(p->parm[1-i]);
a = newoper(p->kind);
a->parm[i] = c->parm[1-i];
a->parm[1-i] = b;
c->parm[1-i] = p->parm[1-i];
p->parm[1-i] = a;
p->kind = c->kind;
c->kind = a->kind;
++r;
}
return r;
}
/*-----------------------------------------------------------------
distribute
This applies the distributive laws
1. multiplication over addition.
2. (ab)^2 = a^2*b^2
3. x^(a+b) = x^a*x^b
*/
int distribute(node *p) {
node *a,*b,*c;
int i,r=0;
for (i=0; i<p->nump; ++i)
r+=distribute(p->parm[i]);
for (i=0; i<2; ++i) {
c = p->parm[i];
if (p->kind==MUL && aop(c->kind) ||
i && p->kind==EXP && aop(c->kind) ||
!i && p->kind==EXP && (c->kind==MUL || c->kind==DIV)) {
b = deepcopy(p->parm[1-i]);
a = newoper(p->kind);
a->parm[i] = c->parm[1-i];
a->parm[1-i] = b;
c->parm[1-i] = p->parm[1-i];
p->parm[1-i] = a;
p->kind = c->kind;
c->kind = a->kind;
if (i && a->kind==EXP) p->kind+=2; /* change ADD to MUL */
++r;
break; /* just to be safe */
}
}
return r;
}
/*--------------------------------------------------------------------
fixassoc
+ * are left assoc, ^ is right assoc.
*/
int fixassoc(node *p) {
int i,r=0;
node *a,*b;
for (i=0; i<p->nump; ++i)
r+=fixassoc(p->parm[i]);
a = p->lf;
b = p->rt;
switch (p->kind) {
case ADD:
case MUL:
if (b->kind==p->kind || /* a+(b+c) = a+b+c */
b->kind==p->kind+1) { /* a+(b-c) = a+b-c */
swingb;
i=b->kind; b->kind=p->kind; p->kind=i;
}
else break;
++r; break;
case SUB:
case DIV:
if (b->kind==p->kind) { /* a-(b-c) = a-b+c */
swingb;
--p->kind;
}
else if (b->kind==p->kind-1) { /* a-(b+c) = a-b-c */
swingb;
++b->kind;
}
else break;
++r; break;
case EXP:
if (a->kind==EXP) { /* (x^y)^z = x^(y*z) */
swinga;
a->kind = MUL;
}
else break;
++r; break;
}
return r;
}
/*--------------------------------------------------------------------
Move numbers within clag.
*/
int movenums(node *p,int up,int oper) {
int i,r=0;
node *a,*b;
for (i=0; i<p->nump; ++i)
r+=movenums(p->parm[i],up,oper);
if (p->kind!=oper) return r;
a = p->lf;
b = p->rt;
i = 1;
if (a->kind!=oper) {
a=p; i=0;
}
if (a->parm[i]->kind==NUM) {
if (p->rt->kind==NUM) { /* combine nums */
if (oper==ADD) a->parm[i]->value += p->rt->value;
else a->parm[i]->value *= p->rt->value;
a = p->lf;
nodecpy(p,a);
freenode(a);
freenode(b);
++r;
}
else if (up) { /* switch */
p->rt = a->parm[i];
a->parm[i] = b;
++r;
}
}
else if (b->kind==NUM && !up) { /* switch */
p->rt = a->parm[i];
a->parm[i] = b;
++r;
}
return r;
}
/*--------------------------------------------------------------------
calcnode
calculate as many numeric results as possible
*/
int calcnode(node *p,int keeprat) {
int i,r=0;
node *a,*b;
for (i=0; i<p->nump; ++i)
r+=calcnode(p->parm[i],keeprat);
a = p->lf;
b = p->rt;
switch (p->kind) {
case ADD:
if (a->kind==NUM && a->value==0) { /* 0+x = x */
nodecpy(p,b);
freenode(a); freenode(b);
}
else if (b->kind==NUM && b->value==0) { /* x+0 = x */
nodecpy(p,a);
freenode(a); freenode(b);
}
else if (a->kind==NUM && b->kind==NUM) { /* n+n = n */
p->kind = NUM;
p->nump = 0;
p->value = a->value + b->value;
freenode(a); freenode(b);
}
else if (b->kind==MUL && /* a+-1*b = a-b */
b->lf->kind==NUM &&
b->lf->value==-1) {
p->kind = SUB;
p->rt = b->rt;
freenode(b->lf); freenode(b);
}
else if (b->kind==MUL && /* a+b*(-1) = a-b */
b->rt->kind==NUM &&
b->rt->value==-1) {
p->kind = SUB;
p->rt = b->lf;
freenode(b->rt); freenode(b);
}
else break; ++r;
break;
case SUB:
if (a->kind==NUM && a->value==0) { /* 0-x = -x */
p->kind = MUL;
a->value = -1;
}
else if (a->kind==NUM && b->kind==NUM) { /* n-n = n */
p->kind = NUM;
p->nump = 0;
p->value = a->value - b->value;
freenode(a); freenode(b);
}
else if (b->kind==NUM && b->value==0) { /* x-0 = x */
nodecpy(p,a);
freenode(a); freenode(b);
}
else break; ++r;
break;
case MUL:
if (a->kind==NUM && b->kind==NUM) { /* n*n = n */
p->kind = NUM;
p->nump = 0;
p->value = a->value * b->value;
freenode(a); freenode(b);
}
else if (a->kind==NUM && a->value==0) { /* 0*x = 0 */
p->kind = NUM;
p->nump = 0;
p->value = 0;
freenode(a); freetree(b);
}
else if (b->kind==NUM && b->value==0) { /* x*0 = 0 */
p->kind = NUM;
p->nump = 0;
p->value = 0;
freetree(a); freenode(b);
}
else if (a->kind==NUM && a->value==1) { /* 1*x = x */
nodecpy(p,b);
freenode(a); freenode(b);
}
else if (b->kind==NUM && b->value==1) { /* x*1 = x */
nodecpy(p,a);
freenode(a); freenode(b);
}
else break; ++r;
break;
case DIV:
if (b->kind==NUM && b->value==0) { /* x/0 = BAD */
p->kind = BAD;
p->nump = 0;
p->value = 0;
freetree(a); freenode(b);
}
else if (a->kind==NUM && a->value==0) { /* 0/x = 0 */
p->kind = NUM;
p->nump = 0;
p->value = 0;
freenode(a); freetree(b);
}
else if (b->kind==NUM && b->value==1) { /* x/1 = x */
nodecpy(p,a);
freenode(a); freenode(b);
}
else if (a->kind==NUM && b->kind==NUM) { /* n/n = n */
if (keeprat &&
whole(a->value) && /* if both int, then reduce */
whole(b->value)) {
if (!reduce(&a->value,&b->value)) break;
}
else {
p->kind = NUM;
p->nump = 0;
p->value = a->value / b->value;
freenode(a); freenode(b);
}
}
/*-----------------------------------------------------------------
the following is called "stretch rule" to accomidate numbers
separated by the bisection function.
*/
else if (
(a->kind==NUM || a->kind==MUL && a->rt->kind==NUM && !!(a=a->rt)) &&
(b->kind==NUM || b->kind==MUL && b->rt->kind==NUM && !!(b=b->rt))) {
if (keeprat &&
whole(a->value) && /* if both int, then reduce */
whole(b->value)) {
if (!reduce(&a->value,&b->value)) break;
}
else {
a->value = a->value / b->value;
b->value = 1.0;
}
}
else break; ++r;
break;
case EXP:
if (b->kind==NUM && b->value==0) { /* x^0 = 1 */
p->kind = NUM;
p->nump = 0;
p->value = 1;
freetree(a); freenode(b);
}
else if (a->kind==NUM && a->value==1) { /* 1^x = 1 */
p->kind = NUM;
p->nump = 0;
p->value = 1;
freenode(a); freetree(b);
}
else if (b->kind==NUM && b->value==1) { /* x^1 = x */
nodecpy(p,a);
freenode(a); freenode(b);
}
else if (a->kind==NUM && b->kind==NUM) { /* n^n = n */
if (keeprat && b->value<1 && whole(a->value) &&
fabs(a->value) < pow(10,sigdig))
break;
if (a->value < 0 && !whole(b->value)) { /* domain error */
p->kind = MUL;
a->value = pow(-a->value,b->value);
b->kind = VAR;
strcpy(b->name,"i"); /* imaginary */
}
else {
p->kind = NUM;
p->nump = 0;
p->value = pow(a->value,b->value);
freenode(a); freenode(b);
}
}
else if (a->kind==VAR && !strcmp(a->name,"i") && /* i^2 = -1 */
b->kind==NUM && (b->value>=2 || b->value<0)) {
i=1;
while (b->value >= 2) {
i *= -1;
b->value -= 2;
}
while (b->value < 0) {
i *= -1;
b->value += 2;
}
p->kind = MUL;
p->lf = newnum(i);
p->rt = newoper(EXP);
p->rt->lf = a;
p->rt->rt = b;
}
else break; ++r;
break;
case FUN:
if (p->nump==1 && a->kind==NUM) {
if (!strcmp(p->name,"sin")) p->value=sin(a->value);
else if (!strcmp(p->name,"cos")) p->value=cos(a->value);
else if (!strcmp(p->name,"tan")) p->value=tan(a->value);
else if (!strcmp(p->name,"acos")) p->value=acos(a->value);
else if (!strcmp(p->name,"asin")) p->value=asin(a->value);
else if (!strcmp(p->name,"atan")) p->value=atan(a->value);
else if (!strcmp(p->name,"cosh")) p->value=cosh(a->value);
else if (!strcmp(p->name,"sinh")) p->value=sinh(a->value);
else if (!strcmp(p->name,"tanh")) p->value=tanh(a->value);
else if (!strcmp(p->name,"ln")) p->value=log(a->value);
else if (!strcmp(p->name,"log")) p->value=log10(a->value);
else if (!strcmp(p->name,"abs")) p->value=fabs(a->value);
else break;
p->kind = NUM;
p->nump = 0;
freenode(a);
}
break;
}
return r;
}
/*-----------------------------------------------------------------
get term, return p without coefficient
*/
node *get_term(node *p, int oper, double *r) {
*r = 1.0; /* default coeff */
if (p->kind==oper && p->rt->kind==NUM) {
*r = p->rt->value;
return p->lf;
}
return p;
}
/*--------------------------------------------------------------------
Combine all terms with common base within a clag.
*/
int combine(node *p) {
int i,oper,r=0;
node *a,*b;
node *t1,*t2;
double c1,c2;
for (i=0; i<p->nump; ++i)
r+=combine(p->parm[i]);
a = p->lf;
b = p->rt;
oper = p->kind;
if (oper!=ADD && oper!=MUL) /* not a clag */
return r;
i = 1;
if (a->kind!=oper) {
a=p; i=0;
}
t1 = get_term(a->parm[i],oper+2,&c1);
t2 = get_term(b,oper+2,&c2);
if (equal(t1,t2)) { /* same base, combine terms */
if (a->parm[i]==t1)
freetree(t1);
else { /* free one of the expressions */
freetree(b);
b = a->parm[i];
t2 = t1;
}
c2 += c1;
if (b==t2) { /* make room for coeff */
b = newoper(oper+2);
b->lf = t2;
b->rt = newnum(0);
}
b->rt->value = c2;
if (c2==1.0) { /* remove unit coeff */
freenode(b->rt);
freenode(b);
b = t2;
}
if (i) { /* cleanup... 2 stage */
nodecpy(p,a);
freenode(a);
p->rt = b;
}
else { /* 1 stage */
nodecpy(p,b);
freenode(b);
}
++r;
}
return r;
}
/*-----------------------------------------------------------------
make-tree convert a table of coefficients to a node tree.
Note
the coefficients are REUSED (NOT copied), so don't free them.
*/
node *maketree(node **coef, int sz, node *base) {
int i;
node *p,*tmp;
p = coef[0];
for (i=1; i<sz; ++i) {
if (coef[i]->kind==NUM && !coef[i]->value) {
freenode(coef[i]); /* throw away zeros */
}
else {
tmp = p;
p = newoper(ADD);
p->lf = tmp;
p->rt = tmp = newoper(MUL);
tmp->lf = coef[i]; /* add coef[i]*base^i */
if (i>1) {
tmp->rt = newoper(EXP);
tmp->rt->lf = deepcopy(base);
tmp->rt->rt = newnum(i);
}
else tmp->rt = deepcopy(base);
}
}
return p;
}
/*-----------------------------------------------------------------
looks inside 'a' for any expression b or b^N.
if found, it changes it to a 1 (ONE) and returns the exponent.
Note, if N is not an integer (it's a fraction or expression)
then findbase ignores it.
When findbase returns 0, then you can assume 'a' is unchanged.
*/
int findbase(node *a, node *b) {
int i,x=1;
if (equal(a,b) || /* any expression */
a->kind==EXP && a->rt->kind==NUM && /* EXPONENTS */
(x = a->rt->value, whole(x)) &&
x >=0 && equal(a->lf,b)) {
movenode(a,newnum(1));
return x;
}
if (a->kind==MUL) { /* recurse on MULTIPLY */
x = findbase(a->lf,b);
if (x) return x;
x = findbase(a->rt,b);
return x;
}
return 0;
}
/*-----------------------------------------------------------------
add entry to coef table grow and init table
*/
#define addcoef(k,a) do { \
node *tmp; \
if (sz<=i) { \
coef = realloc((void*)coef,(i+1)*sizeof*coef); \
for ( ; sz<=i; ++sz) \
coef[sz] = newnum(0); \
} \
tmp = coef[i]; \
coef[i] = newoper(k); \
coef[i]->lf = tmp; \
coef[i]->rt = a; \
} while(0)
/*-----------------------------------------------------------------
MAKETABLE convert a tree to a table of coefficients which is indexed
by the power of a given base. For example, a*x^2 + 3*x - b would be
0 -> 0 - b
1 -> 0 + 3
2 -> 0 + a
the size returned is the index of the last non-zero coefficient + 1
Note:
the tree, p, is expected to have correct association.
the coefficients always have a leading zero.
the tree is not altered or referred to by the table.
on return, you are guaranteed that result[i]->kind is one of NUM,
ADD, SUB and if it is NUM then it is zero.
*/
node **maketable(node *base, node *p, int *size) {
node *a;
int i,sz;
node **coef;
coef = malloc(sizeof*coef); /* initial size = 1 */
checknull(coef);
*coef = newnum(0);
sz = 1;
a = deepcopy(p); /* make a working copy */
while (1) {
i = findbase(a,base);
if (!i && aop(a->kind)) {
i = findbase(a->rt,base);
addcoef(a->kind,a->rt);
}
else {
addcoef(ADD,a);
break;
}
a = a->lf;
}
*size = sz;
return coef;
}
/*-----------------------------------------------------------------
polynomial long division
base is the expression on which the division is done (e.g. x)
nm and dn are numerator and denominator
returns:
a new tree if success, else returns NULL
*/
node *polydiv(node *base, node *nm, node *dn) {
node **num,**den,**quo,**rem;
int szn,szd,szq,szr;
int i,j;
node *tmp,*p;
num = maketable(base,nm,&szn);
den = maketable(base,dn,&szd);
/* If degree of numerator is less than denominator, or
if the denominator does not contain base, then return failure */
if (szn < szd || szd < 2) {
for (i=0; i<szn; ++i) freetree(num[i]);
for (i=0; i<szd; ++i) freetree(den[i]);
free(num); free(den);
return NULL;
}
szq = szn-szd+1;
quo = malloc(szq*sizeof*quo);
checknull(quo);
szr = szd-1;
rem = malloc(szr*sizeof*rem);
checknull(rem);
/* Divide every coefficient by leading coeff in denominator */
p = den[szd-1];
if (!(p->kind==ADD && p->rt->kind==NUM && p->rt->value==1.0)) {
for (i=0; i<szn; ++i) {
if (num[i]->kind != NUM) { twirl();
tmp = num[i];
num[i] = newoper(DIV);
num[i]->lf = tmp;
num[i]->rt = deepcopy(p);
}
}
for (i=0; i<szd-1; ++i) {
if (den[i]->kind != NUM) { twirl();
tmp = den[i];
den[i] = newoper(DIV);
den[i]->lf = tmp;
den[i]->rt = deepcopy(p);
}
}
}
/* Construct the coefficients of the quotient */
for (i=1; i<=szq; ++i) {
quo[szq-i] = deepcopy(num[szn-i]);
for (j=1; j<i; ++j) if (i-j < szd) { twirl();
tmp = quo[szq-i];
quo[szq-i] = newoper(SUB);
quo[szq-i]->lf = tmp;
quo[szq-i]->rt = tmp = newoper(MUL);
tmp->lf = deepcopy(den[szd-i+j-1]);
tmp->rt = deepcopy(quo[szq-j]);
}
}
/* Construct the coefficients of the remainder */
for (i=0; i<szr; ++i) {
rem[i] = deepcopy(num[i]);
for (j=0; j<=i; ++j) if (i-j < szq) { twirl();
tmp = rem[i];
rem[i] = newoper(SUB);
rem[i]->lf = tmp;
rem[i]->rt = tmp = newoper(MUL);
tmp->lf = deepcopy(den[j]);
tmp->rt = deepcopy(quo[i-j]);
}
}
/* Convert the quo and rem tables into a node tree */
p = newoper(ADD);
p->rt = newoper(DIV);
p->rt->lf = maketree(rem,szr,base);
p->rt->rt = deepcopy(dn);
p->lf = maketree(quo,szq,base);
/* Clean up and return */
for (i=0; i<szn; ++i) freetree(num[i]);
for (i=0; i<szd; ++i) freetree(den[i]);
free(num);
free(quo);
free(rem);
free(den);
return p;
}
/*-----------------------------------------------------------------
polycoef - collect the coefficients of a polynomial
*/
void polycoef(node *base, node *p) {
node **coef;
int sz;
int i;
if (!p || !base || p->kind==EQU || base->kind==EQU) return;
coef = maketable(base,p,&sz);
if (sz < 2) {
if (sz) freetree(coef[0]);
free(coef);
return;
}
for (i=0; i<sz; ++i) /* calculate the coefficients */
while (calcnode(coef[i],1));
movenode(p,maketree(coef,sz,base)); /* replace p */
free(coef);
}
/*-----------------------------------------------------------------
use quadratic equation to factor a degree-2 polynomial
2 2
2 b + sqrt(b - 4ac) b - sqrt(b - 4ac)
ax + bx + c ==> (ax + -----------------) (x + -----------------)
2 2a
*/
void quadratic(node *base,node *p) {
node **coef,*a;
int sz;
int i;
coef = maketable(base,p,&sz);
if (sz != 3) {
for (i=0; i<sz; ++i) freetree(coef[i]);
free(coef);
return;
}
/* Construct the two binomials p = (ax + u)(x + v) */
a = newoper(ADD);
a->lf = deepcopy(coef[1]); /* b */
a->rt = newoper(EXP);
a->rt->lf = newoper(SUB);
a->rt->lf->lf = newoper(EXP); /* b^2 */
a->rt->lf->lf->lf = deepcopy(coef[1]);
a->rt->lf->lf->rt = newnum(2);
a->rt->lf->rt = newoper(MUL); /* 4ac */
a->rt->lf->rt->lf = newoper(MUL);
a->rt->lf->rt->lf->lf = newnum(4);
a->rt->lf->rt->lf->rt = deepcopy(coef[2]);
a->rt->lf->rt->rt = deepcopy(coef[0]);
a->rt->rt = newnum(0.5);
movenode(p,newoper(MUL));
p->lf = newoper(ADD);
p->lf->lf = newoper(MUL); /* ax */
p->lf->lf->lf = deepcopy(coef[2]);
p->lf->lf->rt = deepcopy(base);
p->lf->rt = newoper(DIV);
p->lf->rt->lf = a;
p->lf->rt->rt = newnum(2);
p->rt = newoper(ADD);
p->rt->lf = deepcopy(base);
p->rt->rt = newoper(DIV);
p->rt->rt->lf = deepcopy(a);
p->rt->rt->lf->kind = SUB;
p->rt->rt->rt = newoper(MUL); /* 2a */
p->rt->rt->rt->lf = newnum(2);
p->rt->rt->rt->rt = deepcopy(coef[2]);
while(calcnode(p->lf->lf->lf,1));
while(calcnode(p->lf->rt,1));
while(calcnode(p->rt->rt,1));
for (i=0; i<sz; ++i) freetree(coef[i]);
free(coef);
}
/*-----------------------------------------------------------------
next factor, returns the ith factor of an expression.
it is a new nodetree.
*/
node *nextfact(node *p,int i) {
int j;
double k;
if (i<1) return newnum(1); /* everything has factor 1 */
if (p->kind==NUM && (k = fabs(p->value), k<maxrat) && whole(k)) {
if (k==1) return NULL; /* don't return 1 twice */
for (j=2; j<maxrat && j*2<=k; ++j)
if (fmod(k,j)==0 && !--i)
return newnum(j);
}
else if (p->kind==MUL) {
while (i>2 && p->kind==MUL) {
p=p->lf; i-=2;
}
if (i==2 && p->kind==MUL)
return deepcopy(p->rt);
}
if (i==1) return deepcopy(p);
return NULL;
}
/*-----------------------------------------------------------------
check root
returns the new quotient on success, else null;
*/
node *checkroot(node *base, node *p, node *den) {
node *ans;
twirl();
ans = polydiv(base,p,den);
if (!ans) return NULL;
if (ans->kind==ADD && ans->rt->kind==DIV) { /* always true!! */
while (distribute(ans->rt));
simplify(ans->rt);
simplify(ans->rt);
if (ans->rt->kind==NUM && ans->rt->value==0) /* success!! */
return ans;
}
freetree(ans);
return NULL;
}
/*-----------------------------------------------------------------
factor a polynomial using rational roots
*/
void factrpoly(node *base,node *p) {
node **coef,*a,*b,*q,*dn,*nm,*r;
int sz;
int i,j,n;
coef = maketable(base,p,&sz);
if (sz < 3) {
for (i=0; i<sz; ++i) freetree(coef[i]);
free(coef);
return;
}
/* For every possible rational root, check it for divisibility */
while (exexpand(coef[0]));
while (nosubdiv(coef[0]));
while (fixassoc(coef[0]));
while (calcnode(coef[0],0));
while (calcnode(coef[sz-1],0));
nm = deepcopy(p); /* initialize numerator */
n = 2; /* n is factor counter */
r = NULL; /* intialize the list of factors */
for (i=0; n<sz; ++i) {
b = nextfact(coef[sz-1],i); /* get next factor */
if (!b) break;
for (j=0; n<sz;) {
a = nextfact(coef[0],j); /* next factor */
if (!a) break;
/*-----------------------------------------------------------------
check roots a/b, -a/b, ia/b, -ia/b
*/
dn = cons(deepcopy(base),SUB,cons(a,DIV,deepcopy(b)));
simplify(dn->rt); /* twice for good luck */
#define DO_CHECK \
simplify(dn->rt); \
q = checkroot(base,nm,dn); \
if (q) { \
r = cons(r,MUL,dn);\
freetree(nm); \
nm = q; \
simplify(nm); \
simplify(nm); \
++n; \
continue; \
}
DO_CHECK
dn->rt = cons(dn->rt,MUL,newnum(-1));
DO_CHECK
a = newnode(); a->kind = VAR; strcpy(a->name,"i");
dn->rt = cons(dn->rt,MUL,a);
DO_CHECK
dn->rt = cons(dn->rt,MUL,newnum(-1));
DO_CHECK
freetree(dn);
++j;
}
freetree(b);
}
if (n > 2) {
r = cons(r,MUL,nm);
movenode(p,r); /* replace p with r */
}
for (i=0; i<sz; ++i) freetree(coef[i]);
free(coef);
return;
}
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The following functions relate to displaying expressions on the
screen.
*/
/*-----------------------------------------------------------------
twirl - this spins a little status indicator on the bottom
of the display to reassure the user in long calculations.
*/
void twirl(void) {
static char c[] = "/-\\|";
static int i=0;
gotoxy(5,ti.screenheight);
putch(c[i]);
if (++i > 3) i=0;
}
/*--------------------------------------------------------------------
debug dump
*/
void dumpnode(node *p,int tab) {
int i;
for (i=0; i<tab; ++i) putch(' ');
cprintf("%p %d %d (%d %d) %d '%s'\r\n",p,p->kind,p->nump,
p->px,p->py,p->sy,p->name);
for (i=0; i<p->nump; ++i)
dumpnode(p->parm[i],tab+2);
}
/*--------------------------------------------------------------------
re compute size of each node
*/
void resize(node *p,int ppr) {
double x;
int mpr,i;
mpr = pr[p->kind];
p->ay = 0;
switch (p->kind) {
case NUM:
x = fabs(p->value);
p->name[0] = 0;
if (p->value<0) strcat(p->name,"-");
if (x==PI) strcat(p->name,piname);
else if (x==E) strcat(p->name,ename);
else if (x==HUGE_VAL) strcat(p->name,infname);
else if (ch8 && x==0.5) strcat(p->name,halfname);
else if (ch8 && x==0.25) strcat(p->name,qtrname);
else sprintf(p->name,"%1.15G",p->value);
p->sy=1;
p->sx=strlen(p->name);
break;
case BAD:
strcpy(p->name,errname); /* fall thru */
case VAR:
p->sy=1;
p->sx=strlen(p->name);
break;
case EQU:
case ADD:
case SUB:
p->sx = 3;
if (mpr<ppr) p->sx += 2; /* parens */
resize(p->lf,mpr);
resize(p->rt,mpr+1); /* +1 = a hack */
p->sy = max(p->lf->sy,p->rt->sy);
p->sx += p->lf->sx + p->rt->sx;
strcpy(p->name,kname[p->kind]);
break;
case MUL:
p->sx = 1;
if (mpr<ppr) p->sx += 2; /* parens */
resize(p->lf,mpr);
resize(p->rt,mpr+1); /* +1 = a hack */
p->sy = max(p->lf->sy,p->rt->sy);
p->sx += p->lf->sx + p->rt->sx;
strcpy(p->name,kname[p->kind]);
break;
case DIV:
resize(p->lf,1); /* 1 = div suppresses parens */
resize(p->rt,1);
p->sx = max(p->lf->sx,p->rt->sx) + 2;
p->sy = p->lf->sy + p->rt->sy + 1;
p->ay = p->lf->sy - p->rt->sy;
if (p->ay <0) p->ay--;
p->ay /= 2;
if (mpr<ppr) p->sx += 2; /* parens */
strcpy(p->name,kname[p->kind]);
break;
case EXP:
resize(p->lf,mpr+1);
resize(p->rt,mpr);
p->sx = p->lf->sx + p->rt->sx;
p->sy = p->lf->sy + p->rt->sy;
p->ay = p->rt->sy - p->lf->sy;
if (p->ay >0) p->ay++;
p->ay /= 2;
if (mpr<ppr) p->sx += 2; /* parens */
strcpy(p->name,kname[p->kind]);
break;
case FUN:
p->sx = strlen(p->name) + 2; /* 2 parens */
p->sy = 0;
for (i=0; i<p->nump; ++i) {
resize(p->parm[i],mpr);
setmax(p->sy,p->parm[i]->sy);
p->sx += 1 + p->parm[i]->sx; /* 1 comma */
}
break;
default:
printf("Error in resize");
pause;
}
}
/*--------------------------------------------------------------------
leftpar - print a left parenthesis
*/
void leftpar(int h) {
int i;
if (h<2) {
putch('('); return; }
relxy(0,-h/2);
putch(ulc);
for (i=0; i<h-2; ++i) {
relxy(-1,1);
putch(vline);
}
relxy(-1,1);
putch(llc);
}
/*--------------------------------------------------------------------
rightpar - print a right parenthesis
*/
void rightpar(int h) {
int i;
if (h<2) {
putch(')'); return; }
relxy(0,-h/2);
putch(urc);
for (i=0; i<h-2; ++i) {
relxy(-1,1);
putch(vline);
}
relxy(-1,1);
putch(lrc);
}
/*-----------------------------------------------------------------
fix attributes
*/
int fixattr(node *p) {
gettextinfo(&ti);
if (p==src) textattr(bold1);
/* else if (p==tgt) textattr(bold2); */
return ti.attribute;
}
#define seen (x > 0 && x < ti.screenwidth)
/*--------------------------------------------------------------------
print expression
*/
void show(node *p,int ppr,int x,int y) {
int mpr,i,z,s0,s1,attr;
mpr = pr[p->kind];
if (yadj) y = y + p->ay; /* adjust y */
gotoxy(x,y);
attr=fixattr(p); /* save old attribute */
switch (p->kind) {
case NUM:
case BAD:
case VAR:
p->px = x; p->py = y;
if (seen) cputs(p->name);
break;
case EQU:
case ADD:
case SUB:
if (mpr<ppr) { if (seen) leftpar(p->sy); ++x; } /* parens */
show(p->lf,mpr,x,y);
x += p->lf->sx + 1;
gotoxy(x-1,y);
fixattr(p);
if (seen) cprintf(" %s ",kname[p->kind]);
show(p->rt,mpr+1,x+2,y);
p->px = x; p->py = y;
if (mpr<ppr) {
x += 2+p->rt->sx;
gotoxy(x,y);
fixattr(p);
if (seen) rightpar(p->sy); /* parens */
}
break;
case MUL:
if (mpr<ppr) { if (seen) leftpar(p->sy); ++x; } /* parens */
show(p->lf,mpr,x,y);
x += p->lf->sx;
gotoxy(x,y);
fixattr(p);
if (seen) cputs(ch8 ? kname[p->kind] : "*");
show(p->rt,mpr+1,x+1,y);
p->px = x; p->py = y;
if (mpr<ppr) {
x += 1+p->rt->sx;
gotoxy(x,y);
fixattr(p);
if (seen) rightpar(p->sy); /* parens */
}
break;
case DIV:
if (seen) for (i=0; i<p->sx; ++i) {
if (x+i < ti.screenwidth) putch(hline);
}
else if (x<1 && 0<(i=x+p->sx)) {
gotoxy(1,y); while (--i) putch(hline);
}
p->px = x; p->py = y;
if (mpr<ppr) {
gotoxy(x,y);
if (seen) leftpar(p->sy); /* parens */
}
show(p->lf,1,x + (p->sx - p->lf->sx)/2,
y - (p->lf->sy + 1)/2);
fixattr(p);
show(p->rt,1,x + (p->sx - p->rt->sx)/2,
y + (p->rt->sy + 2)/2);
if (mpr<ppr) {
x+=p->sx-1;
gotoxy(x,y);
fixattr(p);
if (seen) rightpar(p->sy); /* parens */
}
break;
case EXP:
if (mpr<ppr) { if (seen) leftpar(p->sy); ++x; } /* parens */
show(p->rt,mpr ,x + p->lf->sx,y - (p->rt->sy + 1)/2);
fixattr(p);
show(p->lf,mpr+1,x, y + (p->lf->sy + 0)/2);
p->px = x + p->lf->sx - 1;
p->py = y - 1;
if (mpr<ppr) {
x += p->sx-2;
gotoxy(x,y);
fixattr(p);
if (seen) rightpar(p->sy); /* parens */
}
break;
case FUN:
p->px = x; p->py = y;
if (seen) {
cputs(p->name);
leftpar(p->sy);
}
x += strlen(p->name) + 1;
for (i=0; ; ) {
show(p->parm[i],mpr,x,y);
x += p->parm[i]->sx;
gotoxy(x,y);
fixattr(p);
if (++i >= p->nump) break;
if (seen) putch(comma);
++x;
}
if (seen) rightpar(p->sy); /* parens */
break;
default:
printf("Error in print\n");
pause;
}
textattr(attr);
}
/*-----------------------------------------------------------------
display menu
*/
void show_menu(void) {
int i,x,y,n;
textattr(mcolor);
for (i=0; i<numm; ++i) {
n = ti.screenwidth / mwidth;
x = (i%n)*mwidth+1;
y = 1+i/n;
gotoxy(x,y);
if (x>1) putch(vline); else putch(' ');
cputs(menu[i]);
for (x=strlen(menu[i])+1; x<mwidth; ++x) putch(' ');
}
mheight = y+1; /* number of lines used by the menu */
relxy(-1,0);
for (x=wherex(); x>1 && x<=ti.screenwidth; ++x)
if (x%mwidth!=1) putch(' '); else putch(vline);
/* MAKE THE BOX AROUND THE FORMULA */
if (ch8) {
gotoxy(1,y+1);
putch(201);
for (i=2; i<ti.screenwidth; ++i) putch(205);
putch(187);
for (i=y+2; i<ti.screenheight; ++i) {
gotoxy(1,i); putch(186); gotoxy(ti.screenwidth,i); putch(186);
}
gotoxy(1,ti.screenheight);
putch(200);
for (i=2; i<ti.screenwidth; ++i) putch(205);
putch(188);
}
/* gotoxy(8,ti.screenheight); cprintf(" M=%ld ",farcoreleft()); */
gotoxy(ti.screenwidth/2-12,ti.screenheight);
cputs(" Algebra Editor 2.1 ");
relxy(5,0); cputs(" F1=Help ");
relxy(5,0); cputs(" Esc=Quit ");
}
/*-----------------------------------------------------------------
Check for menu select
*/
int selection(int x, int y) {
int i,n;
n = ti.screenwidth / mwidth;
for (i=0; i<numm; ++i)
if (x <= (i%n+1)*mwidth && y==1+i/n) return i;
return -1;
}
/*--------------------------------------------------------------------
display expressions (as many as possible) starting at p.
*/
void display(node *p) {
int x,y,i,ts=0;
if (tgt) {
resize(tgt,0);
ts = ti.screenheight/2;
if (ts > tgt->sy) ts=tgt->sy;
}
y=mheight+1;
numsee = 0;
while (p) {
resize(p,0);
x = (ti.screenwidth-2 - p->sx)/2 + 2 + panx;
y += p->sy+1;
if (y+ts >= ti.screenheight) break;
show(p,0,x,y - (p->sy+1)/2);
p = p->next;
++numsee;
}
if (!p && y+ts+1 < ti.screenheight) { /* end of list */
gotoxy(ti.screenwidth/2-6,y+1);
cputs("(end of list)");
}
if (ts) {
x = (ti.screenwidth-2 - tgt->sx)/2 + 2 + panx;
y = ti.screenheight - ts + tgt->sy/2;
show(tgt,0,x,y);
textattr(mcolor);
gotoxy(2,ti.screenheight-ts-1);
for (i=2; i<ti.screenwidth; ++i) putch(hline);
gotoxy(4,ti.screenheight-ts-1);
cputs("KEY");
textattr(norm);
}
}
/*--------------------------------------------------------------------
debug print out
*/
void debug(node *p) {
int x,y;
clrscr();
resize(p,0);
x = (ti.screenwidth-2 - p->sx)/2 + 2 + panx;
y = ti.screenheight/2;
show(p,0,x,y);
getch();
}
/*-----------------------------------------------------------------
Simplify
This function converts a rational expression to normal form.
Some of the attributes of normal form: no negative exponents,
terms are sorted over mul and add.
The first movenums pushes all numbers to the left so that
when the sortnode runs it pushes them to the right and combines
them.
*/
void simplify(node *p)
{
while (calcnode(p,1));
while (fixassoc(p));
while (nosubdiv(p));
while (fixassoc(p));
while (movenums(p,0,MUL));
sortnode(p,MUL);
while (movenums(p,0,ADD));
sortnode(p,ADD);
while (combine(p));
while (calcnode(p,1));
sortnode(p,MUL);
sortnode(p,ADD);
while (bisect(p));
while (calcnode(p,1)); /* use stretch rule to reduce */
while (movenums(p,0,MUL));
}
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The following functions relate to reading and writing data files.
*/
/*--------------------------------------------------------------------
load a sample expression
*/
#define push(tt) tt->next = tos; tos = tt
#define pop(tt) tt=tos; \
if (!tos) printf("stack underflow!\n"); else tos=tos->next
node *load(char *filename) {
FILE *f;
static char tok[80];
node *tos,*p;
int i;
double t;
f = fopen(filename,"r");
if (f==NULL) {
printf("unable to open %s for read\n",filename);
pause;
return NULL;
}
tos = NULL;
while (1) {
if (1 != fscanf(f,"%24s",tok)) break;
if (tok[0]==';') {
fgets(tok,sizeof tok,f);
continue;
}
if (*tok=='?') { /* --- Options ---- */
switch (tok[1]) {
case 's': fscanf(f,"%d",&bold1); break;
case 'c': fscanf(f,"%d",&norm); break;
case 'm': fscanf(f,"%d",&mcolor); break;
case 'e': fscanf(f,"%d",&sigdig); break;
case 'd': fscanf(f,"%lg",&maxrat); break;
case 'p': fscanf(f,"%d",&maxpow); break;
case 'a': fscanf(f,"%d",&ch8); break;
case 'v': fscanf(f,"%d",&i); directvideo=(i>0); break;
case 'y': fscanf(f,"%d",&yadj); break;
}
continue;
}
if (*tok=='*') { /* ------------------ */
p = newoper(MUL);
pop(p->rt);
pop(p->lf);
push(p);
}
else if (*tok=='=') { /* ------------------ */
p = newoper(EQU);
pop(p->rt);
pop(p->lf);
push(p);
}
else if (*tok=='+') { /* ------------------ */
p = newoper(ADD);
pop(p->rt);
pop(p->lf);
push(p);
}
else if (*tok=='-' && !tok[1]) { /* ------------------ */
p = newoper(SUB);
pop(p->rt);
pop(p->lf);
push(p);
}
else if (*tok=='/') { /* ------------------ */
p = newoper(DIV);
pop(p->rt);
pop(p->lf);
push(p);
}
else if (*tok=='^') { /* ------------------ */
p = newoper(EXP);
pop(p->rt);
pop(p->lf);
push(p);
}
else if (*tok=='@') { /* ------------------ */
pop(p);
if (p->kind != NUM) printf("missing function arity %s.\n",tok);
strcpy(p->name,tok+1);
p->kind = FUN;
p->nump = p->value;
if (p->nump < 0) printf("too few args to %s.\n",tok);
if (p->nump >= MAXP) printf("too many args to %s.\n",tok);
for (i=p->nump; i--; ) {
pop(p->parm[i]);
}
push(p);
}
else if (*tok<='9' && *tok>='0' || *tok=='.' || *tok=='-') {
sscanf(tok,"%lg",&t);
p = newnum(t);
push(p);
}
else { /* non-operator, non-numeric */
p = newnode();
strcpy(p->name,tok);
p->kind = VAR;
p->nump = 0;
push(p);
}
}
fclose(f);
return tos;
}
/*-----------------------------------------------------------------
reverse the node list
*/
node *reverse(node *b) {
node *a,*c;
a = NULL;
while (b) {
c = b->next;
b->next = a;
a = b;
b = c;
}
return a;
}
/*-----------------------------------------------------------------
loadfile
*/
void loadfile(char *fn) {
if (firf) lastnode(firf)->next = reverse(load(fn));
else curf = firf = reverse(load(fn));
}
/*-----------------------------------------------------------------
fprinttree
*/
void fprinttree(FILE *f,node *p) {
int i;
for (i=0; i<p->nump; ++i)
fprinttree(f,p->parm[i]);
switch (p->kind) {
case NUM: fprintf(f," %1.15g",p->value); break;
case EQU: fprintf(f," ="); break;
case FUN: fprintf(f," %d @%s",p->nump,p->name); break;
case ADD: fprintf(f," +"); break;
case SUB: fprintf(f," -"); break;
case MUL: fprintf(f," *"); break;
case DIV: fprintf(f," /"); break;
case EXP: fprintf(f," ^"); break;
case VAR: fprintf(f," %s",p->name); break;
default: fprintf(f," ???"); break;
}
}
/*-----------------------------------------------------------------
savefile
*/
void savefile(char *fn) {
node *p;
FILE *f;
time_t t;
f = fopen(fn,"w");
if (!f) {
printf("Unable to open %s for output\n",fn);
pause; return;
}
time(&t);
fprintf(f,";\n; Saved as %s on %s;\n",fn,ctime(&t));
p = firf;
while (p) {
fprinttree(f,p);
fprintf(f,"\n;\n");
p = p->next;
}
fclose(f);
}
/*-----------------------------------------------------------------
fwritetree = f + - * / ^ V N
precedence values: 0 0 2 2 4 4 6 8 8
*/
int wpr[10] = { 0, 8, 2, 2, 4, 4, 6, 8, 8, 8 };
void fwritetree(FILE *f,node *p,int pr) {
int i;
if (pr > wpr[p->kind]) fprintf(f,"(");
switch (p->kind) {
case NUM: fprintf(f,"%1.15G",p->value); break;
case EQU:
fwritetree(f,p->lf,0);
fprintf(f," = ");
fwritetree(f,p->rt,0); break;
case FUN:
fprintf(f,"%s(",p->name);
for (i=0; i<p->nump; ++i) {
fwritetree(f,p->parm[i],0);
if (i<p->nump-1) fprintf(f,",");
}
fprintf(f,")");
break;
case ADD:
fwritetree(f,p->lf,2);
fprintf(f," + ");
fwritetree(f,p->rt,3); break;
case SUB:
fwritetree(f,p->lf,2);
fprintf(f," - ");
fwritetree(f,p->rt,3); break;
case MUL:
fwritetree(f,p->lf,4);
fprintf(f,"*");
fwritetree(f,p->rt,5); break;
case DIV:
fwritetree(f,p->lf,4);
fprintf(f,"/");
fwritetree(f,p->rt,5); break;
case EXP:
fwritetree(f,p->lf,7);
fprintf(f,"^");
fwritetree(f,p->rt,6); break;
case VAR: fprintf(f,"%s",p->name); break;
default: fprintf(f,"???"); break;
}
if (pr > wpr[p->kind]) fprintf(f,")");
}
/*-----------------------------------------------------------------
writefile this writes the file using infix notation.
*/
void writefile(char *fn) {
node *p;
FILE *f;
time_t t;
f = fopen(fn,"w");
if (!f) {
printf("Unable to open %s for output\n",fn);
pause; return;
}
time(&t);
fprintf(f,";\n; Written as %s on %s;\n",fn,ctime(&t));
p = firf;
while (p) {
fwritetree(f,p,0);
fprintf(f,"\n");
p = p->next;
}
fclose(f);
}
/*-----------------------------------------------------------------
which node is pointed to?
*/
node *find_node(node *p,int x,int y) {
int i; node *t;
switch (p->kind) {
case ADD: case MUL: case EQU:
case EXP: case SUB:
if (x == p->px && y==p->py) {
return p;
}
break;
case DIV:
if (x >= p->px && y==p->py &&
x < (p->px+p->sx)) {
return p;
}
break;
default:
if (x >= p->px && y==p->py &&
x < (p->px + p->sx)) {
return p;
}
}
for (i=0; i<p->nump; ++i)
if (!!(t=find_node(p->parm[i],x,y))) return t;
return NULL;
}
/*-----------------------------------------------------------------
substitute -- prereq tgt MUST be an EQU.
*/
void substitution(node *p) {
int i;
if (equal(p,tgt->lf))
movenode(p,deepcopy(tgt->rt));
else for (i=0; i<p->nump; ++i)
substitution(p->parm[i]);
}
/*-----------------------------------------------------------------
insert key
has special handling for equations...
*/
void insertkey(int opr) {
node *tmp,*t1,*t2;
if (!src || !tgt) return;
if (tgt->kind==EQU) {
t1 = tgt->lf; t2 = tgt->rt;
} else t1 = t2 = tgt;
if (src->kind==EQU) {
tmp = src->lf;
src->lf = newoper(opr);
src->lf->lf = tmp;
src->lf->rt = deepcopy(t1);
tmp = src->rt;
src->rt = newoper(opr);
src->rt->lf = tmp;
src->rt->rt = deepcopy(t2);
src->rt->lf = tmp;
}
else if (opr==EXP) {
tmp = newnode();
nodecpy(tmp,src);
src->kind = EXP;
src->nump = 2;
src->lf = cons(tmp,EXP,deepcopy(t1));
src->rt = cons(newnum(1),DIV,deepcopy(t2));
src = src->lf;
}
else {
tmp = newnode();
nodecpy(tmp,src);
src->kind = opr ^ 1;
src->nump = 2;
src->lf = newoper(opr);
src->rt = deepcopy(t2);
src = src->lf;
src->lf = tmp;
src->rt = deepcopy(t1);
}
}
/*-----------------------------------------------------------------
prompt the user for oneline input
return value is non-reuseable!
*/
char *keyin(char *pmt) {
static char buf[160];
window(1,ti.screenheight-4,ti.screenwidth-1,ti.screenheight-1);
textattr(norm);
clrscr();
cputs(pmt);
cputs("\r\n");
buf[0] = sizeof buf - 2;
cgets(buf);
window(1,1,ti.screenwidth,ti.screenheight);
return buf+2;
}
/*-----------------------------------------------------------------
show help file
*/
void showhelp(char *argv0) {
FILE *f;
int i,c=0;
static char s[85];
strcpy(argv0+strlen(argv0)-3,"HLP");
f = fopen(argv0,"r");
if (!f) { printf("Unable to find help file '%s'\n",argv0);
pause; return;
}
textattr(norm);
clrscr();
i = ti.screenheight-1;
while (!feof(f)) {
printf(fgets(s,80,f));
if (!--i) {
i = ti.screenheight-4;
while (!(c=getch()));
if (c==27) break;
}
}
if (c!=27) getch();
fclose(f);
}
/*--------------------------------------------------------------------
main
*/
void main(int argc,char *argv[]) {
int i,x,y,b,done=0,dmp=0,mous=1;
node *tmp,*p;
directvideo = 1; /* don't use bios */
_wscroll = 0; /* disable scrolling */
gettextinfo(&ti);
printf("ALGED: Algebra Editor by John Henckel, ver "__DATE__"\n\n");
printf("Copyright (c) 1994 by John Henckel.\n");
printf("Permission to use, copy, modify, distribute and sell this software\n");
printf("and its documentation for any purpose is hereby granted without fee,\n");
printf("provided that the above copyright notice appear in all copies.\n");
/*-----------------------------------------------------------------
init mouse
*/
if (init_mouse() != -1) {
printf("mouse driver not found\n");
mous=0;
}
/*-----------------------------------------------------------------
load data files
*/
firf = NULL;
loadfile("ALGED.1ST");
for (i=1; i<argc; ++i)
loadfile(argv[i]);
curf = firf;
/*-----------------------------------------------------------------
main loop
*/
src = curf;
while (!done) {
window(2,mheight+1,ti.screenwidth-1,ti.screenheight-1);
textattr(norm);
clrscr();
window(1,1,ti.screenwidth,ti.screenheight); /* full */
display(curf);
if (dmp && src) {
putch('\r'); putch('\n');
dumpnode(src,1);
}
show_menu();
_setcursortype(_NOCURSOR);
if (mous) show_mouse();
if (mous) while (!!(b = get_mouse(&x,&y)));
while (!mous || !(b = get_mouse(&x,&y)))
if (kbhit()) {
while (!(b = getch()));
break;
}
if (mous) hide_mouse();
_setcursortype(_NORMALCURSOR);
/*-----------------------------------------------------------------
check for keypresses
*/
i=100; /* default = do nothing */
if (b==27) break; /* escape */
else if (b==';') i=99; /* help */
else if (b=='v' && src) primefact(src);
else if (b=='.' && src) { /* copy pick to key */
p = deepcopy(src);
if (tgt) freetree(tgt);
tgt = p;
}
else if (b==73) src=curf; /* pgup = pick top */
else if (b==79 && src && src->nump>1) /* end = pick lf */
src = src->lf;
else if (b==81 && src && src->nump>1) /* pgdn = pick rt */
src = src->rt;
else if (b > 7) {
for (i=0; i<numm; ++i)
if (hotkey[i]==b) break;
if (i==numm) continue; /* invalid key */
}
else { /* mouse click */
x=x/8+1; y=y/8+1;
i = selection(x,y);
}
if (i>=0) {
switch (i) {
case 99: showhelp(argv[0]); break;
case EQK:
if (src && tgt && src->kind!=EQU && tgt->kind!=EQU) {
tmp = newoper(EQU);
tmp->lf = tgt;
tmp->rt = deepcopy(src);
tgt = tmp;
}
break;
case DIS: if (src) while(distribute(src)); break;
case CAL:
if (src && (b==1||b=='c')) {
while(movenums(src,1,MUL)); /* move up for stretch rule */
while(calcnode(src,0));
while(movenums(src,0,MUL));
}
else if (src) primefact(src);
break;
case COD:
if (src)
if (src->kind==DIV) while (distribute2(src));
else while (comdeno(src));
break;
case SIM: if (src) simplify(src); break;
case ASS: if (src) associate(src); break;
case PCO: if (src && tgt) polycoef(tgt,src); break;
case RAT: if (src) ration(src); break;
case EXX: if (src) while (exexpand(src)); break;
case EXJ: if (src) while (expjoin(src)); break;
case SBS: if (src && tgt && tgt->kind==EQU) substitution(src); break;
case QUA: if (src && tgt) quadratic(tgt,src); break;
case FAP: if (src && tgt) factrpoly(tgt,src); break;
case PLY:
if (tgt && src && src->kind==DIV) {
tmp = polydiv(tgt,src->lf,src->rt);
if (tmp) movenode(src,tmp);
while (calcnode(src,1)); /* rmv 0+x and 0*x */
}
break;
case PPR: panx -= 10; break;
case PP0: panx = 0; break;
case PPL: panx += 10; break;
case CH8: ch8=1-ch8;
if (ch8) {
hline = 196; vline = 179;
urc = 191; llc = 192;
lrc = 217; ulc = 218;
strcpy(piname,"\xE3");
}
else {
hline = '-'; vline = '|';
urc = '\\'; llc = '\\';
lrc = '/'; ulc = '/';
strcpy(piname,"pi");
}
clrscr();
break;
case WRI:
writefile(keyin("Enter filename to write to:"));
break;
case LOD:
loadfile(keyin("Enter filename to load from:"));
break;
case SAV:
savefile(keyin("Enter filename to save to:"));
break;
case CLR:
while (firf) {
tmp = firf;
firf = firf->next;
freetree(tmp);
} curf = NULL;
break;
case ADZ: insertkey(ADD); break;
case SUZ: insertkey(SUB); break;
case MUZ: insertkey(MUL); break;
case DIZ: insertkey(DIV); break;
case EXZ: insertkey(EXP); break;
case DEL:
tmp = curf;
if (!curf) break;
if (curf==firf) curf = firf = firf->next;
else curf = prevnode(curf)->next = curf->next;
freetree(tmp);
break;
case INK:
if (!tgt) break;
tgt->next = curf;
if (curf==firf) curf=firf=tgt;
else {
curf = prevnode(curf);
curf->next = tgt;
curf = tgt;
}
src = curf;
tgt = deepcopy(tgt);
break;
case ENT:
window(1,ti.screenheight-4,ti.screenwidth-1,ti.screenheight-1);
textattr(norm);
clrscr();
cputs("Enter a new key using postfix notation;"
" press F6 Enter when you are done.\r\n");
if (tgt) freetree(tgt);
tgt = load("con");
window(1,1,ti.screenwidth,ti.screenheight);
break;
case NXT:
if (curf) curf = curf->next;
if (!curf) curf = firf;
src = curf;
break;
case PRV:
if (curf==firf) curf = lastnode(curf);
else curf = prevnode(curf);
src = curf;
break;
}
}
else { /* not a menu selection */
tmp = curf;
p = NULL;
for (i=0; i<numsee && tmp; ++i) {
if (!!(p=find_node(tmp,x,y))) break;
tmp = tmp->next;
}
if (!p && tgt)
p = find_node(tgt,x,y);
if (b==1) /* LMB */
src = p;
else if (p) { /* RMB on an expression */
p = deepcopy(p);
if (tgt) freetree(tgt);
tgt = p;
}
else { /* RMB on nothing */
if (tgt) freetree(tgt);
tgt=NULL;
}
}
}
textattr(7);
clrscr();
}