/** Test constructor and toString. */
  public void testConstruction() {
    c = fac.getONE();
    assertTrue("length( c ) = 1", c.length() == 1);
    assertTrue("isZERO( c )", !c.isZERO());
    assertTrue("isONE( c )", c.isONE());

    d = fac.getZERO();
    assertTrue("length( d ) = 0", d.length() == 0);
    assertTrue("isZERO( d )", d.isZERO());
    assertTrue("isONE( d )", !d.isONE());
  }
 /** Test random polynomial. */
 public void testRandom() {
   for (int i = 0; i < 7; i++) {
     a = fac.random(ll);
     // fac.random(rl+i, kl*(i+1), ll+2*i, el+i, q );
     assertTrue("length( a" + i + " ) <> 0", a.length() >= 0);
     assertTrue(" not isZERO( a" + i + " )", !a.isZERO());
     assertTrue(" not isONE( a" + i + " )", !a.isONE());
   }
 }
  /** Test modular evaluation gcd. */
  public void testModEvalGcd() {

    GreatestCommonDivisorAbstract<ModInteger> ufd_me = new GreatestCommonDivisorModEval();

    for (int i = 0; i < 1; i++) {
      a = dfac.random(kl * (i + 2), ll + 2 * i, el + 0 * i, q);
      b = dfac.random(kl * (i + 2), ll + 2 * i, el + 0 * i, q);
      c = dfac.random(kl * (i + 2), ll + 2 * i, el + 0 * i, q);
      c = c.multiply(dfac.univariate(0));
      // a = ufd.basePrimitivePart(a);
      // b = ufd.basePrimitivePart(b);

      if (a.isZERO() || b.isZERO() || c.isZERO()) {
        // skip for this turn
        continue;
      }
      assertTrue("length( c" + i + " ) <> 0", c.length() > 0);
      // assertTrue(" not isZERO( c"+i+" )", !c.isZERO() );
      // assertTrue(" not isONE( c"+i+" )", !c.isONE() );

      a = a.multiply(c);
      b = b.multiply(c);
      // System.out.println("a  = " + a);
      // System.out.println("b  = " + b);

      d = ufd_me.gcd(a, b);

      c = ufd.basePrimitivePart(c).abs();
      e = PolyUtil.<ModInteger>basePseudoRemainder(d, c);
      // System.out.println("c  = " + c);
      // System.out.println("d  = " + d);
      assertTrue("c | gcd(ac,bc) " + e, e.isZERO());

      e = PolyUtil.<ModInteger>basePseudoRemainder(a, d);
      // System.out.println("e = " + e);
      assertTrue("gcd(a,b) | a" + e, e.isZERO());

      e = PolyUtil.<ModInteger>basePseudoRemainder(b, d);
      // System.out.println("e = " + e);
      assertTrue("gcd(a,b) | b" + e, e.isZERO());
    }
  }
  /**
   * Minimal ordered groebner basis.
   *
   * @param Fp a Groebner base.
   * @return a reduced Groebner base of Fp.
   */
  @SuppressWarnings("cast")
  @Override
  public List<GenPolynomial<C>> minimalGB(List<GenPolynomial<C>> Fp) {
    GenPolynomial<C> a;
    ArrayList<GenPolynomial<C>> G;
    G = new ArrayList<GenPolynomial<C>>(Fp.size());
    ListIterator<GenPolynomial<C>> it = Fp.listIterator();
    while (it.hasNext()) {
      a = it.next();
      if (a.length() != 0) { // always true
        // already monic  a = a.monic();
        G.add(a);
      }
    }
    if (G.size() <= 1) {
      return G;
    }

    ExpVector e;
    ExpVector f;
    GenPolynomial<C> p;
    ArrayList<GenPolynomial<C>> F;
    F = new ArrayList<GenPolynomial<C>>(G.size());
    boolean mt;

    while (G.size() > 0) {
      a = G.remove(0);
      e = a.leadingExpVector();

      it = G.listIterator();
      mt = false;
      while (it.hasNext() && !mt) {
        p = it.next();
        f = p.leadingExpVector();
        mt = e.multipleOf(f);
      }
      it = F.listIterator();
      while (it.hasNext() && !mt) {
        p = it.next();
        f = p.leadingExpVector();
        mt = e.multipleOf(f);
      }
      if (!mt) {
        F.add(a);
      } else {
        // System.out.println("dropped " + a.length());
      }
    }
    G = F;
    if (G.size() <= 1) {
      return G;
    }
    Collections.reverse(G); // important for lex GB

    MiMPIReducerServer<C>[] mirs = (MiMPIReducerServer<C>[]) new MiMPIReducerServer[G.size()];
    int i = 0;
    F = new ArrayList<GenPolynomial<C>>(G.size());
    while (G.size() > 0) {
      a = G.remove(0);
      // System.out.println("doing " + a.length());
      List<GenPolynomial<C>> R = new ArrayList<GenPolynomial<C>>(G.size() + F.size());
      R.addAll(G);
      R.addAll(F);
      mirs[i] = new MiMPIReducerServer<C>(R, a);
      pool.addJob(mirs[i]);
      i++;
      F.add(a);
    }
    G = F;
    F = new ArrayList<GenPolynomial<C>>(G.size());
    for (i = 0; i < mirs.length; i++) {
      a = mirs[i].getNF();
      F.add(a);
    }
    return F;
  }
  /**
   * Distributed Groebner base, part for MPI master.
   *
   * @param modv number of module variables.
   * @param F polynomial list.
   * @return GB(F) a Groebner base of F or null, if a IOException occurs.
   */
  public List<GenPolynomial<C>> GBmaster(int modv, List<GenPolynomial<C>> F)
      throws MPIException, IOException {
    List<GenPolynomial<C>> G = new ArrayList<GenPolynomial<C>>();
    GenPolynomial<C> p;
    PairList<C> pairlist = null;
    boolean oneInGB = false;
    // int l = F.size();
    int unused = 0;
    ListIterator<GenPolynomial<C>> it = F.listIterator();
    while (it.hasNext()) {
      p = it.next();
      if (p.length() > 0) {
        p = p.monic();
        if (p.isONE()) {
          oneInGB = true;
          G.clear();
          G.add(p);
          // return G; must signal termination to others
        }
        if (!oneInGB) {
          G.add(p);
        }
        if (pairlist == null) {
          pairlist = strategy.create(modv, p.ring);
          if (!p.ring.coFac.isField()) {
            throw new IllegalArgumentException("coefficients not from a field");
          }
        }
        // theList not updated here
        if (p.isONE()) {
          unused = pairlist.putOne();
        } else {
          unused = pairlist.put(p);
        }
      } else {
        // l--;
      }
    }
    // if (l <= 1) {
    // return G; must signal termination to others
    // }
    logger.info("done pairlist, initialize DHT: " + unused);

    DistHashTableMPI<Integer, GenPolynomial<C>> theList =
        new DistHashTableMPI<Integer, GenPolynomial<C>>(engine);
    theList.init();
    // logger.info("done DHT: " + theList);

    List<GenPolynomial<C>> al = pairlist.getList();
    for (int i = 0; i < al.size(); i++) {
      // no wait required
      GenPolynomial<C> nn = theList.put(Integer.valueOf(i), al.get(i));
      if (nn != null) {
        logger.info("double polynomials " + i + ", nn = " + nn + ", al(i) = " + al.get(i));
      }
    }

    Terminator fin = new Terminator(threads - 1);
    MPIReducerServer<C> R;
    for (int i = 1; i < threads; i++) {
      logger.debug("addJob " + i + " of " + threads);
      MPIChannel chan = new MPIChannel(engine, i); // closed in server
      R = new MPIReducerServer<C>(i, fin, chan, theList, pairlist);
      pool.addJob(R);
    }
    logger.debug("main loop waiting");
    fin.waitDone();
    int ps = theList.size();
    logger.info("#distributed list = " + ps);
    // make sure all polynomials arrived: not needed in master
    // G = (ArrayList)theList.values();
    G = pairlist.getList();
    if (ps != G.size()) {
      logger.info("#distributed list = " + theList.size() + " #pairlist list = " + G.size());
    }
    long time = System.currentTimeMillis();
    List<GenPolynomial<C>> Gp = minimalGB(G); // not jet distributed but threaded
    time = System.currentTimeMillis() - time;
    logger.debug("parallel gbmi = " + time);
    G = Gp;
    logger.info("theList.terminate()");
    theList.terminate();
    logger.info("end" + pairlist);
    return G;
  }