/**
  * ResidueSolvableWordPolynomial left and right multiplication. Product with exponent vector.
  *
  * @param e exponent.
  * @param f exponent.
  * @return x<sup>e</sup> * this * x<sup>f</sup>, where * denotes solvable multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiply(ExpVector e, ExpVector f) {
   if (e == null || e.isZERO()) {
     return this;
   }
   if (f == null || f.isZERO()) {
     return this;
   }
   WordResidue<C> b = ring.getONECoefficient();
   return multiply(b, e, b, f);
 }
  /** Test addition. */
  public void testAddition() {

    a = fac.random(ll);
    b = fac.random(ll);

    c = a.sum(b);
    d = c.subtract(b);
    assertEquals("a+b-b = a", a, d);

    c = fac.random(ll);

    ExpVector u = ExpVector.EVRAND(rl, el, q);
    BigQuaternion x = BigQuaternion.QRAND(kl);

    b = new GenPolynomial<BigQuaternion>(fac, x, u);
    c = a.sum(b);
    d = a.sum(x, u);
    assertEquals("a+p(x,u) = a+(x,u)", c, d);

    c = a.subtract(b);
    d = a.subtract(x, u);
    assertEquals("a-p(x,u) = a-(x,u)", c, d);

    a = new GenPolynomial<BigQuaternion>(fac);
    b = new GenPolynomial<BigQuaternion>(fac, x, u);
    c = b.sum(a);
    d = a.sum(x, u);
    assertEquals("a+p(x,u) = a+(x,u)", c, d);

    c = a.subtract(b);
    d = a.subtract(x, u);
    assertEquals("a-p(x,u) = a-(x,u)", c, d);
  }
 /**
  * ResidueSolvableWordPolynomial multiplication. Left product with exponent vector.
  *
  * @param e exponent.
  * @return x<sup>e</sup> * this, where * denotes solvable multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiplyLeft(ExpVector e) {
   if (e == null || e.isZERO()) {
     return this;
   }
   ResidueSolvableWordPolynomial<C> Cp = ring.valueOf(e);
   return Cp.multiply(this);
 }
 /**
  * ResidueSolvableWordPolynomial multiplication. Commutative product with exponent vector.
  *
  * @param B solvable polynomial.
  * @param f exponent vector.
  * @return B*f, where * is commutative multiplication.
  */
 protected ResidueSolvableWordPolynomial<C> shift(
     ResidueSolvableWordPolynomial<C> B, ExpVector f) {
   ResidueSolvableWordPolynomial<C> C = ring.getZERO().copy();
   if (B == null || B.isZERO()) {
     return C;
   }
   if (f == null || f.isZERO()) {
     return B;
   }
   Map<ExpVector, WordResidue<C>> Cm = C.val;
   Map<ExpVector, WordResidue<C>> Bm = B.val;
   for (Map.Entry<ExpVector, WordResidue<C>> y : Bm.entrySet()) {
     ExpVector e = y.getKey();
     WordResidue<C> a = y.getValue();
     ExpVector d = e.sum(f);
     if (!a.isZERO()) {
       Cm.put(d, a);
     }
   }
   return C;
 }
  /** Test iterators. */
  public void testIterators() {
    // integers
    BigInteger rf = new BigInteger();
    // System.out.println("rf = " + rf);

    // polynomials over integral numbers
    GenPolynomialRing<BigInteger> pf = new GenPolynomialRing<BigInteger>(rf, rl);
    // System.out.println("pf = " + pf);

    // random polynomial
    GenPolynomial<BigInteger> p = pf.random(kl, 2 * ll, el, q);
    // System.out.println("p = " + p);

    // test monomials
    for (Monomial<BigInteger> m : p) {
      // System.out.println("m = " + m);
      assertFalse("m.c == 0 ", m.coefficient().isZERO());
      assertFalse("m.e < (0) ", m.exponent().signum() < 0);
    }

    // test exponents
    Iterator<ExpVector> et = p.exponentIterator();
    while (et.hasNext()) {
      ExpVector e = et.next();
      // System.out.println("e = " + e);
      assertFalse("e < (0) ", e.signum() < 0);
    }

    // test coefficents
    Iterator<BigInteger> ct = p.coefficientIterator();
    while (ct.hasNext()) {
      BigInteger i = ct.next();
      // System.out.println("i = " + i);
      assertFalse("i == 0 ", i.isZERO());
    }
  }
 /**
  * Random solvable polynomial.
  *
  * @param k size of random coefficients.
  * @param l number of terms.
  * @param d maximal degree in each variable.
  * @param q density of nozero exponents.
  * @param rnd is a source for random bits.
  * @return a random solvable polynomial.
  */
 @Override
 public GenSolvablePolynomial<C> random(int k, int l, int d, float q, Random rnd) {
   GenSolvablePolynomial<C> r = getZERO(); // .clone();
   // copy( ZERO );
   // new GenPolynomial<C>( this, getZERO().val );
   ExpVector e;
   C a;
   // add random coeffs and exponents
   for (int i = 0; i < l; i++) {
     e = ExpVector.EVRAND(nvar, d, q, rnd);
     a = coFac.random(k, rnd);
     r = (GenSolvablePolynomial<C>) r.sum(a, e);
     // somewhat inefficient but clean
   }
   return r;
 }
 /**
  * Reverse inverse graded lexicographical compare part. Compare entries between begin and end
  * (-1).
  *
  * @param U
  * @param V
  * @param begin
  * @param end
  * @return 0 if U == V, -1 if U &lt; V, 1 if U &gt; V.
  */
 public static int EVRIGLC(ExpVector U, ExpVector V, int begin, int end) {
   return U.revInvGradCompareTo(V, begin, end);
 }
 /**
  * Reverse inverse graded lexicographical compare.
  *
  * @param U
  * @param V
  * @return 0 if U == V, -1 if U &lt; V, 1 if U &gt; V.
  */
 public static int EVRIGLC(ExpVector U, ExpVector V) {
   return U.revInvGradCompareTo(V);
 }
 /**
  * Reverse inverse lexicographical compare part. Compare entries between begin and end (-1).
  *
  * @param U
  * @param V
  * @param begin
  * @param end
  * @return 0 if U == V, -1 if U &lt; V, 1 if U &gt; V.
  */
 public static int EVRILCP(ExpVector U, ExpVector V, int begin, int end) {
   return U.revInvLexCompareTo(V, begin, end);
 }
 /**
  * Reverse inverse lexicographical compare.
  *
  * @param U
  * @param V
  * @return 0 if U == V, -1 if U &lt; V, 1 if U &gt; V.
  */
 public static int EVRILCP(ExpVector U, ExpVector V) {
   return U.revInvLexCompareTo(V);
 }
 /**
  * ExpVector divides test. Test if V is component wise greater or equal to this.
  *
  * @param V
  * @return true if this divides V, else false.
  */
 public boolean divides(ExpVector V) {
   return V.multipleOf(this);
   // return EVMT(V, this);
 }
 /**
  * ExpVector summation.
  *
  * @param U
  * @param V
  * @return U+V.
  */
 public static ExpVector EVSUM(ExpVector U, ExpVector V) {
   return U.sum(V);
 }
 /**
  * ExpVector substitution. Clone and set exponent to d at position i.
  *
  * @param U
  * @param i position.
  * @param d new exponent.
  * @return substituted ExpVector.
  */
 public static ExpVector EVSU(ExpVector U, int i, long d) {
   return U.subst(i, d);
 }
 /**
  * ExpVector least common multiple.
  *
  * @param U
  * @param V
  * @return component wise maximum of U and V.
  */
 public static ExpVector EVLCM(ExpVector U, ExpVector V) {
   return U.lcm(V);
 }
 /**
  * ExpVector maximal degree.
  *
  * @param U
  * @return maximal exponent.
  */
 public static long EVMDEG(ExpVector U) {
   return U.maxDeg();
 }
 /**
  * ExpVector weighted degree.
  *
  * @param w weights.
  * @param U
  * @return weighted sum of all exponents.
  */
 public static long EVWDEG(long[][] w, ExpVector U) {
   return U.weightDeg(w);
 }
 /**
  * ExpVector total degree.
  *
  * @param U
  * @return sum of all exponents.
  */
 public static long EVTDEG(ExpVector U) {
   return U.totalDeg();
 }
 /**
  * ExpVector sign.
  *
  * @param U
  * @return 0 if U is zero, -1 if some entry is negative, 1 if no entry is negativ and at least one
  *     entry is positive.
  */
 public static int EVSIGN(ExpVector U) {
   return U.signum();
 }
 /**
  * ExpVector substitution. Clone and set exponent to d at position i.
  *
  * @param i position.
  * @param d new exponent.
  * @return substituted ExpVector.
  */
 public ExpVector subst(int i, long d) {
   ExpVector V = (ExpVector) this.clone();
   long e = V.setVal(i, d);
   return V;
   // return EVSU(this, i, d);
 }
 /**
  * Inverse weighted lexicographical compare.
  *
  * @param w weight array.
  * @param U
  * @param V
  * @return 0 if U == V, -1 if U &lt; V, 1 if U &gt; V.
  */
 public static int EVIWLC(long[][] w, ExpVector U, ExpVector V) {
   return U.invWeightCompareTo(w, V);
 }
 /**
  * ExpVector absolute value.
  *
  * @param U
  * @return abs(U).
  */
 public static ExpVector EVABS(ExpVector U) {
   return U.abs();
 }
 /**
  * Inverse weighted lexicographical compare part. Compare entries between begin and end (-1).
  *
  * @param w weight array.
  * @param U
  * @param V
  * @param begin
  * @param end
  * @return 0 if U == V, -1 if U &lt; V, 1 if U &gt; V.
  */
 public static int EVIWLC(long[][] w, ExpVector U, ExpVector V, int begin, int end) {
   return U.invWeightCompareTo(w, V, begin, end);
 }
 /**
  * ExpVector difference. Result may have negative entries.
  *
  * @param U
  * @param V
  * @return U-V.
  */
 public static ExpVector EVDIF(ExpVector U, ExpVector V) {
   return U.subtract(V);
 }
  // cannot @Override, @NoOverride
  public ResidueSolvableWordPolynomial<C> multiply(ResidueSolvableWordPolynomial<C> Bp) {
    if (Bp == null || Bp.isZERO()) {
      return ring.getZERO();
    }
    if (this.isZERO()) {
      return this;
    }
    assert (ring.nvar == Bp.ring.nvar);
    if (debug) {
      logger.debug("ring = " + ring);
    }
    ExpVector Z = ring.evzero;
    ResidueSolvableWordPolynomial<C> Dp = ring.getZERO().copy();
    ResidueSolvableWordPolynomial<C> zero = ring.getZERO().copy();
    WordResidue<C> one = ring.getONECoefficient();

    Map<ExpVector, WordResidue<C>> A = val;
    Map<ExpVector, WordResidue<C>> B = Bp.val;
    Set<Map.Entry<ExpVector, WordResidue<C>>> Bk = B.entrySet();
    for (Map.Entry<ExpVector, WordResidue<C>> y : A.entrySet()) {
      WordResidue<C> a = y.getValue();
      ExpVector e = y.getKey();
      if (debug) logger.info("e = " + e + ", a = " + a);
      for (Map.Entry<ExpVector, WordResidue<C>> x : Bk) {
        WordResidue<C> b = x.getValue();
        ExpVector f = x.getKey();
        if (debug) logger.info("f = " + f + ", b = " + b);
        int[] fp = f.dependencyOnVariables();
        int fl1 = 0;
        if (fp.length > 0) {
          fl1 = fp[fp.length - 1];
        }
        int fl1s = ring.nvar + 1 - fl1;
        // polynomial/residue coefficient multiplication
        ResidueSolvableWordPolynomial<C> Cps = ring.getZERO().copy();
        if (ring.polCoeff.coeffTable.isEmpty() || b.isConstant() || e.isZERO()) { // symmetric
          Cps = new ResidueSolvableWordPolynomial<C>(ring, b, e);
          if (debug) logger.info("symmetric coeff: b = " + b + ", e = " + e);
        } else { // unsymmetric
          if (debug) logger.info("unsymmetric coeff: b = " + b + ", e = " + e);
          // recursive polynomial coefficient multiplication : e * b.val
          RecSolvableWordPolynomial<C> rsp1 = new RecSolvableWordPolynomial<C>(ring.polCoeff, e);
          RecSolvableWordPolynomial<C> rsp2 =
              new RecSolvableWordPolynomial<C>(ring.polCoeff, b.val);
          RecSolvableWordPolynomial<C> rsp3 = rsp1.multiply(rsp2);
          Cps = ring.fromPolyCoefficients(rsp3);
        }
        if (debug) {
          logger.info("coeff-poly: Cps = " + Cps);
        }
        // polynomial multiplication
        ResidueSolvableWordPolynomial<C> Dps = ring.getZERO().copy();
        ResidueSolvableWordPolynomial<C> Ds = null;
        ResidueSolvableWordPolynomial<C> D1, D2;
        if (ring.table.isEmpty() || Cps.isConstant() || f.isZERO()) { // symmetric
          if (debug) logger.info("symmetric poly: b = " + b + ", e = " + e);
          ExpVector g = e.sum(f);
          if (Cps.isConstant()) {
            Ds =
                new ResidueSolvableWordPolynomial<C>(
                    ring, Cps.leadingBaseCoefficient(), g); // symmetric!
          } else {
            Ds = shift(Cps, f); // symmetric
          }
        } else { // eventually unsymmetric
          if (debug) logger.info("unsymmetric poly: Cps = " + Cps + ", f = " + f);
          for (Map.Entry<ExpVector, WordResidue<C>> z : Cps.val.entrySet()) {
            // split g = g1 * g2, f = f1 * f2
            WordResidue<C> c = z.getValue();
            ExpVector g = z.getKey();
            if (debug) logger.info("g = " + g + ", c = " + c);
            int[] gp = g.dependencyOnVariables();
            int gl1 = ring.nvar + 1;
            if (gp.length > 0) {
              gl1 = gp[0];
            }
            int gl1s = ring.nvar + 1 - gl1;
            if (gl1s <= fl1s) { // symmetric
              ExpVector h = g.sum(f);
              if (debug) logger.info("disjoint poly: g = " + g + ", f = " + f + ", h = " + h);
              Ds = (ResidueSolvableWordPolynomial<C>) zero.sum(one, h); // symmetric!
            } else {
              ExpVector g1 = g.subst(gl1, 0);
              ExpVector g2 = Z.subst(gl1, g.getVal(gl1)); // bug el1, gl1
              ExpVector g4;
              ExpVector f1 = f.subst(fl1, 0);
              ExpVector f2 = Z.subst(fl1, f.getVal(fl1));
              if (debug) logger.info("poly, g1 = " + g1 + ", f1 = " + f1 + ", Dps = " + Dps);
              if (debug) logger.info("poly, g2 = " + g2 + ", f2 = " + f2);
              TableRelation<WordResidue<C>> rel = ring.table.lookup(g2, f2);
              if (debug) logger.info("poly, g  = " + g + ", f  = " + f + ", rel = " + rel);
              Ds = new ResidueSolvableWordPolynomial<C>(ring, rel.p); // ring.copy(rel.p);
              if (rel.f != null) {
                D2 = new ResidueSolvableWordPolynomial<C>(ring, one, rel.f);
                Ds = Ds.multiply(D2);
                if (rel.e == null) {
                  g4 = g2;
                } else {
                  g4 = g2.subtract(rel.e);
                }
                ring.table.update(g4, f2, Ds);
              }
              if (rel.e != null) {
                D1 = new ResidueSolvableWordPolynomial<C>(ring, one, rel.e);
                Ds = D1.multiply(Ds);
                ring.table.update(g2, f2, Ds);
              }
              if (!f1.isZERO()) {
                D2 = new ResidueSolvableWordPolynomial<C>(ring, one, f1);
                Ds = Ds.multiply(D2);
                // ring.table.update(?,f1,Ds)
              }
              if (!g1.isZERO()) {
                D1 = new ResidueSolvableWordPolynomial<C>(ring, one, g1);
                Ds = D1.multiply(Ds);
                // ring.table.update(e1,?,Ds)
              }
            }
            Ds = Ds.multiplyLeft(c); // assume c commutes with Cs
            Dps = (ResidueSolvableWordPolynomial<C>) Dps.sum(Ds);
          } // end Dps loop
          Ds = Dps;
        }
        Ds = Ds.multiplyLeft(a); // multiply(a,b); // non-symmetric
        if (debug) logger.debug("Ds = " + Ds);
        Dp = (ResidueSolvableWordPolynomial<C>) Dp.sum(Ds);
      } // end B loop
    } // end A loop
    return Dp;
  }
 /**
  * ExpVector multiple test. Test if U is component wise greater or equal to V.
  *
  * @param U
  * @param V
  * @return true if U is a multiple of V, else false.
  */
 public static boolean EVMT(ExpVector U, ExpVector V) {
   return U.multipleOf(V);
 }
 /**
  * ExpVector greatest common divisor.
  *
  * @param U
  * @param V
  * @return component wise minimum of U and V.
  */
 public static ExpVector EVGCD(ExpVector U, ExpVector V) {
   return U.gcd(V);
 }
 /**
  * ExpVector negate.
  *
  * @param U
  * @return -U.
  */
 public static ExpVector EVNEG(ExpVector U) {
   return U.negate();
 }
 /**
  * ExpVector dependency on variables.
  *
  * @param U
  * @return array of indices where U has positive exponents.
  */
 public static int[] EVDOV(ExpVector U) {
   return U.dependencyOnVariables();
 }
  /**
   * 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;
  }