KGC_TEST/KGC/miracl/source/curve/polymod.cpp

375 lines
8.1 KiB
C++

/*
* C++ class to implement a polynomial type and to allow
* arithmetic on polynomials whose elements are from
* the finite field mod p.
*
* 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
*
* This type is automatically reduced
* wrt a polynomial Modulus.
*
* See Knuth The Art of Computer Programming Vol.2, Chapter 4.6
*/
#include "polymod.h"
Poly Modulus;
BOOL iszero(const PolyMod& m) {return iszero(m.p);}
BOOL isone(const PolyMod& m) {return isone(m.p);}
int degree(const PolyMod& m) {return degree(m.p);}
BOOL operator==(const PolyMod& a,const PolyMod& b)
{
PolyMod diff=a-b;
if (iszero(diff)) return TRUE;
return FALSE;
}
BOOL operator!=(const PolyMod& a,const PolyMod& b)
{
PolyMod diff=a-b;
if (iszero(diff)) return FALSE;
return TRUE;
}
ZZn PolyMod::coeff(int i) const {return p.coeff(i);}
PolyMod& PolyMod::operator*=(const PolyMod &b)
{
int i,m,deg,dega,degb,degm=degree(Modulus);
big *G,*B,*R;
BOOL squaring;
term *ptr=p.start;
term *newone,*iptr;
squaring=FALSE;
if (this==&b) squaring=TRUE;
dega=degree(*this);
deg=dega;
if (!squaring)
{
degb=degree(b);
if (degb<dega) deg=degb;
}
else degb=dega;
if (dega+degb-degm < FFT_BREAK_EVEN || deg < FFT_BREAK_EVEN)
{ // residue is small, or at least one of a or b is small
reduce(p*b.p,*this);
return *this;
}
R=(big *)mr_alloc(degm,sizeof(big));
// pad out this to have zero terms
iptr=NULL;
m=degm-1;
do
{
if (ptr==NULL || ptr->n!=m)
{
newone=new term;
newone->next=ptr;
newone->an=(ZZn)0;
newone->n=m;
ptr=newone;
if (iptr==NULL) p.start=ptr=newone;
else iptr->next=ptr;
}
R[m]=getbig(ptr->an);
m--;
iptr=ptr;
ptr=ptr->next;
} while (m>=0);
deg=dega+degb;
if (!squaring) B=(big *)mr_alloc(degb+1,sizeof(big));
G=(big *)mr_alloc(deg+1,sizeof(big));
char *memg=(char *)memalloc(deg+1);
for (i=0;i<=deg;i++) G[i]=mirvar_mem(memg,i);
if (!squaring)
{
ptr=b.p.start;
while (ptr!=NULL)
{
B[ptr->n]=getbig(ptr->an);
ptr=ptr->next;
}
mr_poly_mul(dega,R,degb,B,G);
}
else mr_poly_sqr(dega,R,G);
if (!mr_poly_rem(deg,G,R))
{ // reset the modulus - things have changed
setmod(Modulus);
mr_poly_rem(deg,G,R);
}
// now delete any 0 terms
ptr=p.start;
iptr=NULL;
while (ptr!=NULL)
{
if (ptr->an.iszero())
{
if (ptr==p.start)
p.start=ptr->next;
else
iptr->next=ptr->next;
newone=ptr;
ptr=ptr->next;
delete newone;
continue;
}
iptr=ptr;
ptr=ptr->next;
}
mr_free(R);
memkill(memg,deg+1);
mr_free(G);
if (!squaring) mr_free(B);
return *this;
}
PolyMod operator*(const PolyMod &a,const PolyMod& b)
{
PolyMod prod=a;
prod*=b;
return prod;
}
void reduce(const Poly& p,PolyMod& rem)
{
int i,d;
ZZn t;
big *G,*R;
term *ptr,*pos=NULL;
int n=degree(p);
int degm=degree(Modulus);
if (n-degm < FFT_BREAK_EVEN)
{
rem=(PolyMod)p;
return;
}
G=(big *)mr_alloc(n+1,sizeof(big));
char *memg=(char *)memalloc(n+1);
for (i=0;i<=n;i++) G[i]=mirvar_mem(memg,i);
R=(big *)mr_alloc(degm,sizeof(big));
char *memr=(char *)memalloc(degm);
for (i=0;i<degm;i++) R[i]=mirvar_mem(memr,i);
ptr=p.start;
while (ptr!=NULL)
{
copy(getbig(ptr->an),G[ptr->n]);
ptr=ptr->next;
}
if (!mr_poly_rem(n,G,R))
{ // reset the Modulus - things have changed
setmod(Modulus);
mr_poly_rem(n,G,R);
}
rem.clear();
for (d=degm-1;d>=0;d--)
{
t=R[d];
if (t.iszero()) continue;
pos=rem.addterm(t,d,pos);
}
memkill(memr,degm);
mr_free(R);
memkill(memg,n+1);
mr_free(G);
}
void setmod(const Poly& p)
{
Modulus=p;
setpolymod(p);
}
PolyMod operator-(const PolyMod& a,const PolyMod& b)
{return (a.p-b.p)%Modulus;}
PolyMod operator+(const PolyMod& a,const PolyMod& b)
{return (a.p+b.p)%Modulus;}
PolyMod operator*(const PolyMod& a,const ZZn& z)
{return (z*a.p);}
PolyMod operator*(const ZZn& z,const PolyMod& m)
{return (z*m.p);}
PolyMod operator+(const PolyMod& a,const ZZn& z)
{
PolyMod p=a;
p.addterm(z,0);
return p;
}
PolyMod operator-(const PolyMod& a,const ZZn& z)
{
PolyMod p=a;
p.addterm((-z),0);
return p;
}
PolyMod operator/(const PolyMod& a,const ZZn& z)
{return (a.p/z);}
Poly gcd(const PolyMod& m)
{return gcd(m.p,Modulus);}
//
// Brent & Kung's First Algorithm
// See "Fast Algorithms for Manipulating Formal Power Series
// J.ACM, Vol. 25 No. 4 October 1978 pp 581-595
//
PolyMod compose(const PolyMod& q,const PolyMod& p)
{ // compose polynomials
// assume P(x) = P3x^3 + P2x^2 + P1x^1 +P0
// Calculate P(Q(x)) = P3.(Q(x))^3 + P2.(Q(x))^2 ....
PolyMod C,Q,T;
big t;
term *xptr,*yptr;
int i,j,ik,L,n=degree(Modulus);
int k=isqrt(n+1,1);
if (k*k<n+1) k++;
// step 1
PolyMod *P=new PolyMod[k+1];
P[0]=1;
for (i=1;i<=k;i++) P[i]=(P[i-1]*p);
big *x,*y;
x=(big *)mr_alloc(k,sizeof(big));
y=(big *)mr_alloc(k,sizeof(big));
t=mirvar(0);
T=1;
for (i=0;i<k;i++)
{
ik=i*k;
Q.clear();
for (L=0;L<=n;L++)
{
zero(t);
xptr=q.p.start;
while (xptr!=NULL)
{
if (xptr->n<=ik+k-1) break;
xptr=xptr->next;
}
for (j=k-1;j>=0;j--)
{
x[j]=t;
if (xptr!=NULL)
{
if (ik+j==xptr->n)
{
x[j]=getbig(xptr->an);
xptr=xptr->next;
}
}
// x[j]=q.coeff(i*k+j)
y[j]=t;
yptr=P[j].p.start;
while (yptr!=NULL)
{
if (yptr->n<=L)
{
if (yptr->n==L) y[j]=getbig(yptr->an);
break;
}
yptr=yptr->next;
}
} // y[j]=P[j].coeff(L)
// Asymptotically slow, but very fast in practise ...
nres_dotprod(k,x,y,t);
Q.addterm((ZZn)t,L);
}
C+=(Q*T);
if (i<k-1) T*=P[k];
}
mr_free(t);
mr_free(y);
mr_free(x);
delete [] P;
return C;
}
PolyMod pow(const PolyMod& f,const Big& k)
{
PolyMod u,u2,table[16];
Big w,e;
int i,j,nb,n,nbw,nzs;
if (k==0)
{
u.addterm((ZZn)1,0);
return u;
}
reduce(f.p,u);
if (k==1) return u;
if (degree(f)>=FFT_BREAK_EVEN )
{
u2=(u*u);
table[0]=u;
for (i=1;i<16;i++)
table[i]=u2*table[i-1];
nb=bits(k);
if (nb>1) for (i=nb-2;i>=0;)
{
n=window(k,i,&nbw,&nzs,5);
for (j=0;j<nbw;j++)
u*=u;
if (n>0) u*=table[n/2];
i-=nbw;
if (nzs)
{
for (j=0;j<nzs;j++) u*=u;
i-=nzs;
}
}
}
else
{
e=k;
w=pow((Big)2,bits(e)-1);
e-=w; w/=2;
while (w>0)
{
u*=u;
if (e>=w)
{
e-=w;
u*=f;
}
w/=2;
}
}
return u;
}
ostream& operator<<(ostream& s,const PolyMod& m)
{ s << m.p; return s;}