long ProbIrredTest(const ZZ_pEX& f, long iter)
{
   long n = deg(f);

   if (n <= 0) return 0;
   if (n == 1) return 1;

   ZZ_pEXModulus F;

   build(F, f);

   ZZ_pEX b, r, s;

   FrobeniusMap(b, F);

   long all_zero = 1;

   long i;

   for (i = 0; i < iter; i++) {
      random(r, n);
      TraceMap(s, r, n, F, b);

      all_zero = all_zero && IsZero(s);

      if (deg(s) > 0) return 0;
   }

   if (!all_zero || (n & 1)) return 1;

   PowerCompose(s, b, n/2, F);
   return !IsX(s);
}
static
void RecFindRoots(vec_GF2E& x, const GF2EX& f)
{
   if (deg(f) == 0) return;

   if (deg(f) == 1) {
      long k = x.length();
      x.SetLength(k+1);
      x[k] = ConstTerm(f);
      return;
   }
      
   GF2EX h;

   GF2E r;

   
   {
      GF2EXModulus F;
      build(F, f);

      do {
         random(r);
         clear(h);
         SetCoeff(h, 1, r);
         TraceMap(h, h, F);
         GCD(h, h, f);
      } while (deg(h) <= 0 || deg(h) == deg(f));
   }

   RecFindRoots(x, h);
   div(h, f, h); 
   RecFindRoots(x, h);
}
void FindRoot(GF2E& root, const GF2EX& ff)
// finds a root of ff.
// assumes that ff is monic and splits into distinct linear factors

{
   GF2EXModulus F;
   GF2EX h, h1, f;
   GF2E r;

   f = ff;
   
   if (!IsOne(LeadCoeff(f)))
      Error("FindRoot: bad args");

   if (deg(f) == 0)
      Error("FindRoot: bad args");


   while (deg(f) > 1) {
      build(F, f);
      random(r);
      clear(h);
      SetCoeff(h, 1, r);
      TraceMap(h, h, F);
      GCD(h, h, f);
      if (deg(h) > 0 && deg(h) < deg(f)) {
         if (deg(h) > deg(f)/2)
            div(f, f, h);
         else
            f = h;
      }
   }
 
   root = ConstTerm(f);
}
void EDFSplit(vec_ZZ_pEX& v, const ZZ_pEX& f, const ZZ_pEX& b, long d)
{
   ZZ_pEX a, g, h;
   ZZ_pEXModulus F;
   vec_ZZ_pE roots;
   
   build(F, f);
   long n = F.n;
   long r = n/d;
   random(a, n);
   TraceMap(g, a, d, F, b);
   MinPolyMod(h, g, F, r);
   FindRoots(roots, h);
   FindFactors(v, f, g, roots);
}
static
void EDFSplit(GF2X& f1, GF2X& f2, const GF2X& f, long d)
{
   GF2X a, g;
   GF2XModulus F;
   
   build(F, f);
   long n = F.n;

   do {
      random(a, n);
      TraceMap(g, a, d, F);
   } while (deg(g) <= 0);

   GCD(f1, f, g);
   div(f2, f, f1);
}
long ProbComputeDegree(const ZZ_pX& h, const ZZ_pXModulus& F)
{
   if (F.n == 1 || IsX(h))
      return 1;

   long n = F.n;

   ZZ_pX P1, P2, P3;

   random(P1, n);
   TraceMap(P2, P1, n, F, h);
   ProbMinPolyMod(P3, P2, F, n/2);

   long r = deg(P3);

   if (r <= 0 || n % r != 0)
      return 0;
   else
      return n/r;
}
long ProbIrredTest(const ZZ_pX& f, long iter)
{
   long n = deg(f);

   if (n <= 0) return 0;
   if (n == 1) return 1;

   const ZZ& p = ZZ_p::modulus();

   ZZ_pXModulus F;

   build(F, f);

   ZZ_pX b, r, s;

   PowerXMod(b, p, F);

   long i;

   for (i = 0; i < iter; i++) {
      random(r, n);
      TraceMap(s, r, n, F, b);

      if (deg(s) > 0) return 0;
   }

   if (p >= n) return 1;

   long pp;

   conv(pp, p);
   
   if (n % pp != 0) return 1;

   PowerCompose(s, b, n/pp, F);
   return !IsX(s);
}