www.pudn.com > calc.rar > roots.c
#include "roots.h" #include "integer.h" #include#include #include "fun.h" #include "stack.h" /* This code was written by Sean Seefried when he was a vacation student * with me at UQ Maths. Dept. */ /* Simple linked list implementation of polynomial sequence */ typedef struct _PolySeq { POLYI poly; struct _PolySeq *next; } *PolySeq; /* * Adds a POLYI to the end of a PolySeq. If supplied PolySeq is NULL then a * new PolySeq is created. */ PolySeq addPolySeqTerm(PolySeq ps, POLYI P) { PolySeq CURSOR=ps; if (ps == NULL) { if ((ps=mmalloc(sizeof (struct _PolySeq))) == NULL) return NULL; else { ps->poly=COPYPI(P); ps->next=NULL; } } else { while (CURSOR->next) CURSOR=CURSOR->next; /* CURSOR now points to last term */ CURSOR->next=mmalloc(sizeof (struct _PolySeq)); CURSOR->next->poly=COPYPI(P); CURSOR->next->next=NULL; } return ps; } /* * Frees a polynomial sequence previously created with createPolySeq */ void freePolySeq(PolySeq ps) { PolySeq CURSOR=ps; PolySeq prev; while(CURSOR) { prev=CURSOR; DELETEPI(CURSOR->poly); CURSOR=CURSOR->next; ffree(prev, sizeof (struct _PolySeq)); } } void printPolySeq(PolySeq ps) { PolySeq CURSOR=ps; printf("{"); if (ps) { PRINTPI(CURSOR->poly); while (CURSOR->next) { printf(", "); CURSOR=CURSOR->next; PRINTPI(CURSOR->poly); } } printf("}\n"); } void printPolySeqEval(PolySeq ps, MPR *R) { PolySeq CURSOR=ps; int i; printf("{"); if (ps) { i = SIGN_AT_R_PI(CURSOR->poly, R); printf("%d", i); while (CURSOR->next) { printf(", "); CURSOR=CURSOR->next; i = SIGN_AT_R_PI(CURSOR->poly, R); printf("%d", i); } } printf("}\n"); } /* * Returns the number of sign variations in the Polynomial Sequence at the * evualated at the supplied value. * A sign variation exists between two non-zero numbers c[p] and c[q] (p = p +2, the numbers c[p+1], ..., c[q-1] are all zero and c[p] * and c[q] have opposite signs. */ int signVar(PolySeq PS, MPR *R) { int prev=SIGN_AT_R_PI(PS->poly, R); /* previous polynomial in sequence's value at I */ int vars=0; /* number of variations */ MPR *Z=ZEROR(); int sign; PolySeq CURSOR=PS; while (CURSOR) { /* skip polynomials that evaluate to zero */ sign = SIGN_AT_R_PI(CURSOR->poly, R); while (sign == 0 && CURSOR->next) { CURSOR=CURSOR->next; sign = SIGN_AT_R_PI(CURSOR->poly, R); } if (prev != 0 && (prev*sign < 0)) vars++; prev=sign; CURSOR=CURSOR->next; } FREEMPR(Z); return vars; } /* * Returns a polynomial sequence that is the sturm sequence of the supplied * polynomial. See Akritas, Elements of Computer Algebra, p341 */ PolySeq sturmSeq(POLYI Pptr) { POLYI R1=COPYPI(Pptr); POLYI R2=DERIVPI(R1); POLYI TR, R; /* temp remainder, remainder, modified remainder */ MPI *CONT; PolySeq ps=NULL; ps = addPolySeqTerm(ps, R1); /* It is possible that Pptr was a constant polynomial. Hence its derivative * is zero or NULL. The Sturm sequence should consists of one term - Pptr. */ if (R2 != NULL) ps = addPolySeqTerm(ps, R2); else { DELETEPI(R1); return ps; } while ((DEGREEPI(TR=MODPI(R1,R2)) != 0) && TR != NULL) { CONT=CONTENTPI2(TR); if (SIGNI(CONT) > 0) { POLYI TMPI; R = PRIMITIVEPI(TR); DELETEPI(TR); /* free TR. not needed */ TMPI = R; R = MINUSPI(R); DELETEPI(TMPI); } else { R = PRIMITIVEPI(TR); DELETEPI(TR); } ps = addPolySeqTerm(ps, R); TR=R1; R1=R2; R2=R; DELETEPI(TR); /* deletes R1 */ FREEMPI(CONT); } if (TR != NULL) { /* can guarantee that this is a constant polynomial */ MPI *TMP; POLYI ONE_P=ONEPI(); TMP = LEADPI(TR); if (SIGNI(TMP) > 0) { POLYI NEGONE_P; MPI *CONST=MINUS_ONEI(); NEGONE_P=CONSTANTPI(CONST); addPolySeqTerm(ps, NEGONE_P); DELETEPI(NEGONE_P); FREEMPI(CONST); } else { addPolySeqTerm(ps, ONE_P); } DELETEPI(ONE_P); FREEMPI(TMP); } DELETEPI(TR); DELETEPI(R1); DELETEPI(R2); return ps; } /* * Calculates an upperbound on the positve roots of the supplied polynomial * using Cauchy's Rule. See Akritas, Elements of Computer Algebra. */ MPR *CAUCHY(POLYI P) { POLYI Q=COPYPI(P); POLYI CURSOR; MPR *RETVAL=NULL; MPI *LEAD; MPR *TWO=TWOR(); int t, deg=DEGREEPI(P); /* t a flag, deg is degree of polynomial */ int k, k_, k__, lambda=0, i, j, p, q, r; MPI *CI_=NULL; MPR *CN_=NULL; /* if the leading coefficient is less than zero then multiply polynomial by * -1 */ LEAD=LEADPI(Q); if (SIGNI(LEAD) < 0) { MPI *NEGONE=MINUS_ONEI(); POLYI TMPI; TMPI=Q; Q=SCALARPI(NEGONE, Q); DELETEPI(TMPI); FREEMPI(NEGONE); } CURSOR=Q; while (CURSOR){ if (SIGNI(CURSOR->COEF) < 0) /* count negative terms in poly */ lambda++; CURSOR=CURSOR->NEXT; } k__ = 0 ; j = BINARYB(LEAD)-1; /* j = log base 2 of LEAD */ t = 0; if (!(lambda==0 || deg==0)) { CURSOR=Q; while (CURSOR) { if (SIGNI(CURSOR->COEF) < 0) { k=Q->DEG - CURSOR->DEG; CI_ = MULT_I(CURSOR->COEF, -lambda); i = BINARYB(CI_)-1; /* i = log base 2 of CI_ */ p=i-j-1; q=p/k; r=p-k*q; if (r < 0) { r+=k; q--; } k_ = q + 1; if (r == (k-1)) { MPR *TMPR; MPR *POW; POW=POWERR_2(TWO, k*k_); TMPR=MPI_TO_MPR(Q->COEF); CN_ = MULTR(TMPR, POW); FREEMPR(POW); FREEMPR(TMPR); TMPR=MPI_TO_MPR(CI_); if (COMPARER(TMPR, CN_) > 0) { k_ = k_ + 1; } FREEMPR(CN_); FREEMPR(TMPR); } if (t==0 || k_ > k__) { k__ = k_; t = 1; } FREEMPI(CI_); } CURSOR=CURSOR->NEXT; } } RETVAL = POWERR_2(TWO, k__); FREEMPR(TWO); FREEMPI(LEAD); DELETEPI(Q); return RETVAL; } /* Returns a newly created Interval if possible. * Returns NULL on error. */ Interval createInterval(MPR* left, MPR *right) { Interval intvl; if (!(intvl = malloc (sizeof (struct _Interval)))) return NULL; intvl->left = COPYR(left); intvl->right = COPYR(right); return intvl; } /*-------------------------------------------------------------------*/ /* Frees an interval previously created with createInterval * Undefined behaviour occurs if 'intvl' does not point to a structure of * this type. */ void freeInterval(Interval intvl) { FREEMPR(intvl->left); FREEMPR(intvl->right); free(intvl); } /*-------------------------------------------------------------------*/ /* Returns a stack of intervals that contain only one root each respectively * of the supplied polynomial. If there are no roots, it returns an empty * stack */ Stack STURM(POLYI P) { Stack Bounds=stackNew(); Stack Results=stackNew(); PolySeq sseq; /* sturm sequence */ MPI *ZI, *V; MPR *BR, *ZERO=ZEROR(), *TWO = TWOR(); unsigned debugCounter=0; unsigned numRoots, pn_flag=0; /* must find square free version of polynomial or else * we cannot use the theorm of Sturm. This is done simply by dividing * the supplied polynomial by the gcd of it and it's derivative */ POLYI D=DERIVPI(P); POLYI G=GCDPI(P,D); POLYI Pw=DIVPI(P, G); DELETEPI(D); DELETEPI(G); /* if there is a zero at zero then divide polynomial by X */ ZI = ZEROI(); V = VALPI(Pw, ZI); if (COMPAREI(V, ZI) == 0) { MPI *ONE=ONEI(); POLYI X=NULL, TMPI; PINSERTPI(1, ONE, &X, 0); TMPI = Pw; Pw = DIVPI(Pw, X); DELETEPI(TMPI); DELETEPI(X); FREEMPI(ONE); } FREEMPI(V); FREEMPI(ZI); while (pn_flag < 2) { POLYI TMPPOLY; sseq = sturmSeq(Pw); BR = CAUCHY(Pw); stackPush(Bounds, createInterval(ZERO, BR)); FREEMPR(BR); /* main loop */ while (!stackEmpty(Bounds) && debugCounter < 20) { Interval intvl; intvl = stackPop(Bounds); numRoots = signVar(sseq, intvl->left) - signVar(sseq, intvl->right); debugCounter++; /* DEBUG INFO */ /* printf("The number of roots in the interval ("); PRINTR(intvl->left); printf(", "); PRINTR(intvl->right); printf(") is %d\n", numRoots); printPolySeqEval(sseq, intvl->left); printPolySeqEval(sseq, intvl->right); */ /* DEBUG END */ if (numRoots == 1) { MPR *L, *R; L = intvl->left; R = intvl->right; if (pn_flag == 1) { MPR *TMPR; TMPR = MINUSR(L); /* swap left and right of interval because * they are now negative values */ L = MINUSR(R); R = TMPR; } stackPush(Results, createInterval(L, R)); if (pn_flag == 1) { FREEMPR(L); FREEMPR(R); } } else if (numRoots > 1) { /* numroots > 1 */ MPR *T, *CENTRE; T = ADDR(intvl->left,intvl->right); CENTRE = RATIOR(T,TWO); FREEMPR(T); /* New intervals are (L, (L+R)/2) and ((L+R)/2, R) */ stackPush(Bounds, createInterval(intvl->left, CENTRE)); stackPush(Bounds, createInterval(CENTRE, intvl->right)); /* debug code */ /* printf("This interval has been subdivided into: ("); PRINTR(intvl->left); printf(", "); PRINTR(CENTRE); printf(") , ("); PRINTR(CENTRE); printf(", "); PRINTR(intvl->right); printf(")\n"); */ /* debug code end */ FREEMPR(CENTRE); } freeInterval(intvl); } TMPPOLY=Pw; Pw=P_OF_NEG_X(Pw); DELETEPI(TMPPOLY); freePolySeq(sseq); pn_flag++; } stackFree(&Bounds); FREEMPR(ZERO); FREEMPR(TWO); DELETEPI(Pw); return Results; } /*-------------------------------------------------------------------*/ /* A wrapper function that takes a single polynomial as its argument. * (Handled by parser). Returns open rational intervals that are * guaranteed to enclose the real roots of the supplied polynomial. */ void STURM_W(Stack s) { POLYI P=stackPop(s); Stack Results=STURM(P); FILE *outfile; if (!(outfile = fopen("sturm.out", "w"))) { printf("Could not open file sturm.out for writing\n"); exit(1); } fprintf(outfile, "The intervals containing only one root for the polynomial:\n"); FPRINTPI(outfile, P); fprintf(outfile, " are:\n"); DELETEPI(P); while (!stackEmpty(Results)) { Interval intvl; intvl = stackPop(Results); printf("("); PRINTR(intvl->left); printf(", "); PRINTR(intvl->right); printf(")\n"); fprintf(outfile, "("); FPRINTR(outfile, intvl->left); fprintf(outfile, ", "); FPRINTR(outfile, intvl->right); fprintf(outfile, ")\n"); freeInterval(intvl); } fclose(outfile); stackFree(&Results); } /* Using a bisection method this finds the integer part root of the * supplied polynomial in the supplied _OPEN_ interval. This function is * only guaranteed to return the correct value if the interval * supplied contains exactly one root, and this root does not fall at the * end points. */ MPI *rootInIntervalI(POLYI P, MPI *LEFT, MPI *RIGHT) { MPI *TMP, *LOW=COPYI(LEFT), *HIGH=COPYI(RIGHT), *MID; MPI *ONE = ONEI(); MPI *TWO = TWOI(); TMP=ADDI(LOW, ONE); while (COMPAREI(TMP, HIGH) != 0) { MPI *HIGHVAL, *MIDVAL; FREEMPI(TMP); MID=ADDI(LOW,HIGH); TMP = MID; MID=INTI(MID, TWO); FREEMPI(TMP); MIDVAL = VALPI(P, MID); HIGHVAL = VALPI(P, HIGH); if (SIGNI(MIDVAL) != SIGNI(HIGHVAL)) { TMP = LOW; LOW = MID; FREEMPI(TMP); } else { TMP = HIGH; HIGH = MID; FREEMPI(TMP); } FREEMPI(MIDVAL); FREEMPI(HIGHVAL); TMP = ADDI(LOW, ONE); } FREEMPI(ONE); FREEMPI(TWO); FREEMPI(TMP); FREEMPI(HIGH); return LOW; } /* Using a bisection method this finds the integer part root of the * supplied polynomial in the supplied _OPEN_ interval. This function is * only guaranteed to return the correct value if the interval * supplied contains exactly one root, and this root does not fall at the * end points. * If RIGHT == NULL this signifies that we are searching for a root in * the open interval (LEFT, INFINITY). */ MPI *rootInIntervalR(POLYI P, MPR *LEFT, MPR *RIGHT) { MPR *TMP, *LOW=COPYR(LEFT), *HIGH, *MID; MPR *TWO = TWOR(); MPI *INT_LOW, *INT_HIGH; /* stores the integer part of LOW and HIGH */ if (RIGHT != NULL) HIGH = COPYR(RIGHT); else HIGH = CAUCHY(P); INT_LOW = INTI(LOW->N, LOW->D); INT_HIGH= INTI(HIGH->N, HIGH->D); while (COMPAREI(INT_LOW, INT_HIGH) != 0) { FREEMPI(INT_LOW); FREEMPI(INT_HIGH); MID=ADDR(LOW,HIGH); TMP = MID; MID=RATIOR(MID, TWO); FREEMPR(TMP); if (SIGN_AT_R_PI(P, MID) != SIGN_AT_R_PI(P, HIGH)) { TMP = LOW; LOW = MID; FREEMPR(TMP); } else { TMP = HIGH; HIGH = MID; FREEMPR(TMP); } INT_LOW = INTI(LOW->N, LOW->D); INT_HIGH= INTI(HIGH->N, HIGH->D); } FREEMPR(TWO); FREEMPI(INT_HIGH); FREEMPR(HIGH); FREEMPR(LOW); return INT_LOW; } /* Finds the continued fraction expansion of all real roots of the supplied * polynomial using Lagrange's method and methods first presented in a paper * by Cantor, Galyean and Zimmer called A Continued Fraction Algorithm for * Real Algebraic Number */ void ROOTEXPANSION(Stack s) { POLYI P = stackPop(s); MPI *M = stackPop(s); POLYI Q, CURSOR; /* Making P square free */ POLYI D=DERIVPI(P); POLYI G=GCDPI(P,D); POLYI TMPP; Stack intvlStack; Interval intvl; FILE *outfile; MPR *ONE = ONER(); MPI *NUMQUO, *TMPI; MPI *ZERO =ZEROI(); MPR *TMP; MPIA LANGFRAC; /* the lagrange partial quotients */ int i=0, j, d; /* main loop */ TMPP = P; P = DIVPI(P,G); DELETEPI(TMPP); DELETEPI(G); DELETEPI(D); intvlStack = STURM(P); if (!(outfile = fopen("rootexp.out", "w"))) { printf("Error: Could not open file rootexp.out for writing\n"); exit(1); } while (!stackEmpty(intvlStack)) { MPI *A=NULL; /* next partial quotient */ MPR *AR=NULL; MPR *RN, *RP=NULL; /* next and previous left bound */ MPR *SN, *SP=NULL; /* next and previous right bound */ MPIA CONTFRAC=BUILDMPIA(); /* the zimmer partial quotients */ MPIA COEF; /* coefficients for the transformation */ /* SN will equal NULL to represent INFINITY */ intvl = stackPop(intvlStack); Q = COPYPI(P); RN = COPYR(intvl->left); SN = COPYR(intvl->right); i = 0; while(COMPARER(RN, ONE) != 0 || SN != NULL) { A = rootInIntervalR(Q, RN, SN); AR = MPI_TO_MPR(A); ADD_TO_MPIA(CONTFRAC, A, i++); FREEMPR(RP); /* ok even if null */ FREEMPR(SP); RP = RN; SP = SN; TMP = ADDR(AR, ONE); if (SP != NULL && COMPARER(SP, TMP) < 0) { MPR *TMP2; RN = SUBR(SP, AR); TMP2 = RN; RN = INVERSER(RN); FREEMPR(TMP2); } else RN = ONER(); FREEMPR(TMP); if (COMPARER(RP, AR) > 0) { MPR *TMP2; SN = SUBR(RP, AR); TMP2 = SN; SN = INVERSER(SN); FREEMPR(TMP2); } else SN = NULL; P_OF_X_PLUS(&Q, A); FREEMPI(A); FREEMPR(AR); /* This performs transformation p(x) = x^d*p(1/x +a) */ COEF = BUILDMPIA(); d = DEGREEPI(Q); for (j=0; j<= d; j++) ADD_TO_MPIA(COEF, ZERO, j); CURSOR = Q; while(CURSOR) { ADD_TO_MPIA(COEF, CURSOR->COEF, DEGREEPI(CURSOR)); CURSOR=CURSOR->NEXT; } DELETEPI(Q); Q = NULL; for (j=0; j <= d; j++) PINSERTPI(d-j, COEF->A[j], &Q, 0); PURGEPI(&Q); FREEMPIA(COEF); /* end of transformation */ } /* the zimmer method is finished. We can use Lagrange's method now */ FREEMPR(RN); FREEMPR(RP); FREEMPR(SN); FREEMPR(SP); if (SIGNI(Q->COEF) == -1) { POLYI TMPPOLY; TMPPOLY = Q; Q = MINUSPI(Q); DELETEPI(TMPPOLY); } /* this bit runs lagrange */ NUMQUO = CHANGEI(i); TMPI = NUMQUO; NUMQUO = SUBI(M, NUMQUO); FREEMPI(TMPI); LAGRANGE(Q, &LANGFRAC, NUMQUO); FREEMPI(NUMQUO); /* now to output the continued fractions */ fprintf(outfile, "Continued fraction expansion of roots for the polynomial: \n"); FPRINTPI(outfile, P); fprintf(outfile, "\n"); printf("The root in the interval ("); PRINTR(intvl->left); printf(", "); PRINTR(intvl->right); printf(") has the continued fraction expansion: \n"); fprintf(outfile, "The root in the interval ("); FPRINTR(outfile, intvl->left); fprintf(outfile, ", "); FPRINTR(outfile, intvl->right); fprintf(outfile, ") has the continued fraction expansion: \n"); freeInterval(intvl); for (j=0; j < CONTFRAC->size; j++) { printf("[%d]: ", j); PRINTI(CONTFRAC->A[j]); printf("\n"); fprintf(outfile, "[%d]: ", j); FPRINTI(outfile, CONTFRAC->A[j]); fprintf(outfile, "\n"); } for (j=0; j< LANGFRAC->size; j++) { printf("[%d]: ", j+CONTFRAC->size); PRINTI(LANGFRAC->A[j]); printf("\n"); fprintf(outfile, "[%d]: ", j); FPRINTI(outfile, LANGFRAC->A[j]); fprintf(outfile, "\n"); } printf("--------------------------------------------------\n"); fprintf(outfile, "--------------------------------------------------\n"); FREEMPIA(CONTFRAC); FREEMPIA(LANGFRAC); DELETEPI(Q); } /* end of main loop */ fclose(outfile); stackFree(&intvlStack); FREEMPR(ONE); FREEMPI(ZERO); DELETEPI(P); FREEMPI(M); } USI SIGN_COUNT(MPIA A){ /* This counts the number of sign changes in the array A */ USI i,j,n, changes; MPIA S; j=0; n=A->size; S=BUILDMPIA(); for(i=0;iA[i]->S)==0){ continue; }else{ ADD_TO_MPIA(S, A->A[i], j); j=j+1; } } changes=0; for(i=0;i A[i]->S)*(S->A[i+1]->S)<0){ changes=changes+1; } } FREEMPIA(S); return(changes); } MPI *STURM_SEQUENCE(POLYI P, MPI *B, MPI *E){ POLYI TEMPPOLYI, PA, PB; MPIA VALUES; int n; MPI *TEMP, *MINUSONE; USI count, i, changes; MINUSONE = MINUS_ONEI(); VALUES = BUILDMPIA(); PA = PRIMITIVEPI_(P); if(E->S){ printf("STURM[0]="); PRINTPI(PA); printf("\n"); } TEMP = VALPI(PA, B); ADD_TO_MPIA(VALUES, TEMP, 0); FREEMPI(TEMP); TEMPPOLYI = DERIVPI(PA); PB = PRIMITIVEPI_(TEMPPOLYI); DELETEPI(TEMPPOLYI); if(E->S){ printf("STURM[1]="); PRINTPI(PB); printf("\n"); } TEMP = VALPI(PB, B); ADD_TO_MPIA(VALUES, TEMP, 1); FREEMPI(TEMP); n = DEGREEPI(PB); count = 2; while(n > 0){ TEMPPOLYI = PB; PB = MODPI(PA, PB); n = DEGREEPI(PB); DELETEPI(PA); PA = TEMPPOLYI; TEMPPOLYI = PB; PB = PRIMITIVEPI_(PB); DELETEPI(TEMPPOLYI); TEMPPOLYI = PB; PB = SCALARPI(MINUSONE, PB); DELETEPI(TEMPPOLYI); if(E->S){ printf("STURM[%u]=",count); PRINTPI(PB); printf("\n"); } TEMP = VALPI(PB, B); ADD_TO_MPIA(VALUES, TEMP, count); FREEMPI(TEMP); count++; } if(E->S){ for(i = 0;i < count;i++){ PRINTI(VALUES->A[i]); printf(", "); } printf("\n"); } changes = SIGN_COUNT(VALUES); FREEMPIA(VALUES); FREEMPI(MINUSONE); DELETEPI(PA); DELETEPI(PB); return(CHANGE(changes)); }