home *** CD-ROM | disk | FTP | other *** search
- /*--------------------------------------------------------------------
- Alged: Algebra Editor henckel@vnet.ibm.com
-
- 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.
- */
- #include "alged.h"
- /*-----------------------------------------------------------------
- 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);
-
- /* Calculate remainder trivial stuff */
-
- while (calcnode(p->rt,1));
-
- /* 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 ==> a(x + -----------------) (x + -----------------)
- 2a 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);
- }
-
- /*-----------------------------------------------------------------
- use cubic equation to factor a degree-3 polynomial
- */
- void cubic(node *base,node *pol) {
- node **coef;
- int sz;
- int i,k=1;
- node *a3,*a,*b,*c,*A,*B,*Q,*p,*q,*y1,*y2,*y3,*r;
-
- coef = maketable(base,pol,&sz);
-
- if (sz != 4) {
- for (i=0; i<sz; ++i) freetree(coef[i]);
- free(coef);
- return;
- }
- a = cons(deepcopy(coef[2]),DIV,deepcopy(coef[3]));
- b = cons(deepcopy(coef[1]),DIV,deepcopy(coef[3]));
- c = cons(deepcopy(coef[0]),DIV,deepcopy(coef[3]));
-
- while(calcnode(a,k));
- while(calcnode(b,k));
- while(calcnode(c,k));
-
- a3= cons(deepcopy(a),DIV,newnum(3));
- p = cons(deepcopy(b),SUB,cons(deepcopy(a),MUL,deepcopy(a3)));
- q = cons(newnum(2),MUL,cons(deepcopy(a3),EXP,newnum(3)));
- q = cons(q,SUB,cons(deepcopy(a3),MUL,deepcopy(b)));
- q = cons(q,ADD,deepcopy(c));
-
- while(calcnode(p,k));
- while(calcnode(q,k));
-
- Q = cons(cons(deepcopy(p),DIV,newnum(3)),EXP,newnum(3));
- Q = cons(Q,ADD,cons(cons(deepcopy(q),DIV,newnum(2)),EXP,newnum(2)));
-
- while(calcnode(Q,k));
-
- if (Q->kind!=NUM || Q->value >= 0) { /* not "irreducible" case */
- A = cons(deepcopy(q),DIV,newnum(-2));
- A = cons(A,ADD,cons(deepcopy(Q),EXP,newnum(0.5)));
- A = cons(A,EXP,newnum(1.0/3.0));
-
- B = deepcopy(A);
- B->lf->kind = SUB;
-
- while(calcnode(A,k));
- while(calcnode(B,k));
-
- y1 = cons(deepcopy(A),ADD,deepcopy(B));
- y2 = cons(deepcopy(y1),DIV,newnum(2));
- y2->lf->kind = SUB;
- y2 = cons(y2,MUL,cons(newvar("i"),MUL,cons(newnum(3),EXP,newnum(0.5))));
- y2 = cons(cons(deepcopy(y1),DIV,newnum(-2)),ADD,y2);
-
- y3 = deepcopy(y2);
- y3->kind = SUB;
-
- while(calcnode(y1,k));
- while(calcnode(y2,k));
- while(calcnode(y3,k));
-
- r = deepcopy(coef[3]);
- r = cons(r,MUL,cons(deepcopy(base),SUB,cons(y1,SUB,deepcopy(a3))));
- r = cons(r,MUL,cons(deepcopy(base),SUB,cons(y2,SUB,deepcopy(a3))));
- r = cons(r,MUL,cons(deepcopy(base),SUB,cons(y3,SUB,deepcopy(a3))));
-
- movenode(pol,r);
-
- freetree(A);
- freetree(B);
- }
-
- for (i=0; i<sz; ++i) freetree(coef[i]);
- freetree(a);
- freetree(b);
- freetree(c);
- freetree(a3);
- freetree(Q);
- freetree(p);
- freetree(q);
- 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), 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) { /* always true!! */
- while (distribute(ans->rt));
- simplify(ans->rt);
- while (calcnode(ans->rt,0));
- 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
- Note: this doesn't work unless all the coefficients are
- integers, or at least, not some form of quotient like (a*x/b).
- */
- 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 */
-
- // debug(coef[0]); debug(coef[sz-1]);
- if (coef[0]->kind!=NUM) {
- if (coef[0]->rt->kind==DIV && // coef is (0 + a/b) rational number
- (a = coef[0]->rt->lf)->kind == NUM &&
- (b = coef[0]->rt->rt)->kind == NUM &&
- coef[sz-1]->kind!=NUM) {
- coef[sz-1]->kind = MUL;
- coef[sz-1]->lf = b; // move b to the leading coef
- if (coef[0]->kind==SUB)
- a->value = -a->value;
- freenode(coef[0]->lf);
- freenode(coef[0]->rt);
- freenode(coef[0]);
- coef[0] = a;
- }
- else {
- while (calcnode(coef[0],0));
- if (coef[0]->kind!=NUM) {
- // primefact(coef[0]); this may cause trouble with exponents
- // while (distribute_c(coef[0])); // y^(2*2) = y^2 * y^2
- while (exexpand(coef[0]));
- while (nosubdiv(coef[0]));
- while (distribute_c(coef[0])); // (xy)^-1 = x^-1 * y^-1
- while (fixassoc(coef[0]));
- }
- }
- }
- while (calcnode(coef[sz-1],0));
- // debug(coef[0]); debug(coef[sz-1]);
- 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; \
- while(distribute(nm)); \
- simplify(nm); \
- simplify(nm); \
- ++n; \
- continue; \
- }
- DO_CHECK
- dn->rt = cons(dn->rt,MUL,newnum(-1));
- DO_CHECK
- dn->rt = cons(dn->rt,MUL,newvar("i"));
- 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;
- }
-
-