001/*
002 * $Id: RelationTable.java 5311 2015-10-25 22:28:29Z kredel $
003 */
004
005package edu.jas.poly;
006
007
008import java.io.Serializable;
009import java.util.ArrayList;
010import java.util.HashMap;
011import java.util.Iterator;
012import java.util.LinkedList;
013import java.util.List;
014import java.util.ListIterator;
015import java.util.Map;
016
017import org.apache.log4j.Logger;
018
019import edu.jas.kern.PrettyPrint;
020import edu.jas.structure.RingElem;
021
022
023/**
024 * RelationTable for solvable polynomials. This class maintains the
025 * non-commutative multiplication relations of solvable polynomial rings. The
026 * table entries are initialized with relations of the form x<sub>j</sub> *
027 * x<sub>i</sub> = p<sub>ij</sub>. During multiplication the relations are
028 * updated by relations of the form x<sub>j</sub><sup>k</sup> *
029 * x<sub>i</sub><sup>l</sup> = p<sub>ijkl</sub>. If no relation for
030 * x<sub>j</sub> * x<sub>i</sub> is found in the table, this multiplication is
031 * assumed to be commutative x<sub>i</sub> x<sub>j</sub>. Can also be used for
032 * relations between coefficients and main variables.
033 * @author Heinz Kredel
034 */
035
036public class RelationTable<C extends RingElem<C>> implements Serializable {
037
038
039    /**
040     * The data structure for the relations.
041     */
042    public final Map<List<Integer>, List> table;
043
044
045    /**
046     * The factory for the solvable polynomial ring.
047     */
048    public final GenSolvablePolynomialRing<C> ring;
049
050
051    /**
052     * Usage indicator: table or coeffTable.
053     */
054    public final boolean coeffTable;
055
056
057    private static final Logger logger = Logger.getLogger(RelationTable.class);
058
059
060    private final boolean debug = logger.isDebugEnabled();
061
062
063    /**
064     * Constructor for RelationTable requires ring factory. Note: This
065     * constructor is called within the constructor of the ring factory, so
066     * methods of this class can only be used after the other constructor has
067     * terminated.
068     * @param r solvable polynomial ring factory.
069     */
070    public RelationTable(GenSolvablePolynomialRing<C> r) {
071        this(r, false);
072    }
073
074
075    /**
076     * Constructor for RelationTable requires ring factory. Note: This
077     * constructor is called within the constructor of the ring factory, so
078     * methods of this class can only be used after the other constructor has
079     * terminated.
080     * @param r solvable polynomial ring factory.
081     * @param coeffTable indicator for coeffTable.
082     */
083    public RelationTable(GenSolvablePolynomialRing<C> r, boolean coeffTable) {
084        table = new HashMap<List<Integer>, List>();
085        ring = r;
086        if (ring == null) {
087            throw new IllegalArgumentException("RelationTable no ring");
088        }
089        this.coeffTable = coeffTable;
090    }
091
092
093    /**
094     * RelationTable equals. Tests same keySets and base relations.
095     * @see java.lang.Object#equals(java.lang.Object)
096     */
097    @Override
098    @SuppressWarnings("unchecked")
099    public boolean equals(Object p) {
100        if (p == null) {
101            return false;
102        }
103        if (!(p instanceof RelationTable)) {
104            logger.info("no RelationTable");
105            return false;
106        }
107        RelationTable<C> tab = (RelationTable<C>) p;
108        // not possible because of infinite recursion:
109        //if (!ring.equals(tab.ring)) {
110        //    logger.info("not same Ring " + ring.toScript() + ", " + tab.ring.toScript());
111        //    return false;
112        //}
113        if (!table.keySet().equals(tab.table.keySet())) {
114            logger.info("keySet != :  a = " + table.keySet() + ", b = " + tab.table.keySet());
115            return false;
116        }
117        for (Map.Entry<List<Integer>,List> me : table.entrySet()) {
118            List<Integer> k = me.getKey();
119            List a = me.getValue();
120            List b = tab.table.get(k);
121            // check contents, but only base relations
122            Map<ExpVectorPair, GenPolynomial<C>> t1ex = fromListDeg2(a);
123            Map<ExpVectorPair, GenPolynomial<C>> t2ex = fromListDeg2(b);
124            if (!equalMaps(t1ex, t2ex)) {
125                //System.out.println("a != b, a = " + t1ex + ", b = " + t2ex);
126                return false;
127            }
128        }
129        return true;
130    }
131
132
133    /**
134     * Convert mixed list to map for base relations.
135     * @param a mixed list
136     * @returns a map constructed from the list with deg(key) == 2.
137     */
138    @SuppressWarnings("unchecked")
139    Map<ExpVectorPair, GenPolynomial<C>> fromListDeg2(List a) {
140        Map<ExpVectorPair, GenPolynomial<C>> tex = new HashMap<ExpVectorPair, GenPolynomial<C>>();
141        Iterator ait = a.iterator();
142        while (ait.hasNext()) {
143            ExpVectorPair ae = (ExpVectorPair) ait.next();
144            if (!ait.hasNext()) {
145                break;
146            }
147            GenPolynomial<C> p = (GenPolynomial<C>) ait.next();
148            if (ae.totalDeg() == 2) { // only base relations
149                //System.out.println("ae => p: " + ae + " => " + p);
150                tex.put(ae, p);
151            }
152        }
153        return tex;
154    }
155
156
157    /**
158     * Hash code for base relations.
159     * @param a mixed list
160     * @returns hashCode(a)
161     */
162    @SuppressWarnings("unchecked")
163    int fromListDeg2HashCode(List a) {
164        int h = 0;
165        Iterator ait = a.iterator();
166        while (ait.hasNext()) {
167            ExpVectorPair ae = (ExpVectorPair) ait.next();
168            h = 31 * h + ae.hashCode();
169            if (!ait.hasNext()) {
170                break;
171            }
172            GenPolynomial<C> p = (GenPolynomial<C>) ait.next();
173            if (ae.totalDeg() == 2) { // only base relations
174                //System.out.println("ae => p: " + ae + " => " + p);
175                h = 31 * h + p.val.hashCode(); // avoid hash of ring
176            }
177        }
178        return h;
179    }
180
181
182    /**
183     * Equals for special maps.
184     * @param m1 first map
185     * @param m2 second map
186     * @returns true if both maps are equal
187     */
188    @SuppressWarnings("unchecked")
189    boolean equalMaps(Map<ExpVectorPair, GenPolynomial<C>> m1, Map<ExpVectorPair, GenPolynomial<C>> m2) {
190        if (!m1.keySet().equals(m2.keySet())) {
191            return false;
192        }
193        for (Map.Entry<ExpVectorPair, GenPolynomial<C>> me : m1.entrySet()) {
194            GenPolynomial<C> p1 = me.getValue();
195            ExpVectorPair ep = me.getKey();
196            GenPolynomial<C> p2 = m2.get(ep);
197            if (p1.compareTo(p2) != 0) { // not working: !p1.equals(p2)) { // TODO
198                logger.info("ep = " + ep + ", p1 = " + p1 + ", p2 = " + p2);
199                //logger.info("p1.compareTo(p2) = " + p1.compareTo(p2));
200                //logger.info("p1.equals(p2) = " + p1.equals(p2));
201                return false;
202            }
203        }
204        return true;
205    }
206
207
208    /**
209     * Hash code for this relation table.
210     * @see java.lang.Object#hashCode()
211     */
212    @Override
213    public int hashCode() {
214        //int h = ring.hashCode(); // infinite recursion
215        int h = 0; //table.hashCode();
216        h = table.keySet().hashCode();
217        for (Map.Entry<List<Integer>,List> me : table.entrySet()) {
218            //List<Integer> k = me.getKey();
219            List a = me.getValue();
220            int t1 = fromListDeg2HashCode(a);
221            h = 31 * h + t1;
222        }
223        return h;
224    }
225
226
227    /**
228     * Test if the table is empty.
229     * @return true if the table is empty, else false.
230     */
231    public boolean isEmpty() {
232        return table.isEmpty();
233    }
234
235
236    /**
237     * Get the String representation.
238     * @see java.lang.Object#toString()
239     */
240    @Override
241    public String toString() {
242        List v;
243        StringBuffer s = new StringBuffer("RelationTable[");
244        boolean first = true;
245        for (Map.Entry<List<Integer>,List> me : table.entrySet()) {
246            List<Integer> k = me.getKey();
247            if (first) {
248                first = false;
249            } else {
250                s.append(", ");
251            }
252            s.append(k.toString());
253            v = me.getValue();
254            s.append("=");
255            s.append(v.toString());
256        }
257        s.append("]");
258        return s.toString();
259    }
260
261
262    /**
263     * Get the String representation.
264     * @param vars names for the variables.
265     * @see java.lang.Object#toString()
266     */
267    @SuppressWarnings({ "unchecked", "cast" })
268    public String toString(String[] vars) {
269        if (vars == null) {
270            return toString();
271        }
272        StringBuffer s = new StringBuffer("");
273        String[] cvars = null;
274        if (coeffTable) {
275            if (ring.coFac instanceof GenPolynomialRing) {
276                cvars = ((GenPolynomialRing<C>) (Object) ring.coFac).getVars();
277            } else if (ring.coFac instanceof GenWordPolynomialRing) {
278                cvars = ((GenWordPolynomialRing<C>) (Object) ring.coFac).getVars();
279            }
280            s.append("Coefficient ");
281        }
282        s.append("RelationTable\n(");
283        List v;
284        if (PrettyPrint.isTrue()) {
285            boolean first = true;
286            for (Map.Entry<List<Integer>,List> me : table.entrySet()) {
287                List<Integer> k = me.getKey();
288                if (first) {
289                    first = false;
290                    s.append("\n");
291                } else {
292                    s.append(",\n");
293                }
294                v = me.getValue();
295                for (Iterator jt = v.iterator(); jt.hasNext();) {
296                    ExpVectorPair ep = (ExpVectorPair) jt.next();
297                    GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
298                    if (ep.totalDeg() != 2) { // only base relations
299                        continue;
300                    }
301                    s.append("( " + ep.getFirst().toString(vars) + " ), ");
302                    if (cvars == null) {
303                        s.append("( " + ep.getSecond().toString(vars) + " ), ");
304                    } else {
305                        s.append("( " + ep.getSecond().toString(cvars) + " ), ");
306                    }
307                    s.append("( " + p.toString(vars) + " )");
308                    if (jt.hasNext()) {
309                        s.append(",\n");
310                    }
311                }
312            }
313        } else {
314            boolean first = true;
315            for (Map.Entry<List<Integer>,List> me : table.entrySet()) {
316                List<Integer> k = me.getKey();
317                if (first) {
318                    first = false;
319                } else {
320                    s.append(",\n");
321                }
322                v = me.getValue();
323                for (Iterator jt = v.iterator(); jt.hasNext();) {
324                    ExpVectorPair ep = (ExpVectorPair) jt.next();
325                    s.append("( " + ep.getFirst().toString(vars) + " ), ");
326                    if (cvars == null) {
327                        s.append("( " + ep.getSecond().toString(vars) + " ), ");
328                    } else {
329                        s.append("( " + ep.getSecond().toString(cvars) + " ), ");
330                    }
331                    GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
332                    //s.append("( " + p.toString(vars) + " )");
333                    s.append(" " + p.toString(vars));
334                    if (jt.hasNext()) {
335                        s.append(",\n");
336                    }
337                }
338            }
339        }
340        s.append("\n)\n");
341        return s.toString();
342    }
343
344
345    /**
346     * Get a scripting compatible string representation.
347     * @return script compatible representation for this relation table.
348     */
349    @SuppressWarnings({ "unchecked", "cast" })
350    public String toScript() {
351        // Python case
352        String[] vars = ring.vars;
353        String[] cvars = null;
354        if (coeffTable) {
355            if (ring.coFac instanceof GenPolynomialRing) {
356                cvars = ((GenPolynomialRing<C>) (Object) ring.coFac).getVars();
357            } else if (ring.coFac instanceof GenWordPolynomialRing) {
358                cvars = ((GenWordPolynomialRing<C>) (Object) ring.coFac).getVars();
359            }
360        }
361        List v;
362        StringBuffer s = new StringBuffer("[");
363        boolean first = true;
364        for (Map.Entry<List<Integer>,List> me : table.entrySet()) {
365            List<Integer> k = me.getKey();
366            if (first) {
367                first = false;
368                s.append("");
369            } else {
370                s.append(", ");
371            }
372            v = me.getValue();
373            for (Iterator jt = v.iterator(); jt.hasNext();) {
374                ExpVectorPair ep = (ExpVectorPair) jt.next();
375                GenPolynomial<C> p = (GenPolynomial<C>) jt.next();
376                if (ep.totalDeg() > 2) { // only base relations
377                    continue;
378                }
379                s.append("" + ep.getFirst().toScript(vars) + ", ");
380                if (coeffTable) {
381                    String eps = ep.getSecond().toScript(cvars);
382                    if (eps.isEmpty()) { // if from a deeper down ring
383                        eps = p.leadingBaseCoefficient().abs().toScript();
384                    }
385                    s.append("" + eps + ", ");
386                } else {
387                    s.append("" + ep.getSecond().toScript(vars) + ", ");
388                }
389                //s.append("( " + p.toScript() + " )");
390                s.append(" " + p.toScript());
391                if (jt.hasNext()) {
392                    s.append(", ");
393                }
394            }
395        }
396        s.append("]");
397        return s.toString();
398    }
399
400
401    /**
402     * Update or initialize RelationTable with new relation. relation is e * f =
403     * p.
404     * @param e first term.
405     * @param f second term.
406     * @param p solvable product polynomial.
407     */
408    @SuppressWarnings({ "unchecked", "cast" })
409    public synchronized void update(ExpVector e, ExpVector f, GenSolvablePolynomial<C> p) {
410        if (p == null || e == null || f == null) {
411            throw new IllegalArgumentException("RelationTable update e|f|p == null");
412        }
413        GenSolvablePolynomialRing<C> sring = p.ring;
414        if (debug) {
415            logger.info("new relation = " + sring.toScript(e) + " .*. " + sring.toScript(f) + " = "
416                            + p.toScript());
417        }
418        // test equal HTs for left and right side
419        if (!coeffTable) { // old case
420            if (e.totalDeg() == 1 && f.totalDeg() == 1) { // higher or mixed degrees TODO
421                int[] de = e.dependencyOnVariables();
422                int[] df = f.dependencyOnVariables();
423                logger.debug("update e ? f " + de[0] + " " + df[0]);
424                //int t = ring.tord.getDescendComparator().compare(e,f);
425                //System.out.println("update compare(e,f) = " + t);
426                if (de[0] == df[0]) { // error 
427                    throw new IllegalArgumentException("RelationTable update e==f");
428                }
429                if (de[0] > df[0]) { // invalid update 
430                    logger.error("update e < f: " + sring.toScript(e) + " < " + sring.toScript(f));
431                    //if (debug && false) {
432                    //    throw new IllegalArgumentException("update e < f");
433                    //}
434                    ExpVector tmp = e;
435                    e = f;
436                    f = tmp;
437                    Map.Entry<ExpVector, C> m = p.leadingMonomial();
438                    ExpVector ef = e.sum(f);
439                    if (!ef.equals(m.getKey())) {
440                        throw new IllegalArgumentException("update e*f != lt(p): " + sring.toScript(ef)
441                                        + ", lt = " + sring.toScript(m.getKey()));
442                    }
443                    GenPolynomial<C> r = p.reductum(); //subtract(m.getValue(), m.getKey());
444                    r = r.negate();
445                    //p = (GenSolvablePolynomial<C>) r.sum(m.getValue(), m.getKey());
446                    p = (GenSolvablePolynomial<C>) r;
447                    p.doPutToMap(m.getKey(), m.getValue());
448                }
449            }
450            ExpVector ef = e.sum(f);
451            ExpVector lp = p.leadingExpVector();
452            if (!ef.equals(lp)) { // check for suitable term order
453                logger.error("relation term order = " + ring.tord);
454                throw new IllegalArgumentException("update e*f != lt(p): " + sring.toScript(ef) + " != "
455                                + sring.toScript(lp));
456            }
457        } else { // is coeffTable
458            ExpVector lp = p.leadingExpVector();
459            if (!e.equals(lp)) { // check for suitable term order
460                logger.error("relation term order = " + ring.tord);
461                throw new IllegalArgumentException("Coefficient RelationTable update e != lt(p): "
462                                + sring.toScript(e) + " != " + sring.toScript(lp));
463            }
464            if (p.leadingBaseCoefficient() instanceof GenPolynomial) {
465                lp = ((GenPolynomial<C>) (Object) p.leadingBaseCoefficient()).leadingExpVector();
466                if (!f.equals(lp)) { // check for suitable term order
467                    logger.error("relation term order = " + ring.tord);
468                    logger.error("Coefficient RelationTable update f != lt(lfcd(p)): " + sring.toScript(e)
469                                    + ", f = " + f + ", p = " + p.toScript());
470                    throw new IllegalArgumentException("Coefficient RelationTable update f != lt(lfcd(p)): "
471                                    + e + ", f = " + f + ", p = " + p);
472                }
473            } else if (p.leadingBaseCoefficient() instanceof GenWordPolynomial) {
474                lp = ((GenWordPolynomial<C>) (Object) p.leadingBaseCoefficient()).leadingWord()
475                                .leadingExpVector();
476                if (!f.equals(lp)) { // check for suitable term order and word structure
477                    logger.error("relation term order = " + ring.tord);
478                    logger.error("Coefficient RelationTable update f != lt(lfcd(p)): " + sring.toScript(e)
479                                    + ", f = " + f + ", p = " + p.toScript());
480                    throw new IllegalArgumentException("Coefficient RelationTable update f != lt(lfcd(p)): "
481                                    + e + ", f = " + f + ", p = " + p);
482                }
483            }
484        }
485        List<Integer> key = makeKey(e, f);
486        ExpVectorPair evp = new ExpVectorPair(e, f);
487        if (key.size() != 2) {
488            logger.warn("key = " + key + ", evp = " + evp);
489        }
490        List part = table.get(key);
491        if (part == null) { // initialization only
492            part = new LinkedList();
493            part.add(evp);
494            part.add(p);
495            table.put(key, part);
496            return;
497        }
498        @SuppressWarnings("unused")
499        Object skip;
500        int index = -1;
501        synchronized (part) { // with lookup()
502            for (ListIterator it = part.listIterator(); it.hasNext();) {
503                ExpVectorPair look = (ExpVectorPair) it.next();
504                skip = it.next(); // skip poly
505                if (look.isMultiple(evp)) {
506                    index = it.nextIndex();
507                    // last index of or first index of: break
508                }
509            }
510            if (index < 0) {
511                index = 0;
512            }
513            part.add(index, evp);
514            part.add(index + 1, p);
515        }
516        // table.put( key, part ); // required??
517    }
518
519
520    /**
521     * Update or initialize RelationTable with new relation. relation is e * f =
522     * p.
523     * @param E first term polynomial.
524     * @param F second term polynomial.
525     * @param p solvable product polynomial.
526     */
527    @SuppressWarnings({ "unchecked", "cast" })
528    public void update(GenPolynomial<C> E, GenPolynomial<C> F, GenSolvablePolynomial<C> p) {
529        if (E.isZERO() || F.isZERO()) {
530            throw new IllegalArgumentException("polynomials may not be zero: " + E + ", " + F);
531        }
532        C ce = E.leadingBaseCoefficient();
533        C cf = F.leadingBaseCoefficient();
534        if (!ce.isONE()) {
535            throw new IllegalArgumentException("lbcf of polynomials must be one: " + ce + ", " + cf
536                            + ", p = " + p);
537        }
538        ExpVector e = E.leadingExpVector();
539        ExpVector f = F.leadingExpVector();
540        if (coeffTable && f.isZERO()) {
541            if (cf instanceof GenPolynomial) {
542                f = ((GenPolynomial<C>) (Object) cf).leadingExpVector();
543            } else if (cf instanceof GenWordPolynomial) {
544                f = ((GenWordPolynomial<C>) (Object) cf).leadingWord().leadingExpVector();
545            }
546        }
547        //logger.info("update: " + e + " .*. " + f + " = " + p);
548        update(e, f, p);
549    }
550
551
552    /**
553     * Update or initialize RelationTable with new relation. relation is e * f =
554     * p.
555     * @param E first term polynomial.
556     * @param F second term polynomial.
557     * @param p product polynomial.
558     */
559    public void update(GenPolynomial<C> E, GenPolynomial<C> F, GenPolynomial<C> p) {
560        if (p.isZERO()) {
561            throw new IllegalArgumentException("polynomial may not be zero: " + p);
562        }
563        if (p.isONE()) {
564            throw new IllegalArgumentException("product of polynomials may not be one: " + p);
565        }
566        GenSolvablePolynomial<C> sp = new GenSolvablePolynomial<C>(ring, p.val);
567        update(E, F, sp);
568    }
569
570
571    /**
572     * Update or initialize RelationTable with new relation. relation is e * f =
573     * p.
574     * @param e first term.
575     * @param f second term.
576     * @param p solvable product polynomial.
577     */
578    public void update(ExpVector e, ExpVector f, GenPolynomial<C> p) {
579        if (p.isZERO()) {
580            throw new IllegalArgumentException("polynomial may not be zero: " + p);
581        }
582        if (p.isONE()) {
583            throw new IllegalArgumentException("product of polynomials may not be one: " + p);
584        }
585        GenSolvablePolynomial<C> sp = new GenSolvablePolynomial<C>(ring, p.val);
586        update(e, f, sp);
587    }
588
589
590    /**
591     * Lookup RelationTable for existing relation. Find p with e * f = p. If no
592     * relation for e * f is contained in the table then return the symmetric
593     * product p = 1 e f.
594     * @param e first term.
595     * @param f second term.
596     * @return t table relation container, contains e' and f' with e f = e'
597     *         lt(p) f'.
598     */
599    @SuppressWarnings({ "unchecked", "cast" })
600    public TableRelation<C> lookup(ExpVector e, ExpVector f) {
601        List<Integer> key = makeKey(e, f);
602        List part = table.get(key);
603        if (part == null) { // symmetric product
604            GenSolvablePolynomial<C> p = null;
605            C c = null;
606            if (!coeffTable) {
607                ExpVector ef = e.sum(f);
608                //p = new GenSolvablePolynomial<C>(ring, ring.getONECoefficient(), ef);
609                p = ring.valueOf(ef);
610            } else {
611                if (ring.coFac instanceof GenPolynomialRing) {
612                    GenPolynomialRing<C> cofac = (GenPolynomialRing<C>) (Object) ring.coFac;
613                    //System.out.println("f = " + f + ", e = " + e);
614                    GenPolynomial<C> pc = cofac.valueOf(f);
615                    c = (C) (Object) pc;
616                } else if (ring.coFac instanceof GenWordPolynomialRing) {
617                    GenWordPolynomialRing<C> cofac = (GenWordPolynomialRing<C>) (Object) ring.coFac;
618                    //System.out.println("f = " + f + ", e = " + e);
619                    GenWordPolynomial<C> pc = cofac.valueOf(f);
620                    c = (C) (Object) pc;
621                }
622                p = new GenSolvablePolynomial<C>(ring, c, e);
623                //System.out.println("pc = " + pc + ", p = " + p);
624            }
625            return new TableRelation<C>(null, null, p);
626        }
627        // no distinction between coefficient f or polynomial f
628        ExpVectorPair evp = new ExpVectorPair(e, f);
629        ExpVector ep = null;
630        ExpVector fp = null;
631        ExpVectorPair look = null;
632        GenSolvablePolynomial<C> p = null;
633        synchronized (part) { // with update()
634            for (Iterator it = part.iterator(); it.hasNext();) {
635                look = (ExpVectorPair) it.next();
636                p = (GenSolvablePolynomial<C>) it.next();
637                if (evp.isMultiple(look)) {
638                    ep = e.subtract(look.getFirst());
639                    fp = f.subtract(look.getSecond());
640                    if (ep.isZERO()) {
641                        ep = null;
642                    }
643                    if (fp.isZERO()) {
644                        fp = null;
645                    }
646                    if (debug) {
647                        if (p != null && p.ring.vars != null) {
648                            logger.info("found relation = " + e.toString(p.ring.vars) + " .*. "
649                                            + f.toString(p.ring.vars) + " = " + p);
650                        } else {
651                            logger.info("found relation = " + e + " .*. " + f + " = " + p);
652                        }
653                    }
654                    return new TableRelation<C>(ep, fp, p);
655                }
656            }
657        }
658        // unreacheable code!
659        throw new RuntimeException("no entry found in relation table for " + evp);
660    }
661
662
663    /**
664     * Construct a key for (e,f).
665     * @param e first term.
666     * @param f second term.
667     * @return k key for (e,f).
668     */
669    protected List<Integer> makeKey(ExpVector e, ExpVector f) {
670        int[] de = e.dependencyOnVariables();
671        int[] df = f.dependencyOnVariables();
672        List<Integer> key = new ArrayList<Integer>(de.length + df.length);
673        for (int i = 0; i < de.length; i++) {
674            key.add(Integer.valueOf(de[i]));
675        }
676        for (int i = 0; i < df.length; i++) {
677            key.add(Integer.valueOf(df[i]));
678        }
679        return key;
680    }
681
682
683    /**
684     * Size of the table.
685     * @return n number of non-commutative relations.
686     */
687    public int size() {
688        int s = 0;
689        if (table == null || table.isEmpty()) {
690            return s;
691        }
692        for (Iterator<List> it = table.values().iterator(); it.hasNext();) {
693            List list = it.next();
694            s += list.size() / 2;
695        }
696        return s;
697    }
698
699
700    /**
701     * Extend variables. Used e.g. in module embedding. Extend all ExpVectors
702     * and polynomials of the given relation table by i elements and put the
703     * relations into this table, i.e. this should be empty.
704     * @param tab a relation table to be extended and inserted into this.
705     */
706    @SuppressWarnings("unchecked")
707    public void extend(RelationTable<C> tab) {
708        if (tab.table.isEmpty()) {
709            return;
710        }
711        // assert this.size() == 0
712        int i = ring.nvar - tab.ring.nvar;
713        int j = 0;
714        long k = 0l;
715        List val;
716        for (List<Integer> key : tab.table.keySet()) {
717            val = tab.table.get(key);
718            for (Iterator jt = val.iterator(); jt.hasNext();) {
719                ExpVectorPair ep = (ExpVectorPair) jt.next();
720                ExpVector e = ep.getFirst();
721                ExpVector f = ep.getSecond();
722                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
723                ExpVector ex = e.extend(i, j, k);
724                ExpVector fx;
725                if (coeffTable) {
726                    fx = f;
727                } else {
728                    fx = f.extend(i, j, k);
729                }
730                GenSolvablePolynomial<C> px = (GenSolvablePolynomial<C>) p.extend(ring, j, k);
731                this.update(ex, fx, px);
732            }
733        }
734        return;
735    }
736
737
738    /**
739     * Contract variables. Used e.g. in module embedding. Contract all
740     * ExpVectors and polynomials of the given relation table by i elements and
741     * put the relations into this table, i.e. this should be empty.
742     * @param tab a relation table to be contracted and inserted into this.
743     */
744    @SuppressWarnings("unchecked")
745    public void contract(RelationTable<C> tab) {
746        if (tab.table.isEmpty()) {
747            return;
748        }
749        // assert this.size() == 0
750        int i = tab.ring.nvar - ring.nvar;
751        List val;
752        for (List<Integer> key : tab.table.keySet()) {
753            val = tab.table.get(key);
754            //System.out.println("key = " + key + ", val = " + val);
755            for (Iterator jt = val.iterator(); jt.hasNext();) {
756                ExpVectorPair ep = (ExpVectorPair) jt.next();
757                ExpVector e = ep.getFirst();
758                ExpVector f = ep.getSecond();
759                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
760                ExpVector ec = e.contract(i, e.length() - i);
761                ExpVector fc;
762                if (coeffTable) {
763                    fc = f;
764                } else {
765                    fc = f.contract(i, f.length() - i);
766                }
767                //System.out.println("ec = " + ec + ", fc = " + fc);
768                if (ec.isZERO()) {
769                    continue;
770                }
771                Map<ExpVector, GenPolynomial<C>> mc = p.contract(ring);
772                if (mc.size() != 1) {
773                    continue;
774                }
775                GenPolynomial<C> pc = mc.values().iterator().next();
776                this.update(ec, fc, pc);
777            }
778        }
779        return;
780    }
781
782
783    /**
784     * Recursive representation. Split all ExpVectors and polynomials of the
785     * given relation table to lower elements and upper elements and put the
786     * relations into this table or this as coefficient table, i.e. this should
787     * be empty.
788     * @param tab a relation table to be contracted and inserted into this.
789     */
790    @SuppressWarnings({ "unchecked", "cast" })
791    public void recursive(RelationTable tab) { //<C>
792        if (tab.table.isEmpty()) {
793            return;
794        }
795        //System.out.println("rel ring = " + ring.toScript());
796        // assert this.size() == 0
797        GenPolynomialRing<C> cring = (GenPolynomialRing<C>) (Object) ring.coFac;
798        //System.out.println("cring    = " + cring.toScript());
799        GenSolvablePolynomial<C> pc;
800        int i = ring.nvar; // tab.ring.nvar -
801        for (Object okey : tab.table.keySet()) {
802            List<Integer> key = (List<Integer>) okey;
803            List val = (List) tab.table.get(key);
804            //System.out.println("key = " + key + ", val = " + val);
805            for (Iterator jt = val.iterator(); jt.hasNext();) {
806                ExpVectorPair ep = (ExpVectorPair) jt.next();
807                ExpVector e = ep.getFirst();
808                ExpVector f = ep.getSecond();
809                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
810                ExpVector ec = e.contract(0, i);
811                ExpVector fc;
812                if (coeffTable) {
813                    fc = f;
814                } else {
815                    fc = f.contract(0, i);
816                }
817                //System.out.println("ec = " + ec + ", fc = " + fc);
818                if (ec.isZERO()) {
819                    continue;
820                }
821                Map<ExpVector, GenPolynomial<C>> mc = p.contract(cring);
822                //System.out.println("mc = " + mc + ", p = " + p);
823                //System.out.println("mc.ring = " + mc.values().iterator().next().ring.toScript());
824                if (mc.size() == 1) { // < 1 only for p == 0
825                    pc = (GenSolvablePolynomial<C>) mc.values().iterator().next();
826                    this.update(ec, fc, pc);
827                } else { // construct recursive polynomial
828                    GenSolvablePolynomial<C> qr = ring.getZERO();
829                    for (Map.Entry<ExpVector, GenPolynomial<C>> mce : mc.entrySet()) {
830                        ExpVector g = mce.getKey();
831                        GenPolynomial<C> q = mce.getValue();
832                        C cq = (C) (Object) q;
833                        GenSolvablePolynomial<C> qp = new GenSolvablePolynomial<C>(ring, cq, g);
834                        qr = (GenSolvablePolynomial<C>) qr.sum(qp);
835                    }
836                    if (coeffTable) {
837                        fc = ((GenPolynomial<C>) (Object) qr.leadingBaseCoefficient()).leadingExpVector();
838                    }
839                    if (fc.isZERO()) {
840                        continue;
841                    }
842                    //System.out.println("ec = " + ec + ", fc = " + fc + ", qr = " + qr);
843                    if (coeffTable) {
844                        logger.info("coeffTable: adding " + qr);
845                    } else {
846                        logger.info("no coeffTable: adding " + qr);
847                    }
848                    this.update(ec, fc, qr);
849                }
850            }
851        }
852        return;
853    }
854
855
856    /**
857     * Reverse variables and relations. Used e.g. in opposite rings. Reverse all
858     * ExpVectors and polynomials of the given relation table and put the
859     * modified relations into this table, i.e. this should be empty.
860     * @param tab a relation table to be reverted and inserted into this.
861     */
862    @SuppressWarnings("unchecked")
863    public void reverse(RelationTable<C> tab) {
864        if (tab.table.isEmpty()) {
865            return;
866        }
867        // assert this.size() == 0
868        if (!table.isEmpty()) {
869            logger.error("reverse table not empty");
870        }
871        int k = -1;
872        if (ring.tord.getEvord2() != 0 && ring.partial) {
873            k = ring.tord.getSplit();
874        }
875        logger.debug("k split = " + k);
876        //System.out.println("k split = " + k );
877        //System.out.println("reversed ring = " + ring.toScript() );
878        for (List<Integer> key : tab.table.keySet()) {
879            List val = tab.table.get(key);
880            for (Iterator jt = val.iterator(); jt.hasNext();) {
881                ExpVectorPair ep = (ExpVectorPair) jt.next();
882                ExpVector e = ep.getFirst();
883                ExpVector f = ep.getSecond();
884                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
885                //logger.info("e pre reverse = " + e );
886                //logger.info("f pre reverse = " + f );
887                //logger.info("p pre reverse = " + p );
888                //if (e.degree() > 1 || f.degree() > 1) { // not required
889                //    continue; // revert only base relations
890                //}
891                ExpVector ex;
892                ExpVector fx;
893                GenSolvablePolynomial<C> px;
894                boolean change = true; // if relevant vars reversed
895                if (k >= 0) {
896                    ex = e.reverse(k);
897                    if (coeffTable) {
898                        fx = f;
899                    } else {
900                        fx = f.reverse(k);
901                    }
902                    //int[] ed = ex.dependencyOnVariables(); // = e
903                    //if (ed.length == 0 || ed[0] >= k) { // k >= 0
904                    //    change = false; todo
905                    //}
906                    //int[] fd = fx.dependencyOnVariables(); // = f
907                    //if (fd.length == 0 || fd[0] >= k) { // k >= 0
908                    //    change = false; todo
909                    //}
910                } else {
911                    ex = e.reverse();
912                    if (coeffTable) {
913                        fx = f;
914                    } else {
915                        fx = f.reverse();
916                    }
917                }
918                px = (GenSolvablePolynomial<C>) p.reverse(ring);
919                //System.out.println("update, p, px: " + p.toScript() + " reverse:" + px.toScript() );
920                if (!change) {
921                    this.update(e, f, px); // same order
922                } else {
923                    if (coeffTable) {
924                        this.update(ex, fx, px); // same order
925                    } else {
926                        this.update(fx, ex, px); // opposite order
927                    }
928                }
929            }
930        }
931        return;
932    }
933
934
935    /**
936     * Convert relation table to list of polynomial triples.
937     * @return rel = (e1,f1,p1, ...) where ei * fi = pi are solvable relations.
938     */
939    @SuppressWarnings({ "unchecked", "cast" })
940    public List<GenSolvablePolynomial<C>> relationList() {
941        List<GenSolvablePolynomial<C>> rels = new ArrayList<GenSolvablePolynomial<C>>();
942        //C one = ring.getONECoefficient();
943        for (Map.Entry<List<Integer>,List> me : table.entrySet()) {
944            List<Integer> k = me.getKey();
945            List v = me.getValue();
946            for (Iterator jt = v.iterator(); jt.hasNext();) {
947                ExpVectorPair ep = (ExpVectorPair) jt.next();
948                ExpVector e = ep.getFirst();
949                GenSolvablePolynomial<C> pe = ring.valueOf(e);
950                ExpVector f = ep.getSecond();
951                GenSolvablePolynomial<C> pf = null;
952                if (coeffTable) { // todo
953                    C cf = null;
954                    if (ring.coFac instanceof GenPolynomialRing) {
955                        GenPolynomial<C> cpf;
956                        cpf = ((GenPolynomialRing<C>) (Object) ring.coFac).valueOf(f);
957                        cf = (C) (Object) cpf; // down cast
958                    } else if (ring.coFac instanceof GenWordPolynomialRing) {
959                        GenWordPolynomial<C> cpf;
960                        cpf = ((GenWordPolynomialRing<C>) (Object) ring.coFac).valueOf(f);
961                        cf = (C) (Object) cpf; // down cast
962                    }
963                    //pf = new GenSolvablePolynomial<C>(ring, cf);
964                    pf = ring.valueOf(cf);
965                } else {
966                    pf = ring.valueOf(f);
967                }
968                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
969                rels.add(pe);
970                rels.add(pf);
971                rels.add(p);
972            }
973        }
974        return rels;
975    }
976
977
978    /**
979     * Add list of polynomial triples as relations.
980     * @param rel = (e1,f1,p1, ...) where ei * fi = pi are solvable relations.
981     *            <b>Note:</b> Only because of type erasure, aequivalent to
982     *            addRelations().
983     */
984    //@SuppressWarnings("unchecked")
985    public void addSolvRelations(List<GenSolvablePolynomial<C>> rel) {
986        PolynomialList<C> Prel = new PolynomialList<C>(ring, rel);
987        addRelations(Prel.getList());
988    }
989
990
991    /**
992     * Add list of polynomial triples as relations.
993     * @param rel = (e1,f1,p1, ...) where ei * fi = pi are solvable relations.
994     */
995    @SuppressWarnings({ "unchecked", "cast" })
996    public void addRelations(List<GenPolynomial<C>> rel) {
997        if (rel == null || rel.isEmpty()) {
998            return;
999        }
1000        Iterator<GenPolynomial<C>> relit = rel.iterator();
1001        while (relit.hasNext()) {
1002            GenPolynomial<C> E = relit.next();
1003            ExpVector e = E.leadingExpVector();
1004            ExpVector f = null;
1005            if (!relit.hasNext()) {
1006                throw new IllegalArgumentException("F and poly part missing");
1007            }
1008            GenPolynomial<C> F = relit.next();
1009            if (!relit.hasNext()) {
1010                throw new IllegalArgumentException("poly part missing");
1011            }
1012            GenPolynomial<C> P = relit.next();
1013            if (coeffTable && F.isConstant()) { // todo
1014                if (ring.coFac instanceof GenPolynomialRing) {
1015                    f = ((GenPolynomial<C>) (Object) F.leadingBaseCoefficient()).leadingExpVector();
1016                } else if (ring.coFac instanceof GenWordPolynomialRing) {
1017                    f = ((GenWordPolynomial<C>) (Object) F.leadingBaseCoefficient()).leadingWord()
1018                                    .leadingExpVector();
1019                }
1020            } else {
1021                f = F.leadingExpVector();
1022            }
1023            update(e, f, P);
1024        }
1025        return;
1026    }
1027
1028}