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