1 
2 #ifndef _HELIB_SPX_H_
3 #define _HELIB_SPX_H_
4 /* SPX.h - A MUX class that implements either GF2X or zz_pX: it keeps both
5  *   objects, and decides in runitime to use one or the other as appropriate.
6  *
7  * Modifications:
8  *
9  * Created by Shai Halevi on May 27, 2015
10  */
11 #include <iostream>
12 #include <NTL/version.h>
13 #include <NTL/ZZX.h>
14 #include <NTL/GF2X.h>
15 #include <NTL/lzz_pX.h>
16 
17 // A helper class that maintains the NTL context (this is unnecessary
18 // as of NTL version 9.2.0)
19 #if (NTL_MAJOR_VERSION<9 || (NTL_MAJOR_VERSION==9 && NTL_MINOR_VERSION<2))
20 class SPmodulus { // single precision modulus
21 public:
22   long p;                   // the modulus itself
23   NTL::zz_pContext context; // NTL context, used if !gf2
24 
SPmodulus()25   SPmodulus(): p(0) {}
SPmodulus(long _p)26   SPmodulus(long _p): p(_p), context(_p) {}
SPmodulus(long _p,const NTL::zz_pContext & _c)27   SPmodulus(long _p, const NTL::zz_pContext& _c): p(_p), context(_c) {}
28 
null()29   bool null() const { return (p==0); }
modulus()30   long modulus() const  { return p; }
equals(const SPmodulus & other)31   bool equals(const SPmodulus& other) const
32   { return p==other.p; }
33 };
getContext(const SPmodulus & spm)34 inline const NTL::zz_pContext& getContext(const SPmodulus& spm) { return spm.context; }
35 #else
36 typedef NTL::zz_pContext SPmodulus;
getContext(const SPmodulus & spm)37 inline const NTL::zz_pContext& getContext(const SPmodulus& spm) { return spm; }
38 #endif
isGF2(const SPmodulus & spm)39 inline bool isGF2(const SPmodulus& spm) { return spm.modulus()==2; }
40 
41 
42 
43 // macro for non-const unary operators (e.g., ++SPX)
44 #define sppolyUnary(fnc) SPX& fnc() {			\
45     if (isGF2(modulus)) {NTL::fnc(gf2x);}			\
46     else {NTL::zz_pPush push(getContext(modulus)); NTL::fnc(zzpx);}	\
47     return *this;}
48 
49 // macro for non-const binary operators that take a long (SPX += long)
50 #define sppolyBinaryLeft(fnc) SPX fnc(long y) {		\
51   if (isGF2(modulus)) { NTL::fnc(gf2x,y); }			\
52   else { NTL::zz_pPush push(getContext(modulus)); NTL::fnc(zzpx,y); }\
53   return *this; }
54 
55 // macro for non-const binay operators that take another SPX
56 // (SPX += SPX)
57 #define sppolyBinaryBoth(fnc) SPX fnc(const SPX& y) {	\
58   ensureConsistency(*this,y);					\
59   if (isGF2(modulus)) { NTL::fnc(gf2x,y.gf2x); }               \
60   else { NTL::zz_pPush push(getContext(modulus)); NTL::fnc(zzpx,y.zzpx); }	\
61   return *this;}
62 
63 // The main class SPX, declerations follow GF2X, zz_pX
64 class SPX {
65 public:
66   SPmodulus modulus; // not a reference, the object itself
67   NTL::GF2X gf2x;
68   NTL::zz_pX zzpx;
69 
70   // access methods
getMod()71   const SPmodulus& getMod() const { return modulus; }
72 
73   // helper functions
makeFromVec(const NTL::Vec<T> & v)74   template <class T> void makeFromVec(const NTL::Vec<T>& v)
75   {
76     if (isGF2(modulus)) {
77       NTL::Vec<NTL::GF2> v2;
78       NTL::conv(v2, v);
79       NTL::conv(gf2x, v2);
80     } else {
81       NTL::zz_pPush push(getContext(modulus));
82       NTL::Vec<NTL::zz_p> vp;
83       NTL::conv(vp, v);
84       NTL::conv(zzpx, vp);
85     }
86   }
87 
deg()88   long deg() const
89   { return (isGF2(modulus)? NTL::deg(gf2x): NTL::deg(zzpx)); }
90 
91   template <class T> void getCoeffVec(NTL::Vec<T>& v, long n=-1) const
92   {
93     if (n<0) n=deg()+1;
94     if (isGF2(modulus)) {
95       NTL::Vec<NTL::GF2> v2 = NTL::VectorCopy(gf2x, n);
96       NTL::conv(v, v2);
97     } else {
98       NTL::zz_pPush push(getContext(modulus));
99       NTL::Vec<NTL::zz_p> vp = NTL::VectorCopy(zzpx, n);
100       NTL::conv(v, vp);
101     }
102   }
103 
104   // internal constructors, useful for debugging
SPX(const NTL::GF2X & p2)105   explicit SPX(const NTL::GF2X& p2): modulus(2), gf2x(p2) {}
SPX(const NTL::zz_pX & pp,const SPmodulus & mod)106   SPX(const NTL::zz_pX& pp, const SPmodulus& mod): modulus(mod) {
107     if (isGF2(mod)) { // need to convert to GF2X
108       NTL::Vec<long> v;
109       NTL::conv(v, pp.rep);
110       makeFromVec(v);
111     }
112     else zzpx = pp;
113   }
114 
resetMod(const SPmodulus & mod)115   void resetMod(const SPmodulus& mod)
116   {
117     if (modulus.null()) { // uninititalized object
118       modulus = mod;
119       return;
120     }
121     bool noteq = false;
122     if (isGF2(mod)!=isGF2(modulus))          noteq = true;
123     if (!isGF2(mod) && !mod.equals(modulus)) noteq = true;
124     if (noteq) {
125       gf2x.kill();
126       zzpx.kill();
127       modulus = mod;
128     }
129   }
130 
131   void setCoeff(long i, long c=1)
132   {
133     if (isGF2(modulus)) NTL::SetCoeff(gf2x, i, c);
134     else                NTL::SetCoeff(zzpx, i, c);
135   }
136 
137   // Checking consistency between arguments, T is either SPX or SPXModulus
138   template<class T>
ensureConsistency(const SPX & x,const T & y)139   static void ensureConsistency(const SPX& x, const T& y)
140   { if (isGF2(x.getMod()) && isGF2(y.getMod())) return;
141     if (isGF2(x.getMod()) || isGF2(y.getMod()) || !x.getMod().equals(y.getMod())) {
142       throw std::invalid_argument("mismatched moduli in SPX arguments");
143     }
144   }
145   template<class S, class T>
ensureConsistency(const SPX & x,const S & y,const T & z)146   static void ensureConsistency(const SPX& x, const S& y, const T& z)
147   { ensureConsistency(x,y); ensureConsistency(x,z); }
148 
149   // Constructors
150 
SPX()151   SPX(){}                             // empty object
SPX(const SPmodulus & mod)152   explicit SPX(const SPmodulus& mod): modulus(mod) {} // equal to zero
SPX(const NTL::Vec<long> & coefs,const SPmodulus & mod)153   SPX(const NTL::Vec<long>& coefs, const SPmodulus& mod): // initialized
154     modulus(mod) { makeFromVec<long>(coefs); }
SPX(const NTL::ZZX & poly,const SPmodulus & mod)155   SPX(const NTL::ZZX& poly, const SPmodulus& mod):// initialized from ZZX
156     modulus(mod) { makeFromVec<NTL::ZZ>(poly.rep); }
157 
158   // Convencience constructors: initialize to X^i or c*X^i
SPX(NTL::INIT_MONO_TYPE,long i,const SPmodulus & mod)159   SPX(NTL::INIT_MONO_TYPE, long i, const SPmodulus& mod):
160     modulus(mod) { setCoeff(i); }
SPX(NTL::INIT_MONO_TYPE,long i,long c,const SPmodulus & mod)161   SPX(NTL::INIT_MONO_TYPE, long i, long c, const SPmodulus& mod):
162     modulus(mod) { setCoeff(i,c); }
163 
SPX(NTL::INIT_SIZE_TYPE,long n,const SPmodulus & mod)164   SPX(NTL::INIT_SIZE_TYPE, long n, const SPmodulus& mod):
165     modulus(mod) { setCoeff(n-1,0); }
166   // SPX(INIT_SIZE, n, mod) initializes to zero, but space is
167   // pre-allocated for n coefficients
168 
169   // Implicit copy constructor, destructor, and assignment operator
170 
171   // f.SetMaxLength(n) pre-allocate spaces for n coefficients.  The
172   // polynomial that f represents is unchanged.
SetMaxLength(long n)173   void SetMaxLength(long n)
174   {
175     if (modulus.null()) return; // uninitialized, cannot allocate space
176     if (isGF2(modulus)) gf2x.SetMaxLength(n);
177     else                zzpx.SetMaxLength(n);
178   }
179 
180   // f.kill() sets f to 0 and frees all memory held by f.
kill()181   void kill()
182   {
183     if (modulus.null()) return; // uninitialized, nothing to do
184     if (isGF2(modulus)) gf2x.kill();
185     else                zzpx.kill();
186   }
187 
188   sppolyBinaryLeft(operator+=) // SPX += long
189   sppolyBinaryBoth(operator+=) // SPX += SPPoly
190 
191   template <class T> SPX operator+(T b) const
192   { SPX tmp=*this; tmp += b; return tmp; }
193 
194   sppolyUnary(operator++)                // ++SPX
195   void operator++(int) { operator++(); } // SPX++
196 
197   sppolyBinaryLeft(operator-=) // SPX -= long
198   sppolyBinaryBoth(operator-=) // SPX -= SPPoly
199 
200   template <class T> SPX operator-(T b) const
201   { SPX tmp=*this; tmp -= b; return tmp; }
202 
203   sppolyUnary(operator--)                // --SPX
204   void operator--(int) { operator--(); } // SPX--
205 
negate()206   void negate() {
207     if (isGF2(modulus)) return; // nothing to do, over GF2 we have X == -X
208     else {
209       NTL::zz_pPush push(getContext(modulus));
210       NTL::negate(zzpx, zzpx);
211     }
212   }
213   SPX operator-() const
214   { SPX tmp=*this; tmp.negate(); return tmp; }
215 
216 
217   sppolyBinaryLeft(operator*=) // SPX *= long
218   sppolyBinaryBoth(operator*=) // SPX *= SPPoly
219 
220   template <class T> SPX operator*(T b) const
221   { SPX tmp=*this; tmp *= b; return tmp; }
222 
223   sppolyBinaryLeft(operator<<=) // SPX <<= long
224   SPX operator<<(long n) const
225   { SPX tmp=*this; tmp <<= n; return tmp; }
226 
227   sppolyBinaryLeft(operator>>=) // SPX >>= long
228   SPX operator>>(long n) const
229   { SPX tmp=*this; tmp >>= n; return tmp; }
230 
231   sppolyBinaryBoth(operator/=) // SPX /= SPX
232   sppolyBinaryLeft(operator/=) // SPX /= long
233 
234   template <class T> SPX operator/(T b) const
235   { SPX tmp=*this; tmp /= b; return tmp; }
236 
237   sppolyBinaryBoth(operator%=) // SPX %= SPX
238 
239   SPX operator%(const SPX& b) const
240   { SPX tmp=*this; tmp %= b; return tmp; }
241 
242 // SIZE INVARIANT: for any f in SPX, deg(f)+1 < 2^(NTL_BITS_PER_LONG-4).
243 };
244 
245 typedef NTL::Vec<SPX> vec_SPX; // a conveneince vector decleration
246 
247 // macro for function with signature void fnc(const SPX& x)
248 #define sppolyOnePoly(fnc) fnc(SPX& x) {	 \
249     if (isGF2(x.getMod())) NTL::fnc(x.gf2x);	 \
250     else { NTL::zz_pPush push(getContext(x.getMod())); \
251            NTL::fnc(x.zzpx);}}
252 
253 // macro for function with signature non-void fnc(const type& x)
254 // type is either SPX or SPXModulus
255 #define sppolyConstOnePoly(fnc, typ) fnc(const typ& x) { \
256     if (isGF2(x.getMod())) return NTL::fnc(x.gf2x); \
257     else { NTL::zz_pPush push(getContext(x.getMod()));      \
258            return NTL::fnc(x.zzpx);}}
259 
260 // macro for function with signature void fnc(SPX& x, const type& arg)
261 // type is either SPX or SPXModulus
262 #define sppolyTwoPoly(fnc,typ) fnc(SPX& x, typ& y) {\
263     x.resetMod(y.getMod());				     \
264     if (isGF2(x.getMod())) NTL::fnc(x.gf2x, y.gf2x);    \
265     else { NTL::zz_pPush push(getContext(x.getMod()));	     \
266            NTL::fnc(x.zzpx, y.zzpx);}}
267 
268 // macro for function with signature
269 // void fnc(SPX& x, const SPX& a, const type& b)
270 // type is either SPX or SPXModulus
271 #define sppolyThreePoly(fnc,typ)\
272   fnc(SPX& x, const SPX& a, const typ& b) {\
273     SPX::ensureConsistency(a,b);\
274     x.resetMod(a.getMod());	   \
275     if (isGF2(x.getMod())) NTL::fnc(x.gf2x, a.gf2x, b.gf2x);\
276     else { NTL::zz_pPush push(getContext(x.getMod()));	   \
277            NTL::fnc(x.zzpx, a.zzpx, b.zzpx);}}
278 
279 // macro for function with signature
280 // void fnc(SPX& x, const SPX& a, const type& b)
281 // type is either SPX or SPXModulus
282 #define sppolyFourPoly(fnc,typ)\
283   fnc(SPX& x, const SPX& a, const SPX& b, const typ& f) {  \
284     SPX::ensureConsistency(a,b,f);				    \
285     x.resetMod(a.getMod());					    \
286     if (isGF2(x.getMod()))					    \
287       NTL::fnc(x.gf2x, a.gf2x, b.gf2x, f.gf2x);	    \
288     else { NTL::zz_pPush push(getContext(x.getMod()));		    \
289            NTL::fnc(x.zzpx, a.zzpx, b.zzpx, f.zzpx);}}
290 
291 // macro for function with signature
292 // void fnc(SPX& x, const SPX& a, type b)
293 #define sppolyTwoPolyArg(fnc,typ) \
294   fnc(SPX& x, const SPX& a, typ b) {		    \
295     x.resetMod(a.getMod());					\
296     if (isGF2(x.getMod())) NTL::fnc(x.gf2x, a.gf2x, b);\
297     else { NTL::zz_pPush push(getContext(x.getMod()));	    \
298            NTL::fnc(x.zzpx, a.zzpx, b);}}
299 
300 // macro for function with signature
301 // void fnc(SPX& x, const SPX& g, const typ& F, long m)
302 #define sppolyThreePolyArg(fnc,typ)					\
303   fnc(SPX& x, const SPX& g, const typ& F, long m) {\
304     SPX::ensureConsistency(g,F);			      \
305     x.resetMod(g.getMod());				      \
306     if (isGF2(g.getMod())) NTL::fnc(x.gf2x, g.gf2x, F.gf2x, m);\
307     else { NTL::zz_pPush push(getContext(g.getMod()));	      \
308            NTL::fnc(x.zzpx, g.zzpx, F.zzpx, m);}}
309 
310 /**************************************************************************\
311                               Accessing coefficients
312 
313 The degree of a polynomial f is obtained as deg(f),
314 where the zero polynomial, by definition, has degree -1.
315 
316 A polynomial f is represented as a coefficient vector.
317 Coefficients may be accessed via coeff(f, i) to get the
318 coefficient of X^i in the polynomial f, and SetCoeff(f, i, a)
319 to set the coefficient of X^i in f to the scalar a.
320 
321 \**************************************************************************/
322 
deg(const SPX & a)323 inline long deg(const SPX& a) { return a.deg(); }
324 // return deg(a); deg(0) == -1.
325 
coeff(const SPX & x,long i)326 inline long coeff(const SPX& x, long i)
327 {
328   if (isGF2(x.getMod())) return NTL::conv<long>(NTL::coeff(x.gf2x, i));
329   else {
330     NTL::zz_pPush push(getContext(x.getMod()));
331     return NTL::conv<long>(NTL::coeff(x.zzpx,i));
332   }
333 }
334 
335 // const long coeff(const SPX& a, long i);
336 // returns the coefficient of X^i, or zero if i not in range
337 
LeadCoeff(const SPX & a)338 inline long LeadCoeff(const SPX& a)
339 { return coeff(a, deg(a)); }
340 // returns leading term of a, or zero if a == 0
341 
ConstTerm(const SPX & a)342 const long ConstTerm(const SPX& a)
343 { return coeff(a, 0); }
344 // returns constant term of a, or zero if a == 0
345 
SetCoeff(SPX & x,long i,long a)346 void SetCoeff(SPX& x, long i, long a)
347 { x.setCoeff(i,a); }
348 // makes coefficient of X^i equal to a; error is raised if i < 0
349 
SetCoeff(SPX & x,long i)350 void SetCoeff(SPX& x, long i)
351 { x.setCoeff(i); }
352 
353 // makes coefficient of X^i equal to 1;  error is raised if i < 0
354 
355 inline void sppolyOnePoly(SetX)
356 // void SetX(SPX& x); // x is set to the monomial X
357 
358 inline bool sppolyConstOnePoly(IsX,SPX)
359 // long IsX(const SPX& a); // test if x = X
360 
361 
362 /**************************************************************************\
363 
364                                   Comparison
365 
366 \**************************************************************************/
367 
368 bool operator==(const SPX& a, const SPX& b) {
369   if (isGF2(a.getMod())!=isGF2(b.getMod())) return false;
370   if (isGF2(a.getMod())) return (a.gf2x==b.gf2x);
371   else return (a.getMod().equals(b.getMod()) && (a.zzpx==b.zzpx));
372 }
373 bool operator!=(const SPX& a, const SPX& b)
374 { return !(a==b); }
375 
376 inline bool sppolyConstOnePoly(IsZero,SPX)
377 // bool IsZero(const SPX& a); // test for 0
378 
379 inline bool sppolyConstOnePoly(IsOne,SPX)
380 // bool IsOne(const SPX& a); // test for 1
381 
382 // PROMOTIONS: operators ==, != promote {long, long} to SPX on (a, b)
383 
384 
385 /**************************************************************************\
386 
387                                    Addition
388 
389 \**************************************************************************/
390 
391 // operator notation:
392 
393 inline SPX operator+(long a, const SPX& b)
394 { return (b+a);}
395 //SPX operator-(long, const SPX& b); // not implemented
396 
397 // procedural versions:
398 
add(SPX & x,const SPX & a,const SPX & b)399 inline void add(SPX& x, const SPX& a, const SPX& b)// x = a + b
400 { x = a; x+= b; }
add(SPX & x,long a,const SPX & b)401 inline void add(SPX& x, long a, const SPX& b) // x = a + b
402 { x = b; x+= a; }
add(SPX & x,const SPX & a,long b)403 inline void add(SPX& x, const SPX& a, long b) // x = a + b
404 { x = a; x+= b; }
sub(SPX & x,const SPX & a,const SPX & b)405 inline void sub(SPX& x, const SPX& a, const SPX& b) // x = a - b
406 { x=a; x-=b; }
407 // void sub(SPX& x, long a, const SPX& b); // x = a - b, not implemented
sub(SPX & x,const SPX & a,long b)408 inline void sub(SPX& x, const SPX& a, long b) // x = a - b
409 { x=a; x-=b; }
negate(SPX & x,const SPX & a)410 inline void negate(SPX& x, const SPX& a) // x = -a
411 { x = a; x.negate(); }
412 //inline void negate(SPX& x, long a) // x = -a, not implemented
413 
414 
415 /**************************************************************************\
416 
417                                Multiplication
418 
419 \**************************************************************************/
420 
421 // operator notation:
422 
423 inline SPX operator*(long a, const SPX& b)
424 { return (b*a); }
425 
426 // procedural versions:
427 
mul(SPX & x,const SPX & a,const SPX & b)428 inline void mul(SPX& x, const SPX& a, const SPX& b) // x = a * b
429 { x=a; x*=b; }
mul(SPX & x,long a,const SPX & b)430 inline void mul(SPX& x, long a, const SPX& b) // x = a * b
431 { x=b; x*=a; }
mul(SPX & x,const SPX & a,long b)432 inline void mul(SPX& x, const SPX& a, long b) // x = a * b
433 { x=a; x*=b; }
434 
sppolyTwoPoly(sqr,const SPX)435 inline void sppolyTwoPoly(sqr,const SPX)
436 //void sqr(SPX& x, const SPX& a); // x = a^2
437 
438 inline SPX sqr(const SPX& a)
439 { SPX x; sqr(x,a); return x; }
440 
441 /**************************************************************************\
442 
443                                Shift Operations
444 
445 LeftShift by n means multiplication by X^n
446 RightShift by n means division by X^n
447 
448 A negative shift amount reverses the direction of the shift.
449 
450 \**************************************************************************/
451 
452 // procedural versions:
453 
LeftShift(SPX & x,const SPX & a,long n)454 inline void LeftShift(SPX& x, const SPX& a, long n)
455 { x=a; x <<= n; }
LeftShift(const SPX & a,long n)456 inline SPX LeftShift(const SPX& a, long n)
457 { return a << n; }
458 
RightShift(SPX & x,const SPX & a,long n)459 inline void RightShift(SPX& x, const SPX& a, long n)
460 { x=a; x >>= n; }
RightShift(const SPX & a,long n)461 inline SPX RightShift(const SPX& a, long n)
462 { return a >> n; }
463 
464 /**************************************************************************\
465 
466                                   Division
467 
468 \**************************************************************************/
469 
470 // procedural versions:
471 
472 // q = a/b, r = a%b (class T is either SPX or SPXModulus)
473 template<class T>
DivRem(SPX & q,SPX & r,const SPX & a,const T & b)474 inline void DivRem(SPX& q, SPX& r, const SPX& a, const T& b)
475 {
476   SPX::ensureConsistency(a,b);
477   q.resetMod(a.getMod());
478   r.resetMod(a.getMod());
479   if (isGF2(a.getMod()))
480     NTL::DivRem(q.gf2x, r.gf2x, a.gf2x, b.gf2x);
481   else {
482     NTL::zz_pPush push(getContext(a.getMod()));
483     NTL::DivRem(q.zzpx, r.zzpx, a.zzpx, b.zzpx);
484   }
485 }
486 
487 // q = a/b
sppolyThreePoly(div,SPX)488 inline void sppolyThreePoly(div,SPX)
489 // void div(SPX& q, const SPX& a, const SPX& b);
490 
491 // r = a%b
492 inline void sppolyThreePoly(rem,SPX)
493 // void rem(SPX& r, const SPX& a, const SPX& b);
494 
495 // if b | a, sets q = a/b and returns 1; otherwise returns 0
496 inline bool divide(SPX& q, const SPX& a, const SPX& b)
497 {
498   if (isGF2(a.getMod())!=isGF2(b.getMod())) return false;
499   if (!isGF2(a.getMod()) && !a.getMod().equals(b.getMod())) return false;
500   q.resetMod(a.getMod());
501   if (isGF2(a.getMod())) return NTL::divide(q.gf2x, a.gf2x, b.gf2x);
502   else {
503     NTL::zz_pPush push(getContext(a.getMod()));
504     return NTL::divide(q.zzpx, a.zzpx, b.zzpx);
505   }
506 }
507 
divide(const SPX & a,const SPX & b)508 inline bool divide(const SPX& a, const SPX& b)
509 {
510   if (isGF2(a.getMod())!=isGF2(b.getMod())) return false;
511   if (!isGF2(a.getMod()) && !a.getMod().equals(b.getMod())) return false;
512   if (isGF2(a.getMod())) return NTL::divide(a.gf2x, b.gf2x);
513   else {
514     NTL::zz_pPush push(getContext(a.getMod()));
515     return NTL::divide(a.zzpx, b.zzpx);
516   }
517 }
518 // if b | a returns 1; otherwise returns 0
519 
520 /**************************************************************************\
521 
522                                    GCD's
523 
524 \**************************************************************************/
525 
526 
sppolyThreePoly(GCD,SPX)527 inline void sppolyThreePoly(GCD,SPX)
528 //void GCD(SPX& x, const SPX& a, const SPX& b);
529 
530 SPX GCD(const SPX& a, const SPX& b)
531 { SPX x; GCD(x, a, b); return x; }
532 // x = GCD(a, b) (zero if a==b==0).
533 
534 // d = gcd(a,b), a s + b t = d
XGCD(SPX & d,SPX & s,SPX & t,const SPX & a,const SPX & b)535 inline void XGCD(SPX& d, SPX& s, SPX& t,
536 		 const SPX& a, const SPX& b)
537 {
538   SPX::ensureConsistency(a,b);
539   d.resetMod(a.getMod());
540   s.resetMod(a.getMod());
541   t.resetMod(a.getMod());
542   if (isGF2(a.getMod()))
543     NTL::XGCD(d.gf2x, s.gf2x, t.gf2x, a.gf2x, b.gf2x);
544   else {
545     NTL::zz_pPush push(getContext(a.getMod()));
546     NTL::XGCD(d.zzpx, s.zzpx, t.zzpx, a.zzpx, b.zzpx);
547   }
548 }
549 
550 /**************************************************************************\
551 
552                                   Input/Output
553 
554 I/O format:
555 
556    [a_0 a_1 ... a_n],
557 
558 represents the polynomial a_0 + a_1*X + ... + a_n*X^n.
559 
560 On output, all coefficients will be in [0,p-1], and a_n not zero
561 (the zero polynomial is [ ]).  On input, the coefficients may be
562 arbitrary integers which are reduced modulo p, and leading zeros
563 stripped.
564 
565 x must have the right modulus set before calling these functions
566 \**************************************************************************/
567 
568 inline std::istream& operator>>(std::istream& s, SPX& x)
569 {
570   if (isGF2(x.getMod())) return s >> x.gf2x;
571   else {
572     NTL::zz_pPush push(getContext(x.getMod()));
573     return s >> x.gf2x;
574   }
575 }
576 
577 inline std::ostream& operator<<(std::ostream& s, const SPX& x)
578 {
579   if (isGF2(x.getMod())) return s << x.gf2x;
580   else {
581     NTL::zz_pPush push(getContext(x.getMod()));
582     return s << x.zzpx;
583   }
584 }
585 
586 /**************************************************************************\
587 
588                               Some utility routines
589 
590 \**************************************************************************/
591 
592 // x = derivative of a
sppolyTwoPoly(diff,const SPX)593 inline void sppolyTwoPoly(diff,const SPX)
594 // void diff(SPX& x, const SPX& a);
595 
596 inline SPX diff(const SPX& a)
597 { SPX x; diff(x,a); return x; }
598 
599 inline void reverse(SPX& x, const SPX& a, long hi=-2)
600 {
601   if (hi < -1) hi = deg(a);
602   x.resetMod(a.getMod());
603   if (isGF2(x.getMod())) NTL::reverse(x.gf2x, a.gf2x, hi);
604   else {
605     NTL::zz_pPush push(getContext(x.getMod()));
606     NTL::reverse(x.zzpx, a.zzpx, hi);
607   }
608 }
609 
610 inline SPX reverse(const SPX& a, long hi=-2)
611 { SPX x; reverse(x,a,hi); return x; }
612 
613 // x = reverse of a[0]..a[hi] (hi >= -1);
614 // hi defaults to deg(a) in second version
615 
616 
617 /**************************************************************************\
618 
619                              Random Polynomials
620 
621 \**************************************************************************/
622 
623 // x = random polynomial of degree < n, with specified modulus
random(SPX & x,long n,const SPmodulus & mod)624 inline void random(SPX& x, long n, const SPmodulus& mod)
625 {
626   x.resetMod(mod);
627   if (isGF2(x.getMod())) NTL::random(x.gf2x, n);
628   else {
629     NTL::zz_pPush push(getContext(x.getMod()));
630     NTL::random(x.zzpx, n);
631   }
632 }
633 
random_SPX(long n,const SPmodulus & mod)634 inline SPX random_SPX(long n, const SPmodulus& mod)
635 { SPX x; random(x, n, mod); return x; }
636 
637 /**************************************************************************\
638 
639                        Arithmetic mod X^n
640 
641 Required: n >= 0; otherwise, an error is raised.
642 
643 \**************************************************************************/
644 
sppolyTwoPolyArg(trunc,long)645 inline void sppolyTwoPolyArg(trunc,long)
646 //void trunc(SPX& x, const SPX& a, long n); // x = a % X^n
647 
648 inline SPX trunc(const SPX& a, long n)
649 { SPX x; trunc(x,a,n); return x; }
650 
651 // x = a * b % X^n
sppolyThreePolyArg(MulTrunc,SPX)652 inline void sppolyThreePolyArg(MulTrunc,SPX)
653 //void MulTrunc(SPX& x, const SPX& a, const SPX& b, long n);
654 
655 SPX MulTrunc(const SPX& a, const SPX& b, long n)
656 { SPX x; MulTrunc(x,a,b,n); return x; }
657 
658 // x = a^2 % X^n
sppolyTwoPolyArg(SqrTrunc,long)659 inline void sppolyTwoPolyArg(SqrTrunc,long)
660 //void SqrTrunc(SPX& x, const SPX& a, long n);
661 
662 inline SPX SqrTrunc(const SPX& a, long n)
663 { SPX x; SqrTrunc(x,a,n); return x; }
664 
665 // computes x = a^{-1} % X^n.  Must have ConstTerm(a) invertible.
666 
sppolyTwoPolyArg(InvTrunc,long)667 inline void sppolyTwoPolyArg(InvTrunc, long)
668 // void InvTrunc(SPX& x, const SPX& a, long n);
669 
670 inline SPX InvTrunc(const SPX& a, long n)
671 { SPX x; InvTrunc(x,a,n); return x; }
672 
673 /**************************************************************************\
674 
675                 Modular Arithmetic (without pre-conditioning)
676 
677 Arithmetic mod f.
678 
679 All inputs and outputs are polynomials of degree less than deg(f), and
680 deg(f) > 0.
681 
682 NOTE: if you want to do many computations with a fixed f, use the
683 SPXModulus data structure and associated routines below for better
684 performance.
685 
686 \**************************************************************************/
687 
688 // x = (a * b) % f
sppolyFourPoly(MulMod,SPX)689 inline void sppolyFourPoly(MulMod,SPX)
690 //void MulMod(SPX& x, const SPX& a, const SPX& b, const SPX& f);
691 
692 inline SPX MulMod(const SPX& a, const SPX& b, const SPX& f)
693 { SPX x; MulMod(x,a,b,f); return x; }
694 
695 
696 // x = a^2 % f
sppolyThreePoly(SqrMod,SPX)697 inline void sppolyThreePoly(SqrMod,SPX)
698 //void SqrMod(SPX& x, const SPX& a, const SPX& f);
699 
700 inline SPX SqrMod(const SPX& a, const SPX& f)
701 { SPX x; SqrMod(x,a,f); return x; }
702 
703 // x = (a * X) mod f
sppolyThreePoly(MulByXMod,SPX)704 inline void sppolyThreePoly(MulByXMod,SPX)
705 //void MulByXMod(SPX& x, const SPX& a, const SPX& f);
706 
707 inline SPX MulByXMod(const SPX& a, const SPX& f)
708 { SPX x; MulByXMod(x,a,f); return x; }
709 
710 // x = a^{-1} % f, error is a is not invertible
sppolyThreePoly(InvMod,SPX)711 inline void sppolyThreePoly(InvMod,SPX)
712 //void InvMod(SPX& x, const SPX& a, const SPX& f);
713 
714 inline SPX InvMod(const SPX& a, const SPX& f)
715 { SPX x; InvMod(x,a,f); return x; }
716 
InvModStatus(SPX & x,const SPX & a,const SPX & f)717 inline bool InvModStatus(SPX& x, const SPX& a, const SPX& f)
718 {
719   SPX::ensureConsistency(a,f);
720   x.resetMod(a.getMod());
721   if (isGF2(a.getMod()))
722     return NTL::InvModStatus(x.gf2x, a.gf2x, f.gf2x);
723   else {
724     NTL::zz_pPush push(getContext(x.getMod()));
725     return NTL::InvModStatus(x.zzpx, a.zzpx, f.zzpx);
726   }
727 }
728 // if (a, f) = 1, returns 0 and sets x = a^{-1} % f; otherwise,
729 // returns 1 and sets x = (a, f)
730 
731 
732 // for modular exponentiation, see below
733 
734 
735 
736 /**************************************************************************\
737 
738                      Modular Arithmetic with Pre-Conditioning
739 
740 If you need to do a lot of arithmetic modulo a fixed f, build
741 SPXModulus F for f.  This pre-computes information about f that
742 speeds up subsequent computations.
743 
744 As an example, the following routine computes the product modulo f of a vector
745 of polynomials.
746 
747 #include <NTL/SPX.h>
748 
749 void product(SPX& x, const vec_SPX& v, const SPX& f)
750 {
751    SPXModulus F(f);
752    SPX res;
753    res = 1;
754    long i;
755    for (i = 0; i < v.length(); i++)
756       MulMod(res, res, v[i], F);
757    x = res;
758 }
759 
760 
761 Note that automatic conversions are provided so that a SPX can
762 be used wherever a SPXModulus is required, and a SPXModulus
763 can be used wherever a SPX is required.
764 
765 The SPXModulus routines optimize several important special cases:
766 
767   - f = X^n + X^k + 1, where k <= min((n+1)/2, n-NTL_BITS_PER_LONG)
768 
769   - f = X^n + X^{k_3} + X^{k_2} + X^{k_1} + 1,
770       where k_3 <= min((n+1)/2, n-NTL_BITS_PER_LONG)
771 
772   - f = X^n + g, where deg(g) is small
773 
774 
775 \**************************************************************************/
776 
777 class SPXModulus {
778 public:
779   SPmodulus modulus; // not a reference, the object itself
780   NTL::GF2XModulus gf2x;
781   NTL::zz_pXModulus zzpx;
SPXModulus()782   SPXModulus() {} // initially in an unusable state
783 
784   // access methods
getMod()785   const SPmodulus& getMod() const { return modulus; }
786 
SPXModulus(const SPX & f)787   SPXModulus(const SPX& f): // initialize with f, deg(f)>0
788     modulus(f.getMod())
789   {
790     if (isGF2(f.getMod())) gf2x=f.gf2x;
791     else                  zzpx=f.zzpx;
792   }
793   SPXModulus& operator=(const SPX& f) {
794     modulus = f.getMod();
795     if (isGF2(f.getMod())) gf2x=f.gf2x;
796     else                  zzpx=f.zzpx;
797     return *this;
798   }
799 };
800 
build(SPXModulus & F,const SPX & f)801 inline void build(SPXModulus& F, const SPX& f) { F=f; }
802 // pre-computes information about f and stores it in F; deg(f) > 0.
803 // Note that the declaration SPXModulus F(f) is equivalent to
804 // SPXModulus F; build(F, f).
805 
806 // In the following, f refers to the polynomial f supplied to the
807 // build routine, and n = deg(f).
808 
sppolyConstOnePoly(deg,SPXModulus)809 inline long sppolyConstOnePoly(deg,SPXModulus)
810 //long deg(const SPXModulus& F);  // return deg(f)
811 
812 // x = (a * b) % f
813 inline void sppolyFourPoly(MulMod,SPXModulus)
814 //void MulMod(SPX& x, const SPX& a, const SPX& b, const SPXModulus& F);
815 
816 inline SPX MulMod(const SPX& a, const SPX& b, const SPXModulus& f)
817 { SPX x; MulMod(x,a,b,f); return x; }
818 
819 
820 // x = a^2 % f
sppolyThreePoly(SqrMod,SPXModulus)821 inline void sppolyThreePoly(SqrMod,SPXModulus)
822 //void SqrMod(SPX& x, const SPX& a, const SPXModulus& F);
823 
824 inline SPX SqrMod(const SPX& a, const SPXModulus& f)
825 { SPX x; SqrMod(x,a,f); return x; }
826 
827 // x = (a * X) mod f
sppolyThreePoly(MulByXMod,SPXModulus)828 inline void sppolyThreePoly(MulByXMod,SPXModulus)
829 //void MulByXMod(SPX& x, const SPX& a, const SPXModulus& F);
830 
831 inline SPX MulByXMod(const SPX& a, const SPXModulus& f)
832 { SPX x; MulByXMod(x,a,f); return x; }
833 
834 /**
835 // x = a^{-1} % f, error is a is not invertible
836 inline void sppolyThreePoly(InvMod,SPXModulus)
837 //void InvMod(SPX& x, const SPX& a, const SPXModulus& f);
838 
839 inline SPX InvMod(const SPX& a, const SPXModulus& f)
840 { SPX x; InvMod(x,a,f); return x; }
841 **/
842 
843 template <class T>
PowerXMod(SPX & x,T e,const SPXModulus & F)844 void PowerXMod(SPX& x, T e, const SPXModulus& F)
845 {
846   x.resetMod(F.getMod());
847   if (isGF2(x.getMod())) NTL::PowerXMod(x.gf2x, e, F.gf2x);
848   else {
849     NTL::zz_pPush push(getContext(x.getMod()));
850     NTL::PowerXMod(x.zzpx, e, F.zzpx);
851   }
852 }
853 
854 
855 template <class Z> // Z is either long or const ZZ&
PowerMod(SPX & x,const SPX & a,Z e,const SPXModulus & F)856 void PowerMod(SPX& x, const SPX& a, Z e, const SPXModulus& F)
857 {
858   SPX::ensureConsistency(a,F);
859   x.resetMod(a.getMod());
860   if (isGF2(a.getMod()))
861     NTL::PowerMod(x.gf2x, a.gf2x, e, F.gf2x);
862   else {
863     NTL::zz_pPush push(getContext(a.getMod()));
864     NTL::PowerMod(x.zzpx, a.zzpx, e, F.zzpx);
865   }
866 }
867 
868 template <class Z> // Z is either long or const ZZ&
PowerMod(const SPX & a,Z e,const SPXModulus & F)869 SPX PowerMod(const SPX& a, Z e, const SPXModulus& F)
870 { SPX x; PowerMod<Z>(x, a, e, F); return x; }
871 
872 
873 // q = a/b
874 inline void sppolyThreePoly(div,SPXModulus)
875 //void div(SPX& q, const SPX& a, const SPXModulus& F);
876 
877 // r = a%b
878 inline void sppolyThreePoly(rem,SPXModulus)
879 //void rem(SPX& x, const SPX& a, const SPXModulus& F);
880 
881 
882 // x = a % f
883 
884 //void DivRem(SPX& q, SPX& r, const SPX& a, const SPXModulus& F);
885 // q = a/f, r = a%f (defined above in a template)
886 
887 // operator notation:
888 
889 inline SPX operator/(const SPX& a, const SPXModulus& F)
890 { SPX x; div(x,a,F); return x; }
891 inline SPX operator%(const SPX& a, const SPXModulus& F)
892 { SPX x; rem(x,a,F); return x; }
893 
894 inline SPX& operator/=(SPX& x, const SPXModulus& F)
895 {
896   SPX::ensureConsistency(x,F);
897   if (isGF2(x.getMod()))
898     x.gf2x /= F.gf2x;
899   else {
900     NTL::zz_pPush push(getContext(x.getMod()));
901     x.zzpx /= F.zzpx;
902   }
903   return x;
904 }
905 
906 inline SPX& operator%=(SPX& x, const SPXModulus& F)
907 {
908   SPX::ensureConsistency(x,F);
909   if (isGF2(x.getMod()))
910     x.gf2x %= F.gf2x;
911   else {
912     NTL::zz_pPush push(getContext(x.getMod()));
913     x.zzpx %= F.zzpx;
914   }
915   return x;
916 }
917 
918 
919 /**************************************************************************\
920 
921                               Modular Composition
922 
923 Modular composition is the problem of computing g(h) mod f for
924 polynomials f, g, and h.
925 
926 The algorithm employed is that of Brent & Kung (Fast algorithms for
927 manipulating formal power series, JACM 25:581-595, 1978), which uses
928 O(n^{1/2}) modular polynomial multiplications, and O(n^2) scalar
929 operations.
930 
931 
932 
933 \**************************************************************************/
934 
935 // x = g(h) mod f; deg(h) < n
sppolyFourPoly(CompMod,SPXModulus)936 inline void sppolyFourPoly(CompMod,SPXModulus)
937 //void CompMod(SPX& x, const SPX& g, const SPX& h, const SPXModulus& F);
938 
939 SPX CompMod(const SPX& g, const SPX& h, const SPXModulus& F)
940 { SPX x; CompMod(x,g,h,F); return x; }
941 
Comp2Mod(SPX & x1,SPX & x2,const SPX & g1,const SPX & g2,const SPX & h,const SPXModulus & F)942 inline void Comp2Mod(SPX& x1, SPX& x2, const SPX& g1, const SPX& g2,
943 		     const SPX& h, const SPXModulus& F)
944 {
945   SPX::ensureConsistency(g1,g2,F); SPX::ensureConsistency(h,F);
946   x1.resetMod(g1.getMod());
947   x2.resetMod(g2.getMod());
948   if (isGF2(F.getMod()))
949     NTL::Comp2Mod(x1.gf2x, x2.gf2x,
950 	     g1.gf2x, g2.gf2x, h.gf2x, F.gf2x);
951   else {
952     NTL::zz_pPush push(getContext(F.getMod()));
953     NTL::Comp2Mod(x1.zzpx, x2.zzpx,
954 	     g1.zzpx, g2.zzpx, h.zzpx, F.zzpx);
955   }
956 }
957 // xi = gi(h) mod f (i=1,2), deg(h) < n.
958 
Comp3Mod(SPX & x1,SPX & x2,SPX & x3,const SPX & g1,const SPX & g2,const SPX & g3,const SPX & h,const SPXModulus & F)959 inline void Comp3Mod(SPX& x1, SPX& x2, SPX& x3,
960 		     const SPX& g1, const SPX& g2, const SPX& g3,
961 		     const SPX& h, const SPXModulus& F)
962 {
963   SPX::ensureConsistency(g1,g2,F); SPX::ensureConsistency(g3,h,F);
964   x1.resetMod(g1.getMod());
965   x2.resetMod(g2.getMod());
966   x3.resetMod(g3.getMod());
967   if (isGF2(F.getMod()))
968     NTL::Comp3Mod(x1.gf2x, x2.gf2x, x3.gf2x,
969 	     g1.gf2x, g2.gf2x, g3.gf2x, h.gf2x, F.gf2x);
970   else {
971     NTL::zz_pPush push(getContext(F.getMod()));
972     NTL::Comp3Mod(x1.zzpx, x2.zzpx, x3.zzpx,
973 	     g1.zzpx, g2.zzpx, g3.zzpx, h.zzpx, F.zzpx);
974   }
975 }
976 
977 // xi = gi(h) mod f (i=1..3), deg(h) < n
978 
979 
980 /**************************************************************************\
981 
982                      Composition with Pre-Conditioning
983 
984 If a single h is going to be used with many g's then you should build
985 a SPXArgument for h, and then use the compose routine below.  The
986 routine build computes and stores h, h^2, ..., h^m mod f.  After this
987 pre-computation, composing a polynomial of degree roughly n with h
988 takes n/m multiplies mod f, plus n^2 scalar multiplies.  Thus,
989 increasing m increases the space requirement and the pre-computation
990 time, but reduces the composition time.
991 
992 \**************************************************************************/
993 
994 
995 struct SPXArgument {
996   SPmodulus modulus;
997   NTL::GF2XArgument gf2x;
998   NTL::zz_pXArgument zzpx;
999 
1000   // access methods
getModSPXArgument1001   const SPmodulus& getMod() const { return modulus; }
1002 
resetModSPXArgument1003   void resetMod(const SPmodulus& mod)
1004   {
1005     if (modulus.null()) { // uninititalized object
1006       modulus = mod;
1007       return;
1008     }
1009     bool noteq = false;
1010     if (isGF2(mod)!=isGF2(modulus))          noteq = true;
1011     if (!isGF2(mod) && !mod.equals(modulus)) noteq = true;
1012     if (noteq) {
1013       gf2x.H.kill();
1014       zzpx.H.kill();
1015       modulus = mod;
1016     }
1017   }
1018 };
1019 
build(SPXArgument & H,const SPX & h,const SPXModulus & F,long m)1020 inline void build(SPXArgument& H,
1021 		  const SPX& h, const SPXModulus& F, long m)
1022 {
1023   SPX::ensureConsistency(h,F);
1024   H.resetMod(h.getMod());
1025   if (isGF2(h.getMod()))
1026     NTL::build(H.gf2x, h.gf2x, F.gf2x, m);
1027   else {
1028     NTL::zz_pPush push(getContext(h.getMod()));
1029     NTL::build(H.zzpx, h.zzpx, F.zzpx, m);
1030   }
1031 }
1032 // Pre-Computes information about h.  m > 0, deg(h) < n
1033 
CompMod(SPX & x,const SPX & g,const SPXArgument & H,const SPXModulus & F)1034 inline void CompMod(SPX& x, const SPX& g,
1035 		    const SPXArgument& H, const SPXModulus& F)
1036 {
1037   SPX::ensureConsistency(g,H,F);
1038   x.resetMod(F.getMod());
1039   if (isGF2(F.getMod()))
1040     NTL::CompMod(x.gf2x, g.gf2x, H.gf2x, F.gf2x);
1041   else {
1042     NTL::zz_pPush push(getContext(F.getMod()));
1043     NTL::CompMod(x.zzpx, g.zzpx, H.zzpx, F.zzpx);
1044   }
1045 }
1046 
CompMod(const SPX & g,const SPXArgument & H,const SPXModulus & F)1047 inline SPX CompMod(const SPX& g, const SPXArgument& H,
1048 		      const SPXModulus& F)
1049 { SPX x; CompMod(x,g,H,F); return x; }
1050 
setPolyArgBound(long B)1051 inline void setPolyArgBound(long B)
1052 {
1053   extern long GF2XArgBound;
1054   extern long zz_pXArgBound;
1055   GF2XArgBound = zz_pXArgBound = B;
1056 }
1057 // Initially the bounds are 0.  If this is set to a value greater than
1058 // zero, then composition routines will allocate a table of no than
1059 // about SPXArgBound KB.  Setting this value affects all compose
1060 // routines and the power projection and minimal polynomial routines
1061 // below, and indirectly affects many routines in SPXFactoring.
1062 
1063 /**************************************************************************\
1064 
1065                      Power Projection routines
1066 
1067 \**************************************************************************/
1068 
project(long & x,const NTL::Vec<long> & a,const SPX & b)1069 inline void project(long& x, const NTL::Vec<long>& a, const SPX& b)
1070 {
1071   if (isGF2(b.getMod())) {
1072     NTL::GF2 x2;
1073     NTL::vec_GF2 a2 = NTL::conv<NTL::vec_GF2>(a);
1074     NTL::project(x2, a2, b.gf2x);
1075     NTL::conv(x,x2);
1076   }
1077   else {
1078     NTL::zz_pPush push(getContext(b.getMod()));
1079     NTL::zz_p xp;
1080     NTL::vec_zz_p ap = NTL::conv<NTL::vec_zz_p>(a);
1081     NTL::project(xp, ap, b.zzpx);
1082     NTL::conv(x,xp);
1083   }
1084 }
project(const NTL::Vec<long> & a,const SPX & b)1085 inline long project(const NTL::Vec<long>& a, const SPX& b)
1086 { long x; project(x,a,b); return x; }
1087 // x = inner product of a with coefficient vector of b (conv needed)
1088 
1089 
1090 // class T is either SPX or SPXArgument
1091 template <class T>
ProjectPowers(NTL::Vec<long> & x,const NTL::Vec<long> & a,long k,const T & h,const SPXModulus & F)1092 inline void ProjectPowers(NTL::Vec<long>& x, const NTL::Vec<long>& a, long k,
1093 			  const T& h, const SPXModulus& F)
1094 {
1095   SPX::ensureConsistency(h,F);
1096   if (isGF2(F.getMod())) {
1097     NTL::vec_GF2 x2;
1098     NTL::vec_GF2 a2 = NTL::conv<NTL::vec_GF2>(a);
1099     NTL::ProjectPowers(x2, a2, k, h.gf2x, F.gf2x);
1100     NTL::conv(x,x2);
1101   }
1102   else {
1103     NTL::zz_pPush push(getContext(F.getMod()));
1104     NTL::vec_zz_p xp;
1105     NTL::vec_zz_p ap = NTL::conv<NTL::vec_zz_p>(a);
1106     NTL::ProjectPowers(xp, ap, k, h.zzpx, F.zzpx);
1107     NTL::conv(x,xp);
1108   }
1109 }
1110 
1111 template <class T>
ProjectPowers(const NTL::Vec<long> & a,long k,const T & h,const SPXModulus & F)1112 inline NTL::Vec<long> ProjectPowers(const NTL::Vec<long>& a, long k,
1113                    const T& h, const SPXModulus& F)
1114 { NTL::Vec<long> x; ProjectPowers(x,a,k,h,F); return x; }
1115 
1116 // Computes the vector
1117 
1118 //   (project(a, 1), project(a, h), ..., project(a, h^{k-1} % f).
1119 
1120 // Restriction: must have a.length <= deg(F) and deg(h) < deg(F).
1121 // This operation is really the "transpose" of the modular composition
1122 // operation.
1123 
1124 /**************************************************************************\
1125 
1126                               Minimum Polynomials
1127 
1128 All of these routines implement the algorithm from [Shoup, J. Symbolic
1129 Comp. 17:371-391, 1994] and [Shoup, J. Symbolic Comp. 20:363-397,
1130 1995], based on transposed modular composition and the
1131 Berlekamp/Massey algorithm.
1132 
1133 \**************************************************************************/
1134 
1135 
1136 inline void
MinPolySeq(SPX & h,const NTL::Vec<long> & a,long m,const SPmodulus & mod)1137 MinPolySeq(SPX& h, const NTL::Vec<long>& a, long m, const SPmodulus& mod)
1138 {
1139   h.resetMod(mod);
1140   if (isGF2(h.getMod())) {
1141     NTL::vec_GF2 a2 = NTL::conv<NTL::vec_GF2>(a);
1142     NTL::MinPolySeq(h.gf2x, a2, m);
1143   }
1144   else {
1145     NTL::zz_pPush push(getContext(h.getMod()));
1146     NTL::vec_zz_p ap = NTL::conv<NTL::vec_zz_p>(a);
1147     NTL::MinPolySeq(h.zzpx, ap, m);
1148   }
1149 }
1150 // computes the minimum polynomial of a linealy generated sequence; m is a
1151 // bound on the degree of the polynomial; required: a.length() >= 2*m
1152 
sppolyThreePolyArg(ProbMinPolyMod,SPXModulus)1153 inline void sppolyThreePolyArg(ProbMinPolyMod,SPXModulus)
1154 //void ProbMinPolyMod(SPX& h,const SPX& g,const SPXModulus& F,long m);
1155 
1156 inline SPX ProbMinPolyMod(const SPX& g, const SPXModulus& F, long m)
1157 { SPX h; ProbMinPolyMod(h,g,F,m); return h; }
1158 
sppolyThreePoly(ProbMinPolyMod,SPXModulus)1159 inline void sppolyThreePoly(ProbMinPolyMod,SPXModulus)
1160 //void ProbMinPolyMod(SPX& h, const SPX& g, const SPXModulus& F);
1161 
1162 inline SPX ProbMinPolyMod(const SPX& g, const SPXModulus& F)
1163 { SPX h; ProbMinPolyMod(h,g,F); return h; }
1164 
1165 // computes the monic minimal polynomial of (g mod f).  m is a bound on
1166 // the degree of the minimal polynomial; in the second version, this
1167 // argument defaults to n.  The algorithm is probabilistic; it always
1168 // returns a divisor of the minimal polynomial, possibly a proper divisor.
1169 
sppolyThreePolyArg(MinPolyMod,SPXModulus)1170 inline void sppolyThreePolyArg(MinPolyMod,SPXModulus)
1171 //void MinPolyMod(SPX& h, const SPX& g, const SPXModulus& F, long m);
1172 
1173 SPX MinPolyMod(const SPX& g, const SPXModulus& F, long m)
1174 { SPX h; MinPolyMod(h,g,F,m); return h; }
1175 
sppolyThreePoly(MinPolyMod,SPXModulus)1176 inline void sppolyThreePoly(MinPolyMod,SPXModulus)
1177 //void MinPolyMod(SPX& h, const SPX& g, const SPXModulus& F);
1178 
1179 inline SPX MinPolyMod(const SPX& g, const SPXModulus& F)
1180 { SPX h; MinPolyMod(h,g,F); return h; }
1181 
1182 
1183 // same as above, but guarantees that result is correct
1184 
sppolyThreePolyArg(IrredPolyMod,SPXModulus)1185 inline void sppolyThreePolyArg(IrredPolyMod,SPXModulus)
1186 //void IrredPolyMod(SPX& h, const SPX& g, const SPXModulus& F, long m)
1187 
1188 inline SPX IrredPolyMod(const SPX& g, const SPXModulus& F, long m)
1189 { SPX h; IrredPolyMod(h,g,F,m); return h; }
1190 
sppolyThreePoly(IrredPolyMod,SPXModulus)1191 inline void sppolyThreePoly(IrredPolyMod,SPXModulus)
1192 //void IrredPolyMod(SPX& h, const SPX& g, const SPXModulus& F);
1193 
1194 inline SPX IrredPolyMod(const SPX& g, const SPXModulus& F)
1195 { SPX h; IrredPolyMod(h,g,F); return h; }
1196 
1197 
1198 // same as above, but assumes that F is irreducible, or at least that
1199 // the minimal poly of g is itself irreducible.  The algorithm is
1200 // deterministic (and is always correct).
1201 
1202 
1203 /**************************************************************************\
1204 
1205                                 Traces
1206 
1207 \**************************************************************************/
1208 
1209 // class T can be either SPX or SPXModulus
1210 template<class T>
TraceMod(long & x,const SPX & a,const T & F)1211 void TraceMod(long& x, const SPX& a, const T& F)
1212 {
1213   SPX::ensureConsistency(a,F);
1214   if (isGF2(a.getMod())) {
1215     NTL::GF2 x2;
1216     NTL::TraceMod(x2, a.gf2x, F.gf2x);
1217     NTL::conv(x,x2);
1218   } else {
1219     NTL::zz_pPush push(getContext(a.getMod()));
1220     NTL::zz_p xp;
1221     NTL::TraceMod(xp, a.zzpx, F.zzpx);
1222     NTL::conv(x,xp);
1223   }
1224 }
1225 template<class T>
TraceMod(const SPX & a,const T & F)1226 long TraceMod(const SPX& a, const T& F)
1227 { long x; TraceMod(x,a,F); return x; }
1228 
1229 //void TraceMod(long& x, const SPX& a, const SPX& f);
1230 //long TraceMod(const SPX& a, const SPX& f);
1231 // x = Trace(a mod f); deg(a) < deg(f)
1232 
TraceVec(NTL::Vec<long> & S,const SPX & f)1233 inline void TraceVec(NTL::Vec<long>& S, const SPX& f)
1234 {
1235   if (isGF2(f.getMod())) {
1236     NTL::vec_GF2 S2;
1237     TraceVec(S2, f.gf2x);
1238     NTL::conv(S,S2);
1239   }
1240   else {
1241     NTL::zz_pPush push(getContext(f.getMod()));
1242     NTL::vec_zz_p Sp;
1243     TraceVec(Sp, f.zzpx);
1244     NTL::conv(S,Sp);
1245   }
1246 }
1247 
TraceVec(const SPX & f)1248 inline NTL::Vec<long> TraceVec(const SPX& f)
1249 { NTL::Vec<long> S; TraceVec(S,f); return S; }
1250 // S[i] = Trace(X^i mod f), i = 0..deg(f)-1; 0 < deg(f)
1251 
1252 // The above routines implement the asymptotically fast trace
1253 // algorithm from [von zur Gathen and Shoup, Computational Complexity,
1254 // 1992].
1255 
1256 
1257 /**************************************************************************\
1258                            Miscellany
1259 \**************************************************************************/
1260 
1261 inline void sppolyOnePoly(clear)
1262 //void clear(SPX& x); // x = 0
1263 
1264 inline void sppolyOnePoly(set)
1265 //void set(SPX& x);   // x = 1
1266 
1267 inline void sppolyTwoPoly(swap, SPX)
1268 //void swap(SPX& x, SPX& y);
1269 // swap (via "pointer swapping" -- if possible)
1270 
1271 
1272 /**************************************************************************\
1273                            Factoring
1274 \**************************************************************************/
1275 #include <NTL/GF2XFactoring.h>
1276 #include <NTL/pair_GF2X_long.h>
1277 #include <NTL/lzz_pXFactoring.h>
1278 #include <NTL/pair_zz_pX_long.h>
1279 
1280 typedef NTL::Pair<SPX,long> pair_SPX_long;
1281 typedef NTL::Vec<pair_SPX_long> vec_pair_SPX_long;
1282 /*
1283 void SquareFreeDecomp(vec_pair_SPX_long& u, const SPX& f);
1284 inline vec_pair_SPX_long SquareFreeDecomp(const SPX& f)
1285 { vec_pair_SPX_long vp; SquareFreeDecomp(vp, f); return vp; }
1286 
1287 // Performs square-free decomposition. f must be monic, if f = prod_i gi^ei
1288 // then u is set to a list of pairs (gi,ei). The list is is increasing order
1289 // of ei, with trivial terms (i.e., gi=1) deleted.
1290 
1291 
1292 void DDF(vec_pair_SPX_long& factors, const SPX& f, long verbose=0);
1293 inline vec_pair_SPX_long DDF(const SPX& f, long verbose=0)
1294 { vec_pair_SPX_long vp; DDF(vp, f, verbose); return vp; }
1295 
1296 // This computes a distinct-degree factorization.  The input must be
1297 // monic and square-free.  factors is set to a list of pairs (g, d),
1298 // where g is the product of all irreducible factors of f of degree d.
1299 // Only nontrivial pairs (i.e., g != 1) are included.
1300 */
1301 
1302 
1303 inline void EDF(vec_SPX& factors, const SPX& f, long d, long verbose=0)
1304 {
1305   SPX tmp(f.getMod());
1306   factors.kill(); // remove everything from factors (if any)
1307   if (isGF2(f.getMod())) {
1308     NTL::vec_GF2X fac2;
1309     NTL::EDF(fac2, f.gf2x, d, verbose);
1310     factors.SetLength(fac2.length(), tmp); // allocate space
1311     for (long i=0; i<factors.length(); i++)
1312       factors[i].gf2x = fac2[i];
1313   } else {
1314     NTL::zz_pPush push(getContext(f.getMod()));
1315     NTL::vec_zz_pX facp;
1316     NTL::EDF(facp, f.zzpx, PowerXMod(NTL::zz_p::modulus(),f.zzpx), d, verbose);
1317     factors.SetLength(facp.length(), tmp); // allocate space
1318     for (long i=0; i<factors.length(); i++)
1319       factors[i].zzpx = facp[i];
1320   }
1321 }
1322 inline vec_SPX EDF(const SPX& f, long d, long verbose=0)
1323 { vec_SPX factors;  EDF(factors, f, d, verbose); return factors; }
1324 
1325 // Performs equal-degree factorization.  f is monic, square-free,
1326 // and all irreducible factors have same degree.  d = degree of
1327 // irreducible factors of f
1328 
1329 inline void SFCanZass(vec_SPX& factors, const SPX& f, long verbose=0)
1330 {
1331   SPX tmp(f.getMod());
1332   factors.kill(); // remove everything from factors (if any)
1333   if (isGF2(f.getMod())) {
1334     NTL::vec_GF2X fac2;
1335     NTL::SFCanZass(fac2, f.gf2x, verbose);
1336     factors.SetLength(fac2.length(), tmp); // allocate space
1337     for (long i=0; i<factors.length(); i++)
1338       factors[i].gf2x = fac2[i];
1339   } else {
1340     NTL::zz_pPush push(getContext(f.getMod()));
1341     NTL::vec_zz_pX facp;
1342     NTL::SFCanZass(facp, f.zzpx, verbose);
1343     factors.SetLength(facp.length(), tmp); // allocate space
1344     for (long i=0; i<factors.length(); i++)
1345       factors[i].zzpx = facp[i];
1346   }
1347 }
1348 inline vec_SPX SFCanZass(const SPX& f, long verbose=0)
1349 { vec_SPX factors; SFCanZass(factors, f, verbose); return factors; }
1350 
1351 // Assumes f is monic and square-free.  returns list of factors of f.
1352 
1353 
1354 inline void CanZass(vec_pair_SPX_long& factors, const SPX& f, long verbose=0)
1355 {
1356   pair_SPX_long tmp = NTL::Pair<SPX,long>(SPX(f.getMod()),0);
1357   factors.kill(); // remove everything from factors (if any)
1358   if (isGF2(f.getMod())) {
1359     NTL::vec_pair_GF2X_long fac2;
1360     NTL::CanZass(fac2, f.gf2x, verbose);
1361     factors.SetLength(fac2.length(), tmp); // allocate space
1362     for (long i=0; i<factors.length(); i++) {
1363       factors[i].a.gf2x = fac2[i].a;
1364       factors[i].b = fac2[i].b;
1365     }
1366   } else {
1367     NTL::zz_pPush push(getContext(f.getMod()));
1368     NTL::vec_pair_zz_pX_long facp;
1369     NTL::CanZass(facp, f.zzpx, verbose);
1370     factors.SetLength(facp.length(), tmp); // allocate space
1371     for (long i=0; i<factors.length(); i++) {
1372       factors[i].a.zzpx = facp[i].a;
1373       factors[i].b = facp[i].b;
1374     }
1375   }
1376 }
1377 inline vec_pair_SPX_long CanZass(const SPX& f, long verbose=0)
1378 { vec_pair_SPX_long factors; CanZass(factors, f, verbose); return factors; }
1379 
1380 // returns a list of factors, with multiplicities.  f must be monic.
1381 // Calls SquareFreeDecomp and SFCanZass.
1382 
1383 
mul(SPX & f,const vec_pair_SPX_long & v,const SPmodulus & mod)1384 inline void mul(SPX& f, const vec_pair_SPX_long& v, const SPmodulus& mod)
1385 {
1386   f.resetMod(mod);
1387   if (isGF2(f.getMod())) {
1388     NTL::vec_pair_GF2X_long v2;
1389     v2.SetLength(v.length());
1390     for (long i=0; i<v2.length(); i++) {
1391       v2[i].a = v[i].a.gf2x;
1392       v2[i].b = v[i].b;
1393     }
1394     NTL::mul(f.gf2x, v2);
1395   } else {
1396     NTL::zz_pPush push(getContext(f.getMod()));
1397     NTL::vec_pair_zz_pX_long vp;
1398     vp.SetLength(v.length());
1399     for (long i=0; i<vp.length(); i++) {
1400       vp[i].a = v[i].a.zzpx;
1401       vp[i].b = v[i].b;
1402     }
1403     NTL::mul(f.zzpx, vp);
1404   }
1405 }
mul(const vec_pair_SPX_long & v,const SPmodulus & mod)1406 inline SPX mul(const vec_pair_SPX_long& v, const SPmodulus& mod)
1407 { SPX f; mul(f,v,mod); return f; }
1408 
1409 // multiplies polynomials, with multiplicities
1410 
1411 
1412 /**************************************************************************\
1413                             Irreducible Polynomials
1414 \**************************************************************************/
1415 
IterIrredTest(const SPX & f)1416 inline bool IterIrredTest(const SPX& f)
1417 {
1418   if (isGF2(f.getMod()))
1419     return NTL::IterIrredTest(f.gf2x);
1420   else {
1421     NTL::zz_pPush push(getContext(f.getMod()));
1422     return NTL::IterIrredTest(f.zzpx);
1423   }
1424 }
1425 
1426 // performs an iterative deterministic irreducibility test, based on
1427 // DDF.  Fast on average (when f has a small factor).
1428 
1429 /**
1430 inline void BuildSparseIrred(SPX& f, long n, const SPmodulus& mod)
1431 {
1432   f.resetMod(mod);
1433   if (isGF2(mod))
1434     NTL::BuildSparseIrred(f.gf2x, n);
1435   else {
1436     NTL::zz_pPush push(getContext(f.getMod()));
1437     NTL::BuildSparseIrred(f.zzpx, n);
1438   }
1439 }
1440 inline SPX BuildSparseIrred_SPX(long n, const SPmodulus& mod)
1441 { SPX f; BuildSparseIrred(f,n,mod); return f; }
1442 **/
1443 
1444 // Builds a monic irreducible polynomial of degree n.
1445 // If there is an irreducible trinomial X^n + X^k + 1,
1446 // then the one with minimal k is chosen.
1447 // Otherwise, if there is an irreducible pentanomial
1448 // X^n + X^k3 + X^k2 + x^k1 + 1, then the one with minimal
1449 // k3 is chosen (minimizing first k2 and then k1).
1450 // Otherwise, if there is niether an irreducible trinomial
1451 // or pentanomial, the routine result from BuildIrred (see below)
1452 // is chosen---this is probably only of academic interest,
1453 // since it a reasonable, but unproved, conjecture that they
1454 // exist for every n > 1.
1455 
1456 // For n <= 2048, the polynomial is constructed
1457 // by table lookup in a pre-computed table.
1458 
1459 // The SPXModulus data structure and routines (and indirectly GF2E)
1460 // are optimized to deal with the output from BuildSparseIrred.
1461 
BuildIrred(SPX & f,long n,const SPmodulus & mod)1462 inline void BuildIrred(SPX& f, long n, const SPmodulus& mod)
1463 {
1464   f.resetMod(mod);
1465   if (isGF2(mod))
1466     NTL::BuildIrred(f.gf2x, n);
1467   else {
1468     NTL::zz_pPush push(getContext(f.getMod()));
1469     NTL::BuildIrred(f.zzpx, n);
1470   }
1471 }
1472 
BuildIrred_SPX(long n,const SPmodulus & mod)1473 inline SPX BuildIrred_SPX(long n, const SPmodulus& mod)
1474 { SPX f; BuildIrred(f,n,mod); return f; }
1475 
1476 // Build a monic irreducible poly of degree n.  The polynomial
1477 // constructed is "canonical" in the sense that it is of the form
1478 // f=X^n + g, where the bits of g are the those of the smallest
1479 // non-negative integer that make f irreducible.
1480 
1481 // The SPXModulus data structure and routines (and indirectly GF2E)
1482 // are optimized to deal with the output from BuildIrred.
1483 
1484 // Note that the output from BuildSparseIrred will generally yield
1485 // a "better" representation (in terms of efficiency) for
1486 // GF(2^n) than the output from BuildIrred.
1487 
1488 
1489 
BuildRandomIrred(SPX & f,const SPX & g)1490 inline void BuildRandomIrred(SPX& f, const SPX& g)
1491 {
1492   f.resetMod(g.getMod());
1493   if (isGF2(g.getMod()))
1494     NTL::BuildRandomIrred(f.gf2x, g.gf2x);
1495   else {
1496     NTL::zz_pPush push(getContext(g.getMod()));
1497     NTL::BuildRandomIrred(f.zzpx, g.zzpx);
1498   }
1499 }
1500 
BuildRandomIrred(const SPX & g)1501 inline SPX BuildRandomIrred(const SPX& g)
1502 { SPX f; BuildRandomIrred(f, g); return f; }
1503 
1504 // g is a monic irreducible polynomial.  Constructs a random monic
1505 // irreducible polynomial f of the same degree.
1506 
1507 #endif // #ifdef _HELIB_SPX_H_
1508