/* * C++ class to implement a power series type and to allow * arithmetic on it * * WARNING: This class has been cobbled together for a specific use with * the MIRACL library It is not complete, and may not work in other * applications * * See Knuth The Art of Computer Programming Vol.2, Chapter 4.7 */ #include "ps_zzn.h" #define FFT // // all calulations are mod x^psN // Power series is stored {as offset, pwr and a0+a1.x+a2.x^2+a3.x^3....} // where the power of x is to be multiplied by pwr, and the whole power // series is to be divided by x^offset // int psN; // // Copy Constructor // Ps_ZZn::Ps_ZZn(const Ps_ZZn& p) { term_ps_zzn *ptr=p.start; term_ps_zzn *pos=NULL; int pw; start=NULL; offset=p.offset; pwr=p.pwr; while (ptr!=NULL) { pw=ptr->n*p.pwr-p.offset; // conversion needed if (pw>=psN) break; pos=addterm(ptr->an,pw,pos); ptr=ptr->next; } } // // decompresses PS by reducing pwr // void Ps_ZZn::decompress(int m) { // m is divisor of current pwr // e.g pwr is 6 and PS = 1 + x + x^2 // If m=2 then pwr becomes 3 and PS = 1 +x^2 +x^4.... term_ps_zzn *ptr=start; if (start==NULL || m==1 || pwr==1) return; // it is fully decompressed while (ptr!=NULL) { ptr->n*=m; ptr=ptr->next; } pwr/=m; } // // Sets new pwr value. Parameter must exactly divide // all powers in the series // void Ps_ZZn::compress(int p) { term_ps_zzn *ptr=start; if (p==1) return; while (ptr!=NULL) { ptr->n/=p; ptr=ptr->next; } pwr=p; } // // insert missing terms with 0 coefficients // void Ps_ZZn::pad() { // insert any missing 0 coefficients int i=0; term_ps_zzn *ptr=start; term_ps_zzn *pos=NULL; while (ptr!=NULL) { while (in) { pos=addterm((ZZn)0,i*pwr-offset,pos); i++; } i++; ptr=ptr->next; } while (ian); if (b>m) m=b; ptr=ptr->next; } return m; } // // set new offset, so first power of x is 0 // void Ps_ZZn::norm() { int m; term_ps_zzn *ptr; if (start==NULL) return; // // remove any leading 0 terms // while (start->an.iszero()) { ptr=start->next; delete start; start=ptr; if (start==NULL) return; } ptr=start; m=start->n; if (m!=0) { offset-=m*pwr; while (ptr!=NULL) { ptr->n-=m; ptr=ptr->next; } } } // // Dedekind Eta function (1-x)(1-x^2)(1-x^3).... // Ps_ZZn eta() { // simple repeating pattern BOOL even; int one,ce,co,c; term_ps_zzn *pos=NULL; Ps_ZZn n; n.addterm((ZZn)1,0); n.addterm((ZZn)-1,1); n.addterm((ZZn)-1,2); ce=2;co=1; even=TRUE; c=2; one=1; while (can.iszero()) t++; if (t>1) return FALSE; ptr=ptr->next; } return TRUE; } // // add a term to a PS // term_ps_zzn* Ps_ZZn::addterm(const ZZn& a,int power,term_ps_zzn* pos) { term_ps_zzn* newone; term_ps_zzn* ptr; term_ps_zzn *t,*iptr; int dc,pw; ptr=start; iptr=NULL; // // intelligently determine the most compressed form to use // for example if coefficient a=1 always, and power = -7 -5 -3 -1 1 3.... // then set pwr=2, offset=7 and PS = 1 + x + x^2 // pw=power+offset; if (one_term() && pw!=0) { // when PS has only one term, pwr is undefined if (pw<0) pwr=-pw; else pwr=pw; } dc=igcd(pw,pwr); if (dc != pwr) decompress(pwr/dc); power=pw/pwr; // quick scan through to detect if term exists already // and to find insertion point if (pos!=NULL) ptr=pos; while (ptr!=NULL) { if (ptr->n==power) { ptr->an+=a; if (ptr->an.iszero()) { // delete term if (ptr==start) { // delete first one start=ptr->next; delete ptr; norm(); return start; } iptr=ptr; ptr=start; while (ptr->next!=iptr)ptr=ptr->next; ptr->next=iptr->next; delete iptr; return ptr; } return ptr; } if (ptr->nnext; } newone=new term_ps_zzn; newone->next=NULL; newone->an=a; newone->n=power; pos=newone; if (start==NULL) { start=newone; norm(); return pos; } // insert at the start if (iptr==NULL) { t=start; start=newone; newone->next=t; norm(); return pos; } // insert new term t=iptr->next; iptr->next=newone; newone->next=t; return pos; } // // Destructor // Ps_ZZn::~Ps_ZZn() { term_ps_zzn *nx; while (start!=NULL) { nx=start->next; delete start; start=nx; } } // // get coefficient of actual power // ZZn Ps_ZZn::coeff(int power) const { ZZn c=0; term_ps_zzn *ptr=start; if ((power+offset)%pwr != 0) return c; // no such term power=(power+offset)/pwr; while (ptr!=NULL) { if (ptr->n==power) { c=ptr->an; return c; } ptr=ptr->next; } return c; } // // get coefficient of "Internal" power // ZZn Ps_ZZn::cf(int power) const { ZZn c=0; term_ps_zzn *ptr=start; while (ptr!=NULL) { if (ptr->n==power) { c=ptr->an; return c; } ptr=ptr->next; } return c; } // // Zeroise PS and reclaim space // void Ps_ZZn::clear() { term_ps_zzn *ptr; while (start!=NULL) { ptr=start->next; delete start; start=ptr; } offset=0; pwr=1; } // Note: real power = internal power * pwr - offset Ps_ZZn& Ps_ZZn::operator+=(const Ps_ZZn& p) { term_ps_zzn *ptr=p.start; term_ps_zzn *pos=NULL; int pw; while (ptr!=NULL) { pw=ptr->n*p.pwr-p.offset; // convert compressed to real if (pw>=psN) break; pos=addterm(ptr->an,pw,pos); ptr=ptr->next; } return *this; } Ps_ZZn operator-(const Ps_ZZn& p) { Ps_ZZn r=p; term_ps_zzn *ptr=r.start; while (ptr!=NULL) { ptr->an=(-ptr->an); ptr=ptr->next; } return r; } Ps_ZZn& Ps_ZZn::operator-=(const Ps_ZZn& p) { term_ps_zzn *ptr=p.start; term_ps_zzn *pos=NULL; int pw; while (ptr!=NULL) { pw=ptr->n*p.pwr-p.offset; if (pw>=psN) break; pos=addterm(-(ptr->an),pw,pos); ptr=ptr->next; } return *this; } Ps_ZZn operator+(const Ps_ZZn& a,const Ps_ZZn& b) { Ps_ZZn sum=a; sum+=b; return sum; } Ps_ZZn operator-(const Ps_ZZn& a,const Ps_ZZn& b) { Ps_ZZn sum=a; sum-=b; return sum; } // // In situ multiplication. Coeeficients in multiplicand // are directly overwritten. Saves a lot of copying. // Ps_ZZn& Ps_ZZn::operator*=(Ps_ZZn& b) { term_ps_zzn *ptr,*bptr; int g,d,deg,ka,kb; if (IsInt()) { if (start!=NULL) *this = start->an*b; return *this; } if (b.IsInt()) { if (b.start==NULL) clear(); else *this *=(b.start->an); return *this; } g=igcd(pwr,b.pwr); deg=psN/g; #ifdef FFT if (deg>=FFT_BREAK_EVEN) { big *A,*B; A=(big *)mr_alloc(deg,sizeof(big)); B=(big *)mr_alloc(deg,sizeof(big)); ka=pwr/g; decompress(ka); pad(); ptr=start; while (ptr!=NULL) { d=ptr->n; if (d>=deg) break; A[d]=getbig(ptr->an); ptr=ptr->next; } kb=b.pwr/g; b.decompress(kb); bptr=b.start; while (bptr!=NULL) { d=bptr->n; if (d>=deg) break; B[d]=getbig(bptr->an); bptr=bptr->next; } mr_ps_zzn_mul(deg,A,B,A); mr_free(B); mr_free(A); b.compress(kb); } else { #endif *this=*this*b; return *this; #ifdef FFT } #endif norm(); offset+=b.offset; return *this; } Ps_ZZn operator*(Ps_ZZn& a,Ps_ZZn& b) { Ps_ZZn prod; ZZn t; term_ps_zzn *aptr,*bptr,*pos; int ka,kb,i,pa,pb,d,deg,g; if (a.IsInt()) { if (a.start==NULL) return prod; else return (a.start->an*b); } if (b.IsInt()) { if (b.start==NULL) return prod; else return (b.start->an*a); } g=igcd(a.pwr,b.pwr); deg=psN/g; #ifdef FFT if (deg>=FFT_BREAK_EVEN) { // use fast methods big *A,*B,*C; A=(big *)mr_alloc(deg,sizeof(big)); B=(big *)mr_alloc(deg,sizeof(big)); C=(big *)mr_alloc(deg,sizeof(big)); char *memc=(char *)memalloc(deg); for (i=0;in; if (d >= deg) break; A[d]=getbig(aptr->an); aptr=aptr->next; } kb=b.pwr/g; b.decompress(kb); bptr=b.start; while (bptr!=NULL) { d=bptr->n; if (d >= deg) break; B[d]=getbig(bptr->an); bptr=bptr->next; } mr_ps_zzn_mul(deg,A,B,C); pos=NULL; for (d=0;dn*b.pwr-b.offset; pos=NULL; while (aptr!=NULL) { pa=aptr->n*a.pwr-a.offset; if (pb+pa>=psN) break; pos=prod.addterm(aptr->an*bptr->an,pa+pb,pos); aptr=aptr->next; } bptr=bptr->next; } #ifdef FFT } #endif if (prod.start!=NULL) prod.offset=a.offset+b.offset; return prod; } Ps_ZZn operator/(const ZZn& num,const Ps_ZZn& b) { Ps_ZZn quo; term_ps_zzn *pos=NULL; int pw=b.pwr; ZZn w,v0=b.cf(0); for (int n=0;n=psN) break; if (n==0) w=num; else w=0; for (int k=0;k=psN) break; w=a.cf(n); for (int k=0;kan/=x; ptr=ptr->next; } return *this; } Ps_ZZn& Ps_ZZn::operator/=(int m) { term_ps_zzn *ptr=start; while (ptr!=NULL) { ptr->an/=m; ptr=ptr->next; } return *this; } Ps_ZZn operator/(const Ps_ZZn& a,const ZZn& b) { Ps_ZZn quo; quo=a; quo/=b; return quo; } Ps_ZZn operator/(const Ps_ZZn& a,int m) { Ps_ZZn quo; quo=a; quo/=m; return quo; } Ps_ZZn& Ps_ZZn::operator=(int m) { clear(); if (m!=0) addterm((ZZn)m,0); return *this; } Ps_ZZn& Ps_ZZn::operator=(const Ps_ZZn& p) { term_ps_zzn *ptr; term_ps_zzn *pos=NULL; clear(); int pw; pwr=p.pwr; offset=p.offset; ptr=p.start; while (ptr!=NULL) { pw=ptr->n*p.pwr-p.offset; if (pw>=psN) break; pos=addterm(ptr->an,pw,pos); ptr=ptr->next; } return *this; } Ps_ZZn& Ps_ZZn::operator*=(const ZZn& x) { term_ps_zzn *ptr=start; if (x.iszero()) { clear(); return *this; } while (ptr!=NULL) { ptr->an*=x; ptr=ptr->next; } return *this; } Ps_ZZn operator*(const ZZn& z,const Ps_ZZn &p) { Ps_ZZn r=p; r*=z; return r; } Ps_ZZn operator*(const Ps_ZZn &p,const ZZn& z) { Ps_ZZn r=p; r*=z; return r; } Ps_ZZn pow(Ps_ZZn &a,int k) { Ps_ZZn res; int w,e; if (k==0) { res=1; return res; } res=a; if (k==1) return res; e=k; w=1; while (w<=e) w<<=1; w>>=1; e-=w; w/=2; while (w>0) { res*=res; if (e>=w) { e-=w; res*=a; } w/=2; } res.offset*=k; return res; } Ps_ZZn power(const Ps_ZZn &a,int e) { // return f(a^e), e is +ve Ps_ZZn res; term_ps_zzn *ptr; term_ps_zzn *pos=NULL; ptr=a.start; while (ptr!=NULL) { if ((ptr->n*a.pwr)*e < psN) { pos=res.addterm(ptr->an,ptr->n,pos); ptr=ptr->next; } else break; } res.pwr=e*a.pwr; res.offset=e*a.offset; return res; } ostream& operator<<(ostream& s,const Ps_ZZn& p) { BOOL first=TRUE; ZZn a; term_ps_zzn *ptr=p.start; int pw; if (ptr==NULL) { s << "0"; return s; } while (ptr!=NULL) { a=ptr->an; if (a.iszero()) { ptr=ptr->next; continue; } if ((Big)a < get_modulus()/2) { a=(-a); s << " - "; } else if (!first) s << " + "; first=FALSE; pw=ptr->n*p.pwr-p.offset; if (pw==0) { s << a; ptr=ptr->next; continue; } if (a==(ZZn)1) s << "x"; else s << a << "*x"; if (pw!=1) s << "^" << pw; ptr=ptr->next; } return s; }