/**
  * ResidueSolvableWordPolynomial multiplication. Left product with ring element and exponent
  * vector.
  *
  * @param b coefficient polynomial.
  * @param e exponent.
  * @return b x<sup>e</sup> * this, where * denotes solvable multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiplyLeft(WordResidue<C> b, ExpVector e) {
   if (b == null || b.isZERO()) {
     return ring.getZERO();
   }
   ResidueSolvableWordPolynomial<C> Cp = ring.valueOf(b, e);
   return Cp.multiply(this);
 }
 /**
  * ResidueSolvableWordPolynomial multiplication. Product with coefficient ring element.
  *
  * @param b coefficient polynomial.
  * @return this*b, where * is coefficient multiplication.
  */
 @Override
 // public GenSolvablePolynomial<WordResidue<C>> multiply(WordResidue<C> b) {
 public ResidueSolvableWordPolynomial<C> multiply(WordResidue<C> b) {
   ResidueSolvableWordPolynomial<C> Cp = ring.getZERO().copy();
   if (b == null || b.isZERO()) {
     return Cp;
   }
   Cp = ring.valueOf(b);
   return multiply(Cp);
 }
 /**
  * ResidueSolvableWordPolynomial left and right multiplication. Product with ring element and
  * exponent vector.
  *
  * @param b coefficient polynomial.
  * @param e exponent.
  * @param c coefficient polynomial.
  * @param f exponent.
  * @return b x<sup>e</sup> * this * c x<sup>f</sup>, where * denotes solvable multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiply(
     WordResidue<C> b, ExpVector e, WordResidue<C> c, ExpVector f) {
   if (b == null || b.isZERO()) {
     return ring.getZERO();
   }
   if (c == null || c.isZERO()) {
     return ring.getZERO();
   }
   ResidueSolvableWordPolynomial<C> Cp = ring.valueOf(b, e);
   ResidueSolvableWordPolynomial<C> Dp = ring.valueOf(c, f);
   return multiply(Cp, Dp);
 }
 /**
  * ResidueSolvableWordPolynomial left and right multiplication. Product with coefficient ring
  * element.
  *
  * @param b coefficient polynomial.
  * @param c coefficient polynomial.
  * @return b*this*c, where * is coefficient multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiply(WordResidue<C> b, WordResidue<C> c) {
   ResidueSolvableWordPolynomial<C> Cp = ring.getZERO().copy();
   if (b == null || b.isZERO()) {
     return Cp;
   }
   if (c == null || c.isZERO()) {
     return Cp;
   }
   ResidueSolvableWordPolynomial<C> Cb = ring.valueOf(b);
   ResidueSolvableWordPolynomial<C> Cc = ring.valueOf(c);
   return Cb.multiply(this).multiply(Cc);
 }
 /**
  * ResidueSolvableWordPolynomial multiplication. Product with 'monomial'.
  *
  * @param m 'monomial'.
  * @return this * m, where * denotes solvable multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiply(Map.Entry<ExpVector, WordResidue<C>> m) {
   if (m == null) {
     return ring.getZERO();
   }
   return multiply(m.getValue(), m.getKey());
 }
 /**
  * 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. Product with exponent vector.
  *
  * @param e exponent.
  * @return this * x<sup>e</sup>, where * denotes solvable multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiply(ExpVector e) {
   if (e == null || e.isZERO()) {
     return this;
   }
   WordResidue<C> b = ring.getONECoefficient();
   return multiply(b, e);
 }
 // cannot @Override, @NoOverride
 public ResidueSolvableWordPolynomial<C> multiply(
     ResidueSolvableWordPolynomial<C> S, ResidueSolvableWordPolynomial<C> T) {
   if (S.isZERO() || T.isZERO() || this.isZERO()) {
     return ring.getZERO();
   }
   if (S.isONE()) {
     return multiply(T);
   }
   if (T.isONE()) {
     return S.multiply(this);
   }
   return S.multiply(this).multiply(T);
 }
 /**
  * ResidueSolvableWordPolynomial multiplication. Left product with coefficient ring element.
  *
  * @param b coefficient polynomial.
  * @return b*this, where * is coefficient multiplication.
  */
 @Override
 public ResidueSolvableWordPolynomial<C> multiplyLeft(WordResidue<C> b) {
   ResidueSolvableWordPolynomial<C> Cp = ring.getZERO().copy();
   if (b == null || b.isZERO()) {
     return Cp;
   }
   Map<ExpVector, WordResidue<C>> Cm = Cp.val; // getMap();
   Map<ExpVector, WordResidue<C>> Am = val;
   WordResidue<C> c;
   for (Map.Entry<ExpVector, WordResidue<C>> y : Am.entrySet()) {
     ExpVector e = y.getKey();
     WordResidue<C> a = y.getValue();
     c = b.multiply(a);
     if (!c.isZERO()) {
       Cm.put(e, c);
     }
   }
   return Cp;
 }
 /**
  * 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;
 }
 /**
  * Constructor for ResidueSolvableWordPolynomial.
  *
  * @param r solvable polynomial ring factory.
  * @param e exponent.
  */
 public ResidueSolvableWordPolynomial(ResidueSolvableWordPolynomialRing<C> r, ExpVector e) {
   this(r);
   val.put(e, ring.getONECoefficient());
 }
  // 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;
  }