001/*
002 * $Id: RelationTable.java 4108 2012-08-18 10:57:40Z 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 ralations 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>.
032 * @author Heinz Kredel
033 */
034
035public class RelationTable<C extends RingElem<C>> implements Serializable {
036
037
038    /**
039     * The data structure for the relations.
040     */
041    public final Map<List<Integer>, List> table;
042
043
044    /**
045     * The factory for the solvable polynomial ring.
046     */
047    public final GenSolvablePolynomialRing<C> ring;
048
049
050    private static final Logger logger = Logger.getLogger(RelationTable.class);
051
052
053    private final boolean debug = true; //logger.isDebugEnabled();
054
055
056    /**
057     * Constructor for RelationTable requires ring factory. Note: This
058     * constructor is called within the constructor of the ring factory, so
059     * methods of this class can only be used after the other constructor has
060     * terminated.
061     * @param r solvable polynomial ring factory.
062     */
063    protected RelationTable(GenSolvablePolynomialRing<C> r) {
064        table = new HashMap<List<Integer>, List>();
065        ring = r;
066        if (ring == null) {
067            throw new IllegalArgumentException("RelationTable no ring");
068        }
069    }
070
071
072    /**
073     * RelationTable equals. Tests same keySets only, not relations itself. Will
074     * be improved in the future.
075     * @see java.lang.Object#equals(java.lang.Object)
076     */
077    @Override
078    @SuppressWarnings("unchecked")
079    // not jet working
080    public boolean equals(Object p) {
081        if (!(p instanceof RelationTable)) {
082            System.out.println("no RelationTable");
083            return false;
084        }
085        RelationTable<C> tab = null;
086        try {
087            tab = (RelationTable<C>) p;
088        } catch (ClassCastException ignored) {
089        }
090        if (tab == null) {
091            return false;
092        }
093        if (!ring.equals(tab.ring)) {
094            System.out.println("not same Ring " + ring.toScript() + ", " + tab.ring.toScript());
095            return false;
096        }
097        for (List<Integer> k : table.keySet()) {
098            List a = table.get(k);
099            List b = tab.table.get(k);
100            if (b == null) {
101                return false;
102            }
103            // check contents, but only base relations
104            if (!a.equals(b)) {
105                return false;
106            }
107        }
108        for (List<Integer> k : tab.table.keySet()) {
109            List a = table.get(k);
110            List b = tab.table.get(k);
111            if (a == null) {
112                return false;
113            }
114            // check contents, but only base relations
115            if (!a.equals(b)) {
116                return false;
117            }
118        }
119        return true;
120    }
121
122
123    /**
124     * Hash code for this relation table.
125     * @see java.lang.Object#hashCode()
126     */
127    @Override
128    public int hashCode() {
129        int h;
130        h = ring.hashCode();
131        h = 31 * h + table.hashCode();
132        return h;
133    }
134
135
136    /**
137     * Get the String representation.
138     * @see java.lang.Object#toString()
139     */
140    @Override
141    public String toString() {
142        List v;
143        StringBuffer s = new StringBuffer("RelationTable[");
144        boolean first = true;
145        for (List<Integer> k : table.keySet()) {
146            if (first) {
147                first = false;
148            } else {
149                s.append(", ");
150            }
151            s.append(k.toString());
152            v = table.get(k);
153            s.append("=");
154            s.append(v.toString());
155        }
156        s.append("]");
157        return s.toString();
158    }
159
160
161    /**
162     * Get the String representation.
163     * @param vars names for the variables.
164     * @see java.lang.Object#toString()
165     */
166    @SuppressWarnings("unchecked")
167    public String toString(String[] vars) {
168        if (vars == null) {
169            return toString();
170        }
171        List v;
172        StringBuffer s = new StringBuffer("RelationTable\n(");
173        if (PrettyPrint.isTrue()) {
174            boolean first = true;
175            for (List<Integer> k : table.keySet()) {
176                if (first) {
177                    first = false;
178                    s.append("\n");
179                } else {
180                    s.append(",\n");
181                }
182                v = table.get(k);
183                for (Iterator jt = v.iterator(); jt.hasNext();) {
184                    ExpVectorPair ep = (ExpVectorPair) jt.next();
185                    s.append("( " + ep.getFirst().toString(vars) + " ), ");
186                    s.append("( " + ep.getSecond().toString(vars) + " ), ");
187                    GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
188                    s.append("( " + p.toString(vars) + " )");
189                    if (jt.hasNext()) {
190                        s.append(",\n");
191                    }
192                }
193            }
194        } else {
195            boolean first = true;
196            for (List<Integer> k : table.keySet()) {
197                if (first) {
198                    first = false;
199                } else {
200                    s.append(",\n");
201                }
202                v = table.get(k);
203                for (Iterator jt = v.iterator(); jt.hasNext();) {
204                    ExpVectorPair ep = (ExpVectorPair) jt.next();
205                    s.append("( " + ep.getFirst().toString(vars) + " ), ");
206                    s.append("( " + ep.getSecond().toString(vars) + " ), ");
207                    GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
208                    s.append("( " + p.toString(vars) + " )");
209                    if (jt.hasNext()) {
210                        s.append(",\n");
211                    }
212                }
213            }
214        }
215        s.append("\n)\n");
216        return s.toString();
217    }
218
219
220    /**
221     * Get a scripting compatible string representation.
222     * @return script compatible representation for this relation table.
223     */
224    public String toScript() {
225        // Python case
226        String[] vars = ring.vars;
227        List v;
228        StringBuffer s = new StringBuffer("[");
229        boolean first = true;
230        for (List<Integer> k : table.keySet()) {
231            if (first) {
232                first = false;
233                s.append("");
234            } else {
235                s.append(", ");
236            }
237            v = table.get(k);
238            for (Iterator jt = v.iterator(); jt.hasNext();) {
239                ExpVectorPair ep = (ExpVectorPair) jt.next();
240                s.append("" + ep.getFirst().toScript(vars) + ", ");
241                s.append("" + ep.getSecond().toScript(vars) + ", ");
242                GenPolynomial<C> p = (GenPolynomial<C>) jt.next();
243                s.append("( " + p.toScript() + " )");
244                if (jt.hasNext()) {
245                    s.append(", ");
246                }
247            }
248        }
249        s.append("]");
250        return s.toString();
251    }
252
253
254    /**
255     * Update or initialize RelationTable with new relation. relation is e * f =
256     * p.
257     * @param e first term.
258     * @param f second term.
259     * @param p solvable product polynomial.
260     */
261    @SuppressWarnings("unchecked")
262    public synchronized void update(ExpVector e, ExpVector f, GenSolvablePolynomial<C> p) {
263        if (debug) {
264            //System.out.println("new relation = " + e + " .*. " + f + " = " + p);
265            if (p != null && p.ring.vars != null) {
266                logger.info("new relation = " + e.toString(p.ring.vars) + " .*. " + f.toString(p.ring.vars)
267                                + " = " + p);
268                //logger.info("existing relations = " + toString(p.ring.vars));
269            } else {
270                logger.info("new relation = " + e + " .*. " + f + " = " + p);
271            }
272        }
273        if (p == null || e == null || f == null) {
274            throw new IllegalArgumentException("RelationTable update e|f|p == null");
275        }
276        if (debug) {
277            if (e.totalDeg() == 1 && f.totalDeg() == 1) {
278                int[] de = e.dependencyOnVariables();
279                int[] df = f.dependencyOnVariables();
280                logger.debug("update e ? f " + de[0] + " " + df[0]);
281                //int t = ring.tord.getDescendComparator().compare(e,f);
282                //System.out.println("update compare(e,f) = " + t);
283                if (de[0] == df[0]) { // error 
284                    throw new IllegalArgumentException("RelationTable update e==f");
285                }
286                if (de[0] > df[0]) { // invalid update 
287                    logger.error("warning: update e > f " + e + " " + f + " changed");
288                    ExpVector tmp = e;
289                    e = f;
290                    f = tmp;
291                    Map.Entry<ExpVector, C> m = p.leadingMonomial();
292                    GenPolynomial<C> r = p.subtract(m.getValue(), m.getKey());
293                    r = r.negate();
294                    p = (GenSolvablePolynomial<C>) r.sum(m.getValue(), m.getKey());
295                }
296            }
297        }
298        ExpVector ef = e.sum(f);
299        ExpVector lp = p.leadingExpVector();
300        if (!ef.equals(lp)) { // check for suitable term order
301            logger.error("relation term order = " + ring.tord);
302            throw new IllegalArgumentException("RelationTable update e*f != lt(p)");
303        }
304        List<Integer> key = makeKey(e, f);
305        ExpVectorPair evp = new ExpVectorPair(e, f);
306        if (key.size() != 2) {
307            System.out.println("key = " + key + ", evp = " + evp);
308        }
309        List part = table.get(key);
310        if (part == null) { // initialization only
311            part = new LinkedList();
312            part.add(evp);
313            part.add(p);
314            table.put(key, part);
315            return;
316        }
317        Object skip;
318        int index = -1;
319        synchronized (part) { // with lookup()
320            for (ListIterator it = part.listIterator(); it.hasNext();) {
321                ExpVectorPair look = (ExpVectorPair) it.next();
322                skip = it.next(); // skip poly
323                if (look.isMultiple(evp)) {
324                    index = it.nextIndex();
325                    // last index of or first index of: break
326                }
327            }
328            if (index < 0) {
329                index = 0;
330            }
331            part.add(index, evp);
332            part.add(index + 1, p);
333        }
334        // table.put( key, part ); // required??
335    }
336
337
338    /**
339     * Update or initialize RelationTable with new relation. relation is e * f =
340     * p.
341     * @param E first term polynomial.
342     * @param F second term polynomial.
343     * @param p solvable product polynomial.
344     */
345    public void update(GenPolynomial<C> E, GenPolynomial<C> F, GenSolvablePolynomial<C> p) {
346        if (E.isZERO() || F.isZERO()) {
347            throw new IllegalArgumentException("polynomials may not be zero: " + E + ", " + F);
348        }
349        C ce = E.leadingBaseCoefficient();
350        C cf = F.leadingBaseCoefficient();
351        if (!ce.isONE() || !cf.isONE()) {
352            throw new IllegalArgumentException("lbcf of polynomials must be one: " + ce + ", " + cf);
353        }
354        ExpVector e = E.leadingExpVector();
355        ExpVector f = F.leadingExpVector();
356        update(e, f, p);
357    }
358
359
360    /**
361     * Update or initialize RelationTable with new relation. relation is e * f =
362     * p.
363     * @param E first term polynomial.
364     * @param F second term polynomial.
365     * @param p product polynomial.
366     */
367    public void update(GenPolynomial<C> E, GenPolynomial<C> F, GenPolynomial<C> p) {
368        if (p.isZERO()) {
369            throw new IllegalArgumentException("polynomial may not be zero: " + p);
370        }
371        if (p.isONE()) {
372            throw new IllegalArgumentException("product of polynomials may not be one: " + p);
373        }
374        GenSolvablePolynomial<C> sp = new GenSolvablePolynomial<C>(ring, p.val);
375        update(E, F, sp);
376    }
377
378
379    /**
380     * Lookup RelationTable for existing relation. Find p with e * f = p. If no
381     * relation for e * f is contained in the table then return the symmetric
382     * product p = 1 e f.
383     * @param e first term.
384     * @param f second term.
385     * @return t table relation container, contains e' and f' with e f = e'
386     *         lt(p) f'.
387     */
388    @SuppressWarnings("unchecked")
389    public TableRelation<C> lookup(ExpVector e, ExpVector f) {
390        List<Integer> key = makeKey(e, f);
391        List part = table.get(key);
392        if (part == null) { // symmetric product
393            ExpVector ef = e.sum(f);
394            GenSolvablePolynomial<C> p = ring.getONE().multiply(ef);
395            return new TableRelation<C>(null, null, p);
396        }
397        ExpVectorPair evp = new ExpVectorPair(e, f);
398        ExpVector ep = null;
399        ExpVector fp = null;
400        ExpVectorPair look = null;
401        GenSolvablePolynomial<C> p = null;
402        synchronized (part) { // with update()
403            for (Iterator it = part.iterator(); it.hasNext();) {
404                look = (ExpVectorPair) it.next();
405                p = (GenSolvablePolynomial<C>) it.next();
406                if (evp.isMultiple(look)) {
407                    ep = e.subtract(look.getFirst());
408                    fp = f.subtract(look.getSecond());
409                    if (ep.isZERO()) {
410                        ep = null;
411                    }
412                    if (fp.isZERO()) {
413                        fp = null;
414                    }
415                    if (debug) {
416                        if (p != null && p.ring.vars != null) {
417                            logger.info("found relation = " + e.toString(p.ring.vars) + " .*. "
418                                            + f.toString(p.ring.vars) + " = " + p);
419                        } else {
420                            logger.info("found relation = " + e + " .*. " + f + " = " + p);
421                        }
422                    }
423                    return new TableRelation<C>(ep, fp, p);
424                }
425            }
426        }
427        // unreacheable code!
428        throw new RuntimeException("no entry found in relation table for " + evp);
429    }
430
431
432    /**
433     * Construct a key for (e,f).
434     * @param e first term.
435     * @param f second term.
436     * @return k key for (e,f).
437     */
438    protected List<Integer> makeKey(ExpVector e, ExpVector f) {
439        int[] de = e.dependencyOnVariables();
440        int[] df = f.dependencyOnVariables();
441        List<Integer> key = new ArrayList<Integer>(de.length + df.length);
442        for (int i = 0; i < de.length; i++) {
443            key.add(Integer.valueOf(de[i]));
444        }
445        for (int i = 0; i < df.length; i++) {
446            key.add(Integer.valueOf(df[i]));
447        }
448        return key;
449    }
450
451
452    /**
453     * Size of the table.
454     * @return n number of non-commutative relations.
455     */
456    public int size() {
457        int s = 0;
458        if (table == null) {
459            return s;
460        }
461        for (Iterator<List> it = table.values().iterator(); it.hasNext();) {
462            List list = it.next();
463            s += list.size() / 2;
464        }
465        return s;
466    }
467
468
469    /**
470     * Extend variables. Used e.g. in module embedding. Extend all ExpVectors
471     * and polynomials of the given relation table by i elements and put the
472     * relations into this table, i.e. this should be empty.
473     * @param tab a relation table to be extended and inserted into this.
474     */
475    @SuppressWarnings("unchecked")
476    public void extend(RelationTable<C> tab) {
477        if (tab.table.size() == 0) {
478            return;
479        }
480        // assert this.size() == 0
481        int i = ring.nvar - tab.ring.nvar;
482        int j = 0;
483        long k = 0l;
484        List val;
485        for (List<Integer> key : tab.table.keySet()) {
486            val = tab.table.get(key);
487            for (Iterator jt = val.iterator(); jt.hasNext();) {
488                ExpVectorPair ep = (ExpVectorPair) jt.next();
489                ExpVector e = ep.getFirst();
490                ExpVector f = ep.getSecond();
491                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
492                ExpVector ex = e.extend(i, j, k);
493                ExpVector fx = f.extend(i, j, k);
494                GenSolvablePolynomial<C> px = (GenSolvablePolynomial<C>) p.extend(ring, j, k);
495                this.update(ex, fx, px);
496            }
497        }
498        return;
499    }
500
501
502    /**
503     * Contract variables. Used e.g. in module embedding. Contract all
504     * ExpVectors and polynomials of the given relation table by i elements and
505     * put the relations into this table, i.e. this should be empty.
506     * @param tab a relation table to be contracted and inserted into this.
507     */
508    @SuppressWarnings("unchecked")
509    public void contract(RelationTable<C> tab) {
510        if (tab.table.size() == 0) {
511            return;
512        }
513        // assert this.size() == 0
514        int i = tab.ring.nvar - ring.nvar;
515        List val;
516        for (List<Integer> key : tab.table.keySet()) {
517            val = tab.table.get(key);
518            for (Iterator jt = val.iterator(); jt.hasNext();) {
519                ExpVectorPair ep = (ExpVectorPair) jt.next();
520                ExpVector e = ep.getFirst();
521                ExpVector f = ep.getSecond();
522                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
523                ExpVector ec = e.contract(i, e.length() - i);
524                ExpVector fc = f.contract(i, f.length() - i);
525                if (ec.isZERO() || fc.isZERO()) {
526                    continue;
527                }
528                Map<ExpVector, GenPolynomial<C>> mc = p.contract(ring);
529                if (mc.size() != 1) {
530                    continue;
531                }
532                GenSolvablePolynomial<C> pc = null;
533                for (GenPolynomial<C> x : mc.values()) {
534                    if (pc != null) {
535                        // should not happen 
536                        logger.info("e = " + e + ", f = " + f + ", ec = " + ec + ", fc = " + fc);
537                        logger.info("p = " + p + ", mc = " + mc);
538                        throw new RuntimeException("Map.size() != 1: " + mc.size());
539                    }
540                    pc = (GenSolvablePolynomial<C>) x;
541                }
542                this.update(ec, fc, pc);
543            }
544        }
545        return;
546    }
547
548
549    /**
550     * Reverse variables and relations. Used e.g. in opposite rings. Reverse all
551     * ExpVectors and polynomials of the given relation table and put the
552     * modified relations into this table, i.e. this should be empty.
553     * @param tab a relation table to be reverted and inserted into this.
554     */
555    @SuppressWarnings("unchecked")
556    public void reverse(RelationTable<C> tab) {
557        if (tab.table.size() == 0) {
558            return;
559        }
560        // assert this.size() == 0
561        if (table.size() != 0) {
562            logger.error("reverse table not empty");
563        }
564        int k = -1;
565        if (ring.tord.getEvord2() != 0 && ring.partial) {
566            k = ring.tord.getSplit();
567        }
568        logger.debug("k split = " + k);
569        //System.out.println("k split = " + k );
570        for (List<Integer> key : tab.table.keySet()) {
571            List val = tab.table.get(key);
572            for (Iterator jt = val.iterator(); jt.hasNext();) {
573                ExpVectorPair ep = (ExpVectorPair) jt.next();
574                ExpVector e = ep.getFirst();
575                ExpVector f = ep.getSecond();
576                GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next();
577                //logger.info("e pre reverse = " + e );
578                //logger.info("f pre reverse = " + f );
579                //logger.info("p pre reverse = " + p );
580                ExpVector ex;
581                ExpVector fx;
582                GenSolvablePolynomial<C> px;
583                boolean change = true; // if relevant vars reversed
584                if (k >= 0) {
585                    ex = e.reverse(k);
586                    fx = f.reverse(k);
587                    int[] ed = ex.dependencyOnVariables(); // = e
588                    if (ed.length == 0 || ed[0] >= k) { // k >= 0
589                        change = false;
590                    }
591                    int[] fd = fx.dependencyOnVariables(); // = f
592                    if (fd.length == 0 || fd[0] >= k) { // k >= 0
593                        change = false;
594                    }
595                } else {
596                    ex = e.reverse();
597                    fx = f.reverse();
598                }
599                px = (GenSolvablePolynomial<C>) p.reverse(ring);
600                //System.out.println("change = " + change );
601                if (!change) {
602                    this.update(e, f, px); // same order
603                } else {
604                    this.update(fx, ex, px); // opposite order
605                    //this.update( ex, fx, px ); // same order
606                }
607            }
608        }
609        return;
610    }
611
612}
613
614
615/**
616 * TableRelation container for storage and printing in RelationTable.
617 * @author Heinz Kredel
618 */
619
620class TableRelation<C extends RingElem<C>> implements Serializable {
621
622
623    /**
624     * First ExpVector of the data structure.
625     */
626    public final ExpVector e;
627
628
629    /**
630     * Second ExpVector of the data structure.
631     */
632    public final ExpVector f;
633
634
635    /**
636     * GenSolvablePolynomial of the data structure.
637     */
638    public final GenSolvablePolynomial<C> p;
639
640
641    /**
642     * Constructor to setup the data structure.
643     * @param e first term.
644     * @param f second term.
645     * @param p product polynomial.
646     */
647    public TableRelation(ExpVector e, ExpVector f, GenSolvablePolynomial<C> p) {
648        this.e = e;
649        this.f = f;
650        this.p = p;
651    }
652
653
654    /**
655     * Get the String representation.
656     * @see java.lang.Object#toString()
657     */
658    @Override
659    public String toString() {
660        StringBuffer s = new StringBuffer("TableRelation[");
661        s.append("" + e);
662        s.append(" | ");
663        s.append("" + f);
664        s.append(" = ");
665        s.append("" + p);
666        s.append("]");
667        return s.toString();
668    }
669
670}