/*
 * $Id$
 */

package edu.jas.poly;


import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.log4j.Logger;

import edu.jas.kern.PreemptingException;
import edu.jas.kern.PrettyPrint;
import edu.jas.structure.NotInvertibleException;
import edu.jas.structure.RingElem;
import edu.jas.structure.UnaryFunctor;


/**
 * GenPolynomial generic polynomials implementing RingElem. n-variate ordered
 * polynomials over C. Objects of this class are intended to be immutable. The
 * implementation is based on TreeMap respectively SortedMap from exponents to
 * coefficients. Only the coefficients are modeled with generic types, the
 * exponents are fixed to ExpVector with long entries (this will eventually be
 * changed in the future). C can also be a non integral domain, e.g. a
 * ModInteger, i.e. it may contain zero divisors, since multiply() does now
 * check for zeros. Note: multiply() now checks for wrong method dispatch
 * for GenSolvablePolynomial.
 * @param  coefficient type
 * @author Heinz Kredel
 */
public class GenPolynomial> implements RingElem>, /* not yet Polynomial */
Iterable> {


    /**
     * The factory for the polynomial ring.
     */
    public final GenPolynomialRing ring;


    /**
     * The data structure for polynomials.
     */
    protected final SortedMap val; // do not change to TreeMap


    private static final Logger logger = Logger.getLogger(GenPolynomial.class);


    private final boolean debug = logger.isDebugEnabled();


    // protected GenPolynomial() { ring = null; val = null; } // don't use


    /**
     * Private constructor for GenPolynomial.
     * @param r polynomial ring factory.
     * @param t TreeMap with correct ordering.
     */
    private GenPolynomial(GenPolynomialRing r, TreeMap t) {
        ring = r;
        val = t;
        if (ring.checkPreempt) {
            if (Thread.currentThread().isInterrupted()) {
                logger.debug("throw PreemptingException");
                throw new PreemptingException();
            }
        }
    }


    /**
     * Constructor for zero GenPolynomial.
     * @param r polynomial ring factory.
     */
    public GenPolynomial(GenPolynomialRing r) {
        this(r, new TreeMap(r.tord.getDescendComparator()));
    }


    /**
     * Constructor for GenPolynomial c * xe.
     * @param r polynomial ring factory.
     * @param c coefficient.
     * @param e exponent.
     */
    public GenPolynomial(GenPolynomialRing r, C c, ExpVector e) {
        this(r);
        if (!c.isZERO()) {
            val.put(e, c);
        }
    }


    /**
     * Constructor for GenPolynomial c * x0.
     * @param r polynomial ring factory.
     * @param c coefficient.
     */
    public GenPolynomial(GenPolynomialRing r, C c) {
        this(r, c, r.evzero);
    }


    /**
     * Constructor for GenPolynomial xe.
     * @param r polynomial ring factory.
     * @param e exponent.
     */
    public GenPolynomial(GenPolynomialRing r, ExpVector e) {
        this(r, r.coFac.getONE(), e);
    }


    /**
     * Constructor for GenPolynomial.
     * @param r polynomial ring factory.
     * @param v the SortedMap of some other polynomial.
     */
    protected GenPolynomial(GenPolynomialRing r, SortedMap v) {
        this(r);
        val.putAll(v); // assume no zero coefficients
    }


    /**
     * Get the corresponding element factory.
     * @return factory for this Element.
     * @see edu.jas.structure.Element#factory()
     */
    public GenPolynomialRing factory() {
        return ring;
    }


    /**
     * Copy this GenPolynomial.
     * @return copy of this.
     */
    public GenPolynomial copy() {
        return new GenPolynomial(ring, this.val);
    }


    /**
     * Length of GenPolynomial.
     * @return number of coefficients of this GenPolynomial.
     */
    public int length() {
        return val.size();
    }


    /**
     * ExpVector to coefficient map of GenPolynomial.
     * @return val as unmodifiable SortedMap.
     */
    public SortedMap getMap() {
        // return val;
        return Collections. unmodifiableSortedMap(val);
    }


    /**
     * Put an ExpVector to coefficient entry into the internal map of this
     * GenPolynomial. Note: Do not use this method unless you are
     * constructing a new polynomial. this is modified and breaks the
     * immutability promise of this class.
     * @param c coefficient.
     * @param e exponent.
     */
    public void doPutToMap(ExpVector e, C c) {
        if (debug) {
            C a = val.get(e);
            if (a != null) {
                logger.error("map entry exists " + e + " to " + a + " new " + c);
            }
        }
        if (!c.isZERO()) {
            val.put(e, c);
        }
    }


    /**
     * Remove an ExpVector to coefficient entry from the internal map of this
     * GenPolynomial. Note: Do not use this method unless you are
     * constructing a new polynomial. this is modified and breaks the
     * immutability promise of this class.
     * @param e exponent.
     * @param c expected coefficient, null for ignore.
     */
    public void doRemoveFromMap(ExpVector e, C c) {
        C b = val.remove(e);
        if (debug) {
            if ( c == null ) { // ignore b
                return;
            }
            if ( ! c.equals(b) ) {
                logger.error("map entry wrong " + e + " to " + c + " old " + b);
            }
        }
    }


    /**
     * Put an a sorted map of exponents to coefficients into the internal map of
     * this GenPolynomial. Note: Do not use this method unless you are
     * constructing a new polynomial. this is modified and breaks the
     * immutability promise of this class.
     * @param vals sorted map of exponents and coefficients.
     */
    public void doPutToMap(SortedMap vals) {
        for (Map.Entry me : vals.entrySet()) {
            ExpVector e = me.getKey();
            if (debug) {
                C a = val.get(e);
                if (a != null) {
                    logger.error("map entry exists " + e + " to " + a + " new " + me.getValue());
                }
            }
            C c = me.getValue();
            if (!c.isZERO()) {
                val.put(e, c);
            }
        }
    }


    /**
     * String representation of GenPolynomial.
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        if (ring.vars != null) {
            return toString(ring.vars);
        }
        StringBuffer s = new StringBuffer();
        s.append(this.getClass().getSimpleName() + ":");
        s.append(ring.coFac.getClass().getSimpleName());
        if (ring.coFac.characteristic().signum() != 0) {
            s.append("(" + ring.coFac.characteristic() + ")");
        }
        s.append("[ ");
        boolean first = true;
        for (Map.Entry m : val.entrySet()) {
            if (first) {
                first = false;
            } else {
                s.append(", ");
            }
            s.append(m.getValue().toString());
            s.append(" ");
            s.append(m.getKey().toString());
        }
        s.append(" ] "); // no not use: ring.toString() );
        return s.toString();
    }


    /**
     * String representation of GenPolynomial.
     * @param v names for variables.
     * @see java.lang.Object#toString()
     */
    public String toString(String[] v) {
        StringBuffer s = new StringBuffer();
        if (PrettyPrint.isTrue()) {
            if (val.size() == 0) {
                s.append("0");
            } else {
                // s.append( "( " );
                boolean first = true;
                for (Map.Entry m : val.entrySet()) {
                    C c = m.getValue();
                    if (first) {
                        first = false;
                    } else {
                        if (c.signum() < 0) {
                            s.append(" - ");
                            c = c.negate();
                        } else {
                            s.append(" + ");
                        }
                    }
                    ExpVector e = m.getKey();
                    if (!c.isONE() || e.isZERO()) {
                        String cs = c.toString();
                        //if (c instanceof GenPolynomial || c instanceof AlgebraicNumber) {
                        if (cs.indexOf("-") >= 0 || cs.indexOf("+") >= 0) {
                            s.append("( ");
                            s.append(cs);
                            s.append(" )");
                        } else {
                            s.append(cs);
                        }
                        s.append(" ");
                    }
                    if (e != null && v != null) {
                        s.append(e.toString(v));
                    } else {
                        s.append(e);
                    }
                }
                //s.append(" )");
            }
        } else {
            s.append(this.getClass().getSimpleName() + "[ ");
            if (val.size() == 0) {
                s.append("0");
            } else {
                boolean first = true;
                for (Map.Entry m : val.entrySet()) {
                    C c = m.getValue();
                    if (first) {
                        first = false;
                    } else {
                        if (c.signum() < 0) {
                            s.append(" - ");
                            c = c.negate();
                        } else {
                            s.append(" + ");
                        }
                    }
                    ExpVector e = m.getKey();
                    if (!c.isONE() || e.isZERO()) {
                        s.append(c.toString());
                        s.append(" ");
                    }
                    s.append(e.toString(v));
                }
            }
            s.append(" ] "); // no not use: ring.toString() );
        }
        return s.toString();
    }


    /**
     * Get a scripting compatible string representation.
     * @return script compatible representation for this Element.
     * @see edu.jas.structure.Element#toScript()
     */
    @Override
    public String toScript() {
        // Python case
        if (isZERO()) {
            return "0";
        }
        StringBuffer s = new StringBuffer();
        if (val.size() > 1) {
            s.append("( ");
        }
        String[] v = ring.vars;
        if (v == null) {
            v = GenPolynomialRing.newVars("x", ring.nvar);
        }
        boolean first = true;
        for (Map.Entry m : val.entrySet()) {
            C c = m.getValue();
            if (first) {
                first = false;
            } else {
                if (c.signum() < 0) {
                    s.append(" - ");
                    c = c.negate();
                } else {
                    s.append(" + ");
                }
            }
            ExpVector e = m.getKey();
            String cs = c.toScript();
            boolean parenthesis = (cs.indexOf("-") >= 0 || cs.indexOf("+") >= 0);
            if (!c.isONE() || e.isZERO()) {
                if (parenthesis) {
                    s.append("( ");
                }
                s.append(cs);
                if (parenthesis) {
                    s.append(" )");
                }
                if (!e.isZERO()) {
                    s.append(" * ");
                }
            }
            s.append(e.toScript(v));
        }
        if (val.size() > 1) {
            s.append(" )");
        }
        return s.toString();
    }


    /**
     * Get a scripting compatible string representation of the factory.
     * @return script compatible representation for this ElemFactory.
     * @see edu.jas.structure.Element#toScriptFactory()
     */
    @Override
    public String toScriptFactory() {
        // Python case
        return factory().toScript();
    }


    /**
     * Is GenPolynomial<C> zero.
     * @return If this is 0 then true is returned, else false.
     * @see edu.jas.structure.RingElem#isZERO()
     */
    public boolean isZERO() {
        return (val.size() == 0);
    }


    /**
     * Is GenPolynomial<C> one.
     * @return If this is 1 then true is returned, else false.
     * @see edu.jas.structure.RingElem#isONE()
     */
    public boolean isONE() {
        if (val.size() != 1) {
            return false;
        }
        C c = val.get(ring.evzero);
        if (c == null) {
            return false;
        }
        return c.isONE();
    }


    /**
     * Is GenPolynomial<C> a unit.
     * @return If this is a unit then true is returned, else false.
     * @see edu.jas.structure.RingElem#isUnit()
     */
    public boolean isUnit() {
        if (val.size() != 1) {
            return false;
        }
        C c = val.get(ring.evzero);
        if (c == null) {
            return false;
        }
        return c.isUnit();
    }


    /**
     * Is GenPolynomial<C> a constant.
     * @return If this is a constant polynomial then true is returned, else
     *         false.
     */
    public boolean isConstant() {
        if (val.size() != 1) {
            return false;
        }
        C c = val.get(ring.evzero);
        if (c == null) {
            return false;
        }
        return true;
    }


    /**
     * Is GenPolynomial<C> homogeneous.
     * @return true, if this is homogeneous, else false.
     */
    public boolean isHomogeneous() {
        if (val.size() <= 1) {
            return true;
        }
        long deg = -1;
        for (ExpVector e : val.keySet()) {
            if (deg < 0) {
                deg = e.totalDeg();
            } else if (deg != e.totalDeg()) {
                return false;
            }
        }
        return true;
    }


    /**
     * Comparison with any other object.
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    @SuppressWarnings("unchecked")
    public boolean equals(Object B) {
        if (!(B instanceof GenPolynomial)) {
            return false;
        }
        GenPolynomial a = null;
        try {
            a = (GenPolynomial) B;
        } catch (ClassCastException ignored) {
        }
        if (a == null) {
            return false;
        }
        return this.compareTo(a) == 0;
    }


    /**
     * Hash code for this polynomial.
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        int h;
        h = (ring.hashCode() << 27);
        h += val.hashCode();
        return h;
    }


    /**
     * GenPolynomial comparison.
     * @param b GenPolynomial.
     * @return sign(this-b).
     */
    public int compareTo(GenPolynomial b) {
        if (b == null) {
            return 1;
        }
        SortedMap av = this.val;
        SortedMap bv = b.val;
        Iterator> ai = av.entrySet().iterator();
        Iterator> bi = bv.entrySet().iterator();
        int s = 0;
        int c = 0;
        while (ai.hasNext() && bi.hasNext()) {
            Map.Entry aie = ai.next();
            Map.Entry bie = bi.next();
            ExpVector ae = aie.getKey();
            ExpVector be = bie.getKey();
            s = ae.compareTo(be);
            //System.out.println("s = " + s + ", " + ae + ", " +be);
            if (s != 0) {
                return s;
            }
            if (c == 0) {
                C ac = aie.getValue(); //av.get(ae);
                C bc = bie.getValue(); //bv.get(be);
                c = ac.compareTo(bc);
            }
        }
        if (ai.hasNext()) {
            return 1;
        }
        if (bi.hasNext()) {
            return -1;
        }
        // now all keys are equal
        return c;
    }


    /**
     * GenPolynomial signum.
     * @return sign(ldcf(this)).
     */
    public int signum() {
        if (this.isZERO()) {
            return 0;
        }
        ExpVector t = val.firstKey();
        C c = val.get(t);
        return c.signum();
    }


    /**
     * Number of variables.
     * @return ring.nvar.
     */
    public int numberOfVariables() {
        return ring.nvar;
    }


    /**
     * Leading monomial.
     * @return first map entry.
     */
    public Map.Entry leadingMonomial() {
        if (val.size() == 0)
            return null;
        Iterator> ai = val.entrySet().iterator();
        return ai.next();
    }


    /**
     * Leading exponent vector.
     * @return first exponent.
     */
    public ExpVector leadingExpVector() {
        if (val.size() == 0) {
            return null; // ring.evzero? needs many changes 
        }
        return val.firstKey();
    }


    /**
     * Trailing exponent vector.
     * @return last exponent.
     */
    public ExpVector trailingExpVector() {
        if (val.size() == 0) {
            return ring.evzero; // or null ?;
        }
        return val.lastKey();
    }


    /**
     * Leading base coefficient.
     * @return first coefficient.
     */
    public C leadingBaseCoefficient() {
        if (val.size() == 0) {
            return ring.coFac.getZERO();
        }
        return val.get(val.firstKey());
    }


    /**
     * Trailing base coefficient.
     * @return coefficient of constant term.
     */
    public C trailingBaseCoefficient() {
        C c = val.get(ring.evzero);
        if (c == null) {
            return ring.coFac.getZERO();
        }
        return c;
    }


    /**
     * Coefficient.
     * @param e exponent.
     * @return coefficient for given exponent.
     */
    public C coefficient(ExpVector e) {
        C c = val.get(e);
        if (c == null) {
            c = ring.coFac.getZERO();
        }
        return c;
    }


    /**
     * Reductum.
     * @return this - leading monomial.
     */
    public GenPolynomial reductum() {
        if (val.size() <= 1) {
            return ring.getZERO();
        }
        Iterator ai = val.keySet().iterator();
        ExpVector lt = ai.next();
        lt = ai.next(); // size > 1
        SortedMap red = val.tailMap(lt);
        GenPolynomial r = ring.getZERO().copy();
        r.doPutToMap(red); //  new GenPolynomial(ring, red);
        return r;
    }


    /**
     * Degree in variable i.
     * @return maximal degree in the variable i.
     */
    public long degree(int i) {
        if (val.size() == 0) {
            return 0; // 0 or -1 ?;
        }
        int j;
        if (i >= 0) {
            j = ring.nvar - 1 - i;
        } else { // python like -1 means main variable
            j = ring.nvar + i;
        }
        long deg = 0;
        for (ExpVector e : val.keySet()) {
            long d = e.getVal(j);
            if (d > deg) {
                deg = d;
            }
        }
        return deg;
    }


    /**
     * Maximal degree.
     * @return maximal degree in any variables.
     */
    public long degree() {
        if (val.size() == 0) {
            return 0; // 0 or -1 ?;
        }
        long deg = 0;
        for (ExpVector e : val.keySet()) {
            long d = e.maxDeg();
            if (d > deg) {
                deg = d;
            }
        }
        return deg;
    }


    /**
     * Total degree.
     * @return total degree in any variables.
     */
    public long totalDegree() {
        if (val.size() == 0) {
            return 0; // 0 or -1 ?;
        }
        long deg = 0;
        for (ExpVector e : val.keySet()) {
            long d = e.totalDeg();
            if (d > deg) {
                deg = d;
            }
        }
        return deg;
    }


    /**
     * Maximal degree vector.
     * @return maximal degree vector of all variables.
     */
    public ExpVector degreeVector() {
        ExpVector deg = ring.evzero;
        if (val.size() == 0) {
            return deg;
        }
        for (ExpVector e : val.keySet()) {
            deg = deg.lcm(e);
        }
        return deg;
    }


    /**
     * GenPolynomial maximum norm.
     * @return ||this||.
     */
    public C maxNorm() {
        C n = ring.getZEROCoefficient();
        for (C c : val.values()) {
            C x = c.abs();
            if (n.compareTo(x) < 0) {
                n = x;
            }
        }
        return n;
    }


    /**
     * GenPolynomial sum norm.
     * @return sum of all absolute values of coefficients.
     */
    public C sumNorm() {
        C n = ring.getZEROCoefficient();
        for (C c : val.values()) {
            C x = c.abs();
            n = n.sum(x);
        }
        return n;
    }


    /**
     * GenPolynomial summation.
     * @param S GenPolynomial.
     * @return this+S.
     */
    //public > T sum(T /*GenPolynomial*/ S) {
    public GenPolynomial sum(GenPolynomial S) {
        if (S == null) {
            return this;
        }
        if (S.isZERO()) {
            return this;
        }
        if (this.isZERO()) {
            return S;
        }
        assert (ring.nvar == S.ring.nvar);
        GenPolynomial n = this.copy(); //new GenPolynomial(ring, val); 
        SortedMap nv = n.val;
        SortedMap sv = S.val;
        for (Map.Entry me : sv.entrySet()) {
            ExpVector e = me.getKey();
            C y = me.getValue(); //sv.get(e); // assert y != null
            C x = nv.get(e);
            if (x != null) {
                x = x.sum(y);
                if (!x.isZERO()) {
                    nv.put(e, x);
                } else {
                    nv.remove(e);
                }
            } else {
                nv.put(e, y);
            }
        }
        return n;
    }


    /**
     * GenPolynomial addition. This method is not very efficient, since this is
     * copied.
     * @param a coefficient.
     * @param e exponent.
     * @return this + a xe.
     */
    public GenPolynomial sum(C a, ExpVector e) {
        if (a == null) {
            return this;
        }
        if (a.isZERO()) {
            return this;
        }
        GenPolynomial n = this.copy(); //new GenPolynomial(ring, val); 
        SortedMap nv = n.val;
        //if ( nv.size() == 0 ) { nv.put(e,a); return n; }
        C x = nv.get(e);
        if (x != null) {
            x = x.sum(a);
            if (!x.isZERO()) {
                nv.put(e, x);
            } else {
                nv.remove(e);
            }
        } else {
            nv.put(e, a);
        }
        return n;
    }


    /**
     * GenPolynomial addition. This method is not very efficient, since this is
     * copied.
     * @param a coefficient.
     * @return this + a x0.
     */
    public GenPolynomial sum(C a) {
        return sum(a, ring.evzero);
    }


    /**
     * GenPolynomial subtraction.
     * @param S GenPolynomial.
     * @return this-S.
     */
    public GenPolynomial subtract(GenPolynomial S) {
        if (S == null) {
            return this;
        }
        if (S.isZERO()) {
            return this;
        }
        if (this.isZERO()) {
            return S.negate();
        }
        assert (ring.nvar == S.ring.nvar);
        GenPolynomial n = this.copy(); //new GenPolynomial(ring, val); 
        SortedMap nv = n.val;
        SortedMap sv = S.val;
        for (Map.Entry me : sv.entrySet()) {
            ExpVector e = me.getKey();
            C y = me.getValue(); //sv.get(e); // assert y != null
            C x = nv.get(e);
            if (x != null) {
                x = x.subtract(y);
                if (!x.isZERO()) {
                    nv.put(e, x);
                } else {
                    nv.remove(e);
                }
            } else {
                nv.put(e, y.negate());
            }
        }
        return n;
    }


    /**
     * GenPolynomial subtraction. This method is not very efficient, since this
     * is copied.
     * @param a coefficient.
     * @param e exponent.
     * @return this - a xe.
     */
    public GenPolynomial subtract(C a, ExpVector e) {
        if (a == null) {
            return this;
        }
        if (a.isZERO()) {
            return this;
        }
        GenPolynomial n = this.copy(); 
        SortedMap nv = n.val;
        C x = nv.get(e);
        if (x != null) {
            x = x.subtract(a);
            if (!x.isZERO()) {
                nv.put(e, x);
            } else {
                nv.remove(e);
            }
        } else {
            nv.put(e, a.negate());
        }
        return n;
    }


    /**
     * GenPolynomial subtract. This method is not very efficient, since this is
     * copied.
     * @param a coefficient.
     * @return this + a x0.
     */
    public GenPolynomial subtract(C a) {
        return subtract(a, ring.evzero);
    }


    /**
     * GenPolynomial subtract a multiple.
     * @param a coefficient.
     * @param S GenPolynomial.
     * @return this - a xe S.
     */
    public GenPolynomial subtractMultiple(C a, GenPolynomial S) {
        if (a == null) {
            return this;
        }
        if (a.isZERO()) {
            return this;
        }
        if (S == null) {
            return this;
        }
        if (S.isZERO()) {
            return this;
        }
        if (this.isZERO()) {
            return S.multiply(a.negate());
        }
        assert (ring.nvar == S.ring.nvar);
        GenPolynomial n = this.copy(); 
        SortedMap nv = n.val;
        SortedMap sv = S.val;
        for (Map.Entry me : sv.entrySet()) {
            ExpVector f = me.getKey();
            C y = me.getValue(); // assert y != null
            y = a.multiply(y);
            C x = nv.get(f);
            if (x != null) {
                x = x.subtract(y);
                if (!x.isZERO()) {
                    nv.put(f, x);
                } else {
                    nv.remove(f);
                }
            } else if ( !y.isZERO() ) {
                nv.put(f, y.negate());
            }
        }
        return n;
    }


    /**
     * GenPolynomial subtract a multiple.
     * @param a coefficient.
     * @param e exponent.
     * @param S GenPolynomial.
     * @return this - a xe S.
     */
    public GenPolynomial subtractMultiple(C a, ExpVector e, GenPolynomial S) {
        if (a == null) {
            return this;
        }
        if (a.isZERO()) {
            return this;
        }
        if (S == null) {
            return this;
        }
        if (S.isZERO()) {
            return this;
        }
        if (this.isZERO()) {
            return S.multiply(a.negate(),e);
        }
        assert (ring.nvar == S.ring.nvar);
        GenPolynomial n = this.copy(); 
        SortedMap nv = n.val;
        SortedMap sv = S.val;
        for (Map.Entry me : sv.entrySet()) {
            ExpVector f = me.getKey();
            f = e.sum(f);
            C y = me.getValue(); // assert y != null
            y = a.multiply(y);
            C x = nv.get(f);
            if (x != null) {
                x = x.subtract(y);
                if (!x.isZERO()) {
                    nv.put(f, x);
                } else {
                    nv.remove(f);
                }
            } else if ( !y.isZERO() ) {
                nv.put(f, y.negate());
            }
        }
        return n;
    }


    /**
     * GenPolynomial scale and subtract a multiple.
     * @param b scale factor.
     * @param a coefficient.
     * @param e exponent.
     * @param S GenPolynomial.
     * @return this * b - a xe S.
     */
    public GenPolynomial scaleSubtractMultiple(C b, C a, ExpVector e, GenPolynomial S) {
        if (a == null || S == null) {
            return this.multiply(b);
        }
        if (a.isZERO() || S.isZERO()) {
            return this.multiply(b);
        }
        if (this.isZERO() || b == null || b.isZERO()) {
            return S.multiply(a.negate(),e);
        }
        if (b.isONE()) {
            return subtractMultiple(a,e,S);
        }
        assert (ring.nvar == S.ring.nvar);
        GenPolynomial n = this.multiply(b);
        SortedMap nv = n.val;
        SortedMap sv = S.val;
        for (Map.Entry me : sv.entrySet()) {
            ExpVector f = me.getKey();
            f = e.sum(f);
            C y = me.getValue(); // assert y != null
            y = a.multiply(y); // now y can be zero
            C x = nv.get(f);
            if (x != null) {
                x = x.subtract(y);
                if (!x.isZERO()) {
                    nv.put(f, x);
                } else {
                    nv.remove(f);
                }
            } else if ( !y.isZERO() ) {
                nv.put(f, y.negate());
            }
        }
        return n;
    }


    /**
     * GenPolynomial scale and subtract a multiple.
     * @param b scale factor.
     * @param g scale exponent.
     * @param a coefficient.
     * @param e exponent.
     * @param S GenPolynomial.
     * @return this * a xg - a xe S.
     */
    public GenPolynomial scaleSubtractMultiple(C b, ExpVector g, C a, ExpVector e, GenPolynomial S) {
        if (a == null || S == null) {
            return this.multiply(b,g);
        }
        if (a.isZERO() || S.isZERO()) {
            return this.multiply(b,g);
        }
        if (this.isZERO() || b == null || b.isZERO()) {
            return S.multiply(a.negate(),e);
        }
        if (b.isONE() && g.isZERO()) {
            return subtractMultiple(a,e,S);
        }
        assert (ring.nvar == S.ring.nvar);
        GenPolynomial n = this.multiply(b,g);
        SortedMap nv = n.val;
        SortedMap sv = S.val;
        for (Map.Entry me : sv.entrySet()) {
            ExpVector f = me.getKey();
            f = e.sum(f);
            C y = me.getValue(); // assert y != null
            y = a.multiply(y); // y can be zero now
            C x = nv.get(f);
            if (x != null) {
                x = x.subtract(y);
                if (!x.isZERO()) {
                    nv.put(f, x);
                } else {
                    nv.remove(f);
                }
            } else if ( !y.isZERO() ) {
                nv.put(f, y.negate());
            }
        }
        return n;
    }


    /**
     * GenPolynomial negation.
     * @return -this.
     */
    public GenPolynomial negate() {
        GenPolynomial n = ring.getZERO().copy();
        //new GenPolynomial(ring, ring.getZERO().val);
        SortedMap v = n.val;
        for (Map.Entry m : val.entrySet()) {
            C x = m.getValue(); // != null, 0
            v.put(m.getKey(), x.negate());
            // or m.setValue( x.negate() ) if this cloned 
        }
        return n;
    }


    /**
     * GenPolynomial absolute value, i.e. leadingCoefficient > 0.
     * @return abs(this).
     */
    public GenPolynomial abs() {
        if (leadingBaseCoefficient().signum() < 0) {
            return this.negate();
        }
        return this;
    }


    /**
     * GenPolynomial multiplication.
     * @param S GenPolynomial.
     * @return this*S.
     */
    public GenPolynomial multiply(GenPolynomial S) {
        if (S == null) {
            return ring.getZERO();
        }
        if (S.isZERO()) {
            return ring.getZERO();
        }
        if (this.isZERO()) {
            return this;
        }
        assert (ring.nvar == S.ring.nvar);
        if (this instanceof GenSolvablePolynomial || S instanceof GenSolvablePolynomial) {
            //throw new RuntimeException("wrong method dispatch in JRE ");
            logger.debug("warn: wrong method dispatch in JRE multiply(S) - trying to fix");
            GenSolvablePolynomial T = (GenSolvablePolynomial) this;
            GenSolvablePolynomial Sp = (GenSolvablePolynomial) S;
            return T.multiply(Sp);
        }
        GenPolynomial p = ring.getZERO().copy();
        SortedMap pv = p.val;
        for (Map.Entry m1 : val.entrySet()) {
            C c1 = m1.getValue();
            ExpVector e1 = m1.getKey();
            for (Map.Entry m2 : S.val.entrySet()) {
                C c2 = m2.getValue();
                ExpVector e2 = m2.getKey();
                C c = c1.multiply(c2); // check non zero if not domain
                if (!c.isZERO()) {
                    ExpVector e = e1.sum(e2);
                    C c0 = pv.get(e);
                    if (c0 == null) {
                        pv.put(e, c);
                    } else {
                        c0 = c0.sum(c);
                        if (!c0.isZERO()) {
                            pv.put(e, c0);
                        } else {
                            pv.remove(e);
                        }
                    }
                }
            }
        }
        return p;
    }


    /**
     * GenPolynomial multiplication. Product with coefficient ring element.
     * @param s coefficient.
     * @return this*s.
     */
    public GenPolynomial multiply(C s) {
        if (s == null) {
            return ring.getZERO();
        }
        if (s.isZERO()) {
            return ring.getZERO();
        }
        if (this.isZERO()) {
            return this;
        }
        GenPolynomial p = ring.getZERO().copy();
        SortedMap pv = p.val;
        for (Map.Entry m1 : val.entrySet()) {
            C c1 = m1.getValue();
            ExpVector e1 = m1.getKey();
            C c = c1.multiply(s); // check non zero if not domain
            if (!c.isZERO()) {
                pv.put(e1, c); // or m1.setValue( c )
            }
        }
        return p;
    }


    /**
     * GenPolynomial monic, i.e. leadingCoefficient == 1. If leadingCoefficient
     * is not invertible returns this unmodified.
     * @return monic(this).
     */
    public GenPolynomial monic() {
        if (this.isZERO()) {
            return this;
        }
        C lc = leadingBaseCoefficient();
        if (!lc.isUnit()) {
            //System.out.println("lc = "+lc);
            return this;
        }
        C lm = lc.inverse();
        return multiply(lm);
    }


    /**
     * GenPolynomial multiplication. Product with ring element and exponent
     * vector.
     * @param s coefficient.
     * @param e exponent.
     * @return this * s xe.
     */
    public GenPolynomial multiply(C s, ExpVector e) {
        if (s == null) {
            return ring.getZERO();
        }
        if (s.isZERO()) {
            return ring.getZERO();
        }
        if (this.isZERO()) {
            return this;
        }
        if (this instanceof GenSolvablePolynomial) {
            //throw new RuntimeException("wrong method dispatch in JRE ");
            logger.debug("warn: wrong method dispatch in JRE multiply(s,e) - trying to fix");
            GenSolvablePolynomial T = (GenSolvablePolynomial) this;
            return T.multiply(s, e);
        }
        GenPolynomial p = ring.getZERO().copy();
        SortedMap pv = p.val;
        for (Map.Entry m1 : val.entrySet()) {
            C c1 = m1.getValue();
            ExpVector e1 = m1.getKey();
            C c = c1.multiply(s); // check non zero if not domain
            if (!c.isZERO()) {
                ExpVector e2 = e1.sum(e);
                pv.put(e2, c);
            }
        }
        return p;
    }


    /**
     * GenPolynomial multiplication. Product with exponent vector.
     * @param e exponent (!= null).
     * @return this * xe.
     */
    public GenPolynomial multiply(ExpVector e) {
        // assert e != null. This is never allowed.
        if (this.isZERO()) {
            return this;
        }
        if (this instanceof GenSolvablePolynomial) {
            //throw new RuntimeException("wrong method dispatch in JRE ");
            logger.debug("warn: wrong method dispatch in JRE multiply(e) - trying to fix");
            GenSolvablePolynomial T = (GenSolvablePolynomial) this;
            return T.multiply(e);
        }
        GenPolynomial p = ring.getZERO().copy();
        SortedMap pv = p.val;
        for (Map.Entry m1 : val.entrySet()) {
            C c1 = m1.getValue();
            ExpVector e1 = m1.getKey();
            ExpVector e2 = e1.sum(e);
            pv.put(e2, c1);
        }
        return p;
    }


    /**
     * GenPolynomial multiplication. Product with 'monomial'.
     * @param m 'monomial'.
     * @return this * m.
     */
    public GenPolynomial multiply(Map.Entry m) {
        if (m == null) {
            return ring.getZERO();
        }
        return multiply(m.getValue(), m.getKey());
    }


    /**
     * GenPolynomial division. Division by coefficient ring element. Fails, if
     * exact division is not possible.
     * @param s coefficient.
     * @return this/s.
     */
    public GenPolynomial divide(C s) {
        if (s == null || s.isZERO()) {
            throw new ArithmeticException("division by zero");
        }
        if (this.isZERO()) {
            return this;
        }
        //C t = s.inverse();
        //return multiply(t);
        GenPolynomial p = ring.getZERO().copy();
        SortedMap pv = p.val;
        for (Map.Entry m : val.entrySet()) {
            ExpVector e = m.getKey();
            C c1 = m.getValue();
            C c = c1.divide(s);
            if (debug) {
                C x = c1.remainder(s);
                if (!x.isZERO()) {
                    logger.info("divide x = " + x);
                    throw new ArithmeticException("no exact division: " + c1 + "/" + s);
                }
            }
            if (c.isZERO()) {
                throw new ArithmeticException("no exact division: " + c1 + "/" + s + ", in " + this);
            }
            pv.put(e, c); // or m1.setValue( c )
        }
        return p;
    }


    /**
     * GenPolynomial division with remainder. Fails, if exact division by
     * leading base coefficient is not possible. Meaningful only for univariate
     * polynomials over fields, but works in any case.
     * @param S nonzero GenPolynomial with invertible leading coefficient.
     * @return [ quotient , remainder ] with this = quotient * S + remainder and
     *         deg(remainder) < deg(S) or remiander = 0.
     * @see edu.jas.poly.PolyUtil#baseSparsePseudoRemainder(edu.jas.poly.GenPolynomial,edu.jas.poly.GenPolynomial)
     *      .
     */
    @SuppressWarnings("unchecked")
    public GenPolynomial[] quotientRemainder(GenPolynomial S) {
        if (S == null || S.isZERO()) {
            throw new ArithmeticException("division by zero");
        }
        C c = S.leadingBaseCoefficient();
        if (!c.isUnit()) {
            throw new ArithmeticException("lbcf not invertible " + c);
        }
        C ci = c.inverse();
        assert (ring.nvar == S.ring.nvar);
        ExpVector e = S.leadingExpVector();
        GenPolynomial h;
        GenPolynomial q = ring.getZERO().copy();
        GenPolynomial r = this.copy();
        while (!r.isZERO()) {
            ExpVector f = r.leadingExpVector();
            if (f.multipleOf(e)) {
                C a = r.leadingBaseCoefficient();
                f = f.subtract(e);
                a = a.multiply(ci);
                q = q.sum(a, f);
                h = S.multiply(a, f);
                r = r.subtract(h);
            } else {
                break;
            }
        }
        GenPolynomial[] ret = new GenPolynomial[2];
        ret[0] = q;
        ret[1] = r;
        return ret;
    }


    /**
     * GenPolynomial division with remainder. Fails, if exact division by
     * leading base coefficient is not possible. Meaningful only for univariate
     * polynomials over fields, but works in any case.
     * @param S nonzero GenPolynomial with invertible leading coefficient.
     * @return [ quotient , remainder ] with this = quotient * S + remainder and
     *         deg(remainder) < deg(S) or remiander = 0.
     * @see edu.jas.poly.PolyUtil#baseSparsePseudoRemainder(edu.jas.poly.GenPolynomial,edu.jas.poly.GenPolynomial)
     *      .
     * @deprecated use quotientRemainder()
     */
    @Deprecated
    public GenPolynomial[] divideAndRemainder(GenPolynomial S) {
        return quotientRemainder(S);
    }


    /**
     * GenPolynomial division. Fails, if exact division by leading base
     * coefficient is not possible. Meaningful only for univariate polynomials
     * over fields, but works in any case.
     * @param S nonzero GenPolynomial with invertible leading coefficient.
     * @return quotient with this = quotient * S + remainder.
     * @see edu.jas.poly.PolyUtil#baseSparsePseudoRemainder(edu.jas.poly.GenPolynomial,edu.jas.poly.GenPolynomial)
     *      .
     */
    public GenPolynomial divide(GenPolynomial S) {
        if (this instanceof GenSolvablePolynomial || S instanceof GenSolvablePolynomial) {
            //throw new RuntimeException("wrong method dispatch in JRE ");
            //logger.debug("warn: wrong method dispatch in JRE multiply(S) - trying to fix");
            GenSolvablePolynomial T = (GenSolvablePolynomial) this;
            GenSolvablePolynomial Sp = (GenSolvablePolynomial) S;
            return T.quotientRemainder(Sp)[0];
        }
        return quotientRemainder(S)[0];
    }


    /**
     * GenPolynomial remainder. Fails, if exact division by leading base
     * coefficient is not possible. Meaningful only for univariate polynomials
     * over fields, but works in any case.
     * @param S nonzero GenPolynomial with invertible leading coefficient.
     * @return remainder with this = quotient * S + remainder.
     * @see edu.jas.poly.PolyUtil#baseSparsePseudoRemainder(edu.jas.poly.GenPolynomial,edu.jas.poly.GenPolynomial)
     *      .
     */
    public GenPolynomial remainder(GenPolynomial S) {
        if (this instanceof GenSolvablePolynomial || S instanceof GenSolvablePolynomial) {
            //throw new RuntimeException("wrong method dispatch in JRE ");
            //logger.debug("warn: wrong method dispatch in JRE multiply(S) - trying to fix");
            GenSolvablePolynomial T = (GenSolvablePolynomial) this;
            GenSolvablePolynomial Sp = (GenSolvablePolynomial) S;
            return T.quotientRemainder(Sp)[1];
        }
        if (S == null || S.isZERO()) {
            throw new ArithmeticException("division by zero");
        }
        C c = S.leadingBaseCoefficient();
        if (!c.isUnit()) {
            throw new ArithmeticException("lbc not invertible " + c);
        }
        C ci = c.inverse();
        assert (ring.nvar == S.ring.nvar);
        ExpVector e = S.leadingExpVector();
        GenPolynomial h;
        GenPolynomial r = this.copy();
        while (!r.isZERO()) {
            ExpVector f = r.leadingExpVector();
            if (f.multipleOf(e)) {
                C a = r.leadingBaseCoefficient();
                f = f.subtract(e);
                //logger.info("red div = " + e);
                a = a.multiply(ci);
                h = S.multiply(a, f);
                r = r.subtract(h);
            } else {
                break;
            }
        }
        return r;
    }


    /**
     * GenPolynomial greatest common divisor. Only for univariate polynomials
     * over fields.
     * @param S GenPolynomial.
     * @return gcd(this,S).
     */
    public GenPolynomial gcd(GenPolynomial S) {
        if (S == null || S.isZERO()) {
            return this;
        }
        if (this.isZERO()) {
            return S;
        }
        if (ring.nvar != 1) {
            throw new IllegalArgumentException("not univariate polynomials" + ring);
        }
        GenPolynomial x;
        GenPolynomial q = this;
        GenPolynomial r = S;
        while (!r.isZERO()) {
            x = q.remainder(r);
            q = r;
            r = x;
        }
        return q.monic(); // normalize
    }


    /**
     * GenPolynomial extended greatest comon divisor. Only for univariate
     * polynomials over fields.
     * @param S GenPolynomial.
     * @return [ gcd(this,S), a, b ] with a*this + b*S = gcd(this,S).
     */
    @SuppressWarnings("unchecked")
    public GenPolynomial[] egcd(GenPolynomial S) {
        GenPolynomial[] ret = new GenPolynomial[3];
        ret[0] = null;
        ret[1] = null;
        ret[2] = null;
        if (S == null || S.isZERO()) {
            ret[0] = this;
            ret[1] = this.ring.getONE();
            ret[2] = this.ring.getZERO();
            return ret;
        }
        if (this.isZERO()) {
            ret[0] = S;
            ret[1] = this.ring.getZERO();
            ret[2] = this.ring.getONE();
            return ret;
        }
        if (ring.nvar != 1) {
            throw new IllegalArgumentException(this.getClass().getName() + " not univariate polynomials"
                            + ring);
        }
        if (this.isConstant() && S.isConstant()) {
            C t = this.leadingBaseCoefficient();
            C s = S.leadingBaseCoefficient();
            C[] gg = t.egcd(s);
            //System.out.println("coeff gcd = " + Arrays.toString(gg));
            GenPolynomial z = this.ring.getZERO();
            ret[0] = z.sum(gg[0]);
            ret[1] = z.sum(gg[1]);
            ret[2] = z.sum(gg[2]);
            return ret;
        }
        GenPolynomial[] qr;
        GenPolynomial q = this;
        GenPolynomial r = S;
        GenPolynomial c1 = ring.getONE().copy();
        GenPolynomial d1 = ring.getZERO().copy();
        GenPolynomial c2 = ring.getZERO().copy();
        GenPolynomial d2 = ring.getONE().copy();
        GenPolynomial x1;
        GenPolynomial x2;
        while (!r.isZERO()) {
            qr = q.quotientRemainder(r);
            q = qr[0];
            x1 = c1.subtract(q.multiply(d1));
            x2 = c2.subtract(q.multiply(d2));
            c1 = d1;
            c2 = d2;
            d1 = x1;
            d2 = x2;
            q = r;
            r = qr[1];
        }
        // normalize ldcf(q) to 1, i.e. make monic
        C g = q.leadingBaseCoefficient();
        if (g.isUnit()) {
            C h = g.inverse();
            q = q.multiply(h);
            c1 = c1.multiply(h);
            c2 = c2.multiply(h);
        }
        //assert ( ((c1.multiply(this)).sum( c2.multiply(S)).equals(q) )); 
        ret[0] = q;
        ret[1] = c1;
        ret[2] = c2;
        return ret;
    }


    /**
     * GenPolynomial half extended greatest comon divisor. Only for univariate
     * polynomials over fields.
     * @param S GenPolynomial.
     * @return [ gcd(this,S), a ] with a*this + b*S = gcd(this,S).
     */
    @SuppressWarnings("unchecked")
    public GenPolynomial[] hegcd(GenPolynomial S) {
        GenPolynomial[] ret = new GenPolynomial[2];
        ret[0] = null;
        ret[1] = null;
        if (S == null || S.isZERO()) {
            ret[0] = this;
            ret[1] = this.ring.getONE();
            return ret;
        }
        if (this.isZERO()) {
            ret[0] = S;
            return ret;
        }
        if (ring.nvar != 1) {
            throw new IllegalArgumentException(this.getClass().getName() + " not univariate polynomials"
                            + ring);
        }
        GenPolynomial[] qr;
        GenPolynomial q = this;
        GenPolynomial r = S;
        GenPolynomial c1 = ring.getONE().copy();
        GenPolynomial d1 = ring.getZERO().copy();
        GenPolynomial x1;
        while (!r.isZERO()) {
            qr = q.quotientRemainder(r);
            q = qr[0];
            x1 = c1.subtract(q.multiply(d1));
            c1 = d1;
            d1 = x1;
            q = r;
            r = qr[1];
        }
        // normalize ldcf(q) to 1, i.e. make monic
        C g = q.leadingBaseCoefficient();
        if (g.isUnit()) {
            C h = g.inverse();
            q = q.multiply(h);
            c1 = c1.multiply(h);
        }
        //assert ( ((c1.multiply(this)).remainder(S).equals(q) )); 
        ret[0] = q;
        ret[1] = c1;
        return ret;
    }


    /**
     * GenPolynomial inverse. Required by RingElem. Throws not invertible
     * exception.
     */
    public GenPolynomial inverse() {
        if (isUnit()) { // only possible if ldbcf is unit
            C c = leadingBaseCoefficient().inverse();
            return ring.getONE().multiply(c);
        }
        throw new NotInvertibleException("element not invertible " + this + " :: " + ring);
    }


    /**
     * GenPolynomial modular inverse. Only for univariate polynomials over
     * fields.
     * @param m GenPolynomial.
     * @return a with with a*this = 1 mod m.
     */
    public GenPolynomial modInverse(GenPolynomial m) {
        if (this.isZERO()) {
            throw new NotInvertibleException("zero is not invertible");
        }
        GenPolynomial[] hegcd = this.hegcd(m);
        GenPolynomial a = hegcd[0];
        if (!a.isUnit()) { // gcd != 1
            throw new AlgebraicNotInvertibleException("element not invertible, gcd != 1", m, a, m.divide(a));
        }
        GenPolynomial b = hegcd[1];
        if (b.isZERO()) { // when m divides this, e.g. m.isUnit()
            throw new NotInvertibleException("element not invertible, divisible by modul");
        }
        return b;
    }


    /**
     * Extend variables. Used e.g. in module embedding. Extend all ExpVectors by
     * i elements and multiply by x_j^k.
     * @param pfac extended polynomial ring factory (by i variables).
     * @param j index of variable to be used for multiplication.
     * @param k exponent for x_j.
     * @return extended polynomial.
     */
    public GenPolynomial extend(GenPolynomialRing pfac, int j, long k) {
        if (ring.equals(pfac)) { // nothing to do
            return this;
        }
        GenPolynomial Cp = pfac.getZERO().copy();
        if (this.isZERO()) {
            return Cp;
        }
        int i = pfac.nvar - ring.nvar;
        Map C = Cp.val; //getMap();
        Map A = val;
        for (Map.Entry y : A.entrySet()) {
            ExpVector e = y.getKey();
            C a = y.getValue();
            ExpVector f = e.extend(i, j, k);
            C.put(f, a);
        }
        return Cp;
    }


    /**
     * Extend lower variables. Used e.g. in module embedding. Extend all
     * ExpVectors by i lower elements and multiply by x_j^k.
     * @param pfac extended polynomial ring factory (by i variables).
     * @param j index of variable to be used for multiplication.
     * @param k exponent for x_j.
     * @return extended polynomial.
     */
    public GenPolynomial extendLower(GenPolynomialRing pfac, int j, long k) {
        if (ring.equals(pfac)) { // nothing to do
            return this;
        }
        GenPolynomial Cp = pfac.getZERO().copy();
        if (this.isZERO()) {
            return Cp;
        }
        int i = pfac.nvar - ring.nvar;
        Map C = Cp.val; //getMap();
        Map A = val;
        for (Map.Entry y : A.entrySet()) {
            ExpVector e = y.getKey();
            C a = y.getValue();
            ExpVector f = e.extendLower(i, j, k);
            C.put(f, a);
        }
        return Cp;
    }


    /**
     * Contract variables. Used e.g. in module embedding. Remove i elements of
     * each ExpVector.
     * @param pfac contracted polynomial ring factory (by i variables).
     * @return Map of exponents and contracted polynomials. Note: could
     *         return SortedMap
     */
    public Map> contract(GenPolynomialRing pfac) {
        GenPolynomial zero = pfac.getZERO(); //not pfac.coFac;
        TermOrder t = new TermOrder(TermOrder.INVLEX);
        Map> B = new TreeMap>(t.getAscendComparator());
        if (this.isZERO()) {
            return B;
        }
        int i = ring.nvar - pfac.nvar;
        Map A = val;
        for (Map.Entry y : A.entrySet()) {
            ExpVector e = y.getKey();
            C a = y.getValue();
            ExpVector f = e.contract(0, i);
            ExpVector g = e.contract(i, e.length() - i);
            GenPolynomial p = B.get(f);
            if (p == null) {
                p = zero;
            }
            p = p.sum(a, g);
            B.put(f, p);
        }
        return B;
    }


    /**
     * Contract variables to coefficient polynomial. Remove i elements of each
     * ExpVector, removed elements must be zero.
     * @param pfac contracted polynomial ring factory (by i variables).
     * @return contracted coefficient polynomial.
     */
    public GenPolynomial contractCoeff(GenPolynomialRing pfac) {
        Map> ms = contract(pfac);
        GenPolynomial c = pfac.getZERO();
        for (Map.Entry> m : ms.entrySet()) {
            if (m.getKey().isZERO()) {
                c = m.getValue();
            } else {
                throw new RuntimeException("wrong coefficient contraction " + m + ", pol =  " + c);
            }
        }
        return c;
    }


    /**
     * Extend univariate to multivariate polynomial. This is an univariate
     * polynomial in variable i of the polynomial ring, it is extended to the
     * given polynomial ring.
     * @param pfac extended polynomial ring factory.
     * @param i index of the variable of this polynomial in pfac.
     * @return extended multivariate polynomial.
     */
    public GenPolynomial extendUnivariate(GenPolynomialRing pfac, int i) {
        if (i < 0 || pfac.nvar < i) {
            throw new IllegalArgumentException("index " + i + "out of range " + pfac.nvar);
        }
        if (ring.nvar != 1) {
            throw new IllegalArgumentException("polynomial not univariate " + ring.nvar);
        }
        if (this.isONE()) {
            return pfac.getONE();
        }
        int j = pfac.nvar - 1 - i;
        GenPolynomial Cp = pfac.getZERO().copy();
        if (this.isZERO()) {
            return Cp;
        }
        Map C = Cp.val; //getMap();
        Map A = val;
        for (Map.Entry y : A.entrySet()) {
            ExpVector e = y.getKey();
            long n = e.getVal(0);
            C a = y.getValue();
            ExpVector f = ExpVector.create(pfac.nvar, j, n);
            C.put(f, a); // assert not contained
        }
        return Cp;
    }


    /**
     * Make homogeneous.
     * @param pfac extended polynomial ring factory (by 1 variable).
     * @return homogeneous polynomial.
     */
    public GenPolynomial homogenize(GenPolynomialRing pfac) {
        if (ring.equals(pfac)) { // not implemented
            throw new UnsupportedOperationException("case with same ring not implemented");
        }
        GenPolynomial Cp = pfac.getZERO().copy();
        if (this.isZERO()) {
            return Cp;
        }
        long deg = totalDegree();
        //int i = pfac.nvar - ring.nvar;
        Map C = Cp.val; //getMap();
        Map A = val;
        for (Map.Entry y : A.entrySet()) {
            ExpVector e = y.getKey();
            C a = y.getValue();
            long d = deg - e.totalDeg();
            ExpVector f = e.extend(1, 0, d);
            C.put(f, a);
        }
        return Cp;
    }


    /**
     * Dehomogenize.
     * @param pfac contracted polynomial ring factory (by 1 variable).
     * @return in homogeneous polynomial.
     */
    public GenPolynomial deHomogenize(GenPolynomialRing pfac) {
        if (ring.equals(pfac)) { // not implemented
            throw new UnsupportedOperationException("case with same ring not implemented");
        }
        GenPolynomial Cp = pfac.getZERO().copy();
        if (this.isZERO()) {
            return Cp;
        }
        Map C = Cp.val; //getMap();
        Map A = val;
        for (Map.Entry y : A.entrySet()) {
            ExpVector e = y.getKey();
            C a = y.getValue();
            ExpVector f = e.contract(1, pfac.nvar);
            C.put(f, a);
        }
        return Cp;
    }


    /**
     * Reverse variables. Used e.g. in opposite rings.
     * @return polynomial with reversed variables.
     */
    public GenPolynomial reverse(GenPolynomialRing oring) {
        GenPolynomial Cp = oring.getZERO().copy();
        if (this.isZERO()) {
            return Cp;
        }
        int k = -1;
        if (oring.tord.getEvord2() != 0 && oring.partial) {
            k = oring.tord.getSplit();
        }

        Map C = Cp.val; //getMap();
        Map A = val;
        ExpVector f;
        for (Map.Entry y : A.entrySet()) {
            ExpVector e = y.getKey();
            if (k >= 0) {
                f = e.reverse(k);
            } else {
                f = e.reverse();
            }
            C a = y.getValue();
            C.put(f, a);
        }
        return Cp;
    }


    /**
     * Iterator over coefficients.
     * @return val.values().iterator().
     */
    public Iterator coefficientIterator() {
        return val.values().iterator();
    }


    /**
     * Iterator over exponents.
     * @return val.keySet().iterator().
     */
    public Iterator exponentIterator() {
        return val.keySet().iterator();
    }


    /**
     * Iterator over monomials.
     * @return a PolyIterator.
     */
    public Iterator> iterator() {
        return new PolyIterator(val);
    }


    /**
     * Map a unary function to the coefficients.
     * @param f evaluation functor.
     * @return new polynomial with coefficients f(this(e)).
     */
    public GenPolynomial map(final UnaryFunctor f) {
        GenPolynomial n = ring.getZERO().copy();
        SortedMap nv = n.val;
        for (Monomial m : this) {
            //logger.info("m = " + m);
            C c = f.eval(m.c);
            if (c != null && !c.isZERO()) {
                nv.put(m.e, c);
            }
        }
        return n;
    }

}

 

Ads help maintain this website.