001/*
002 * $Id$
003 */
004
005package edu.jas.application;
006
007
008import java.io.Serializable;
009import java.util.ArrayList;
010import java.util.Collections;
011import java.util.Comparator;
012import java.util.List;
013import java.util.SortedSet;
014import java.util.TreeSet;
015
016import org.apache.logging.log4j.LogManager;
017import org.apache.logging.log4j.Logger;
018
019import edu.jas.gb.WordGroebnerBaseAbstract;
020import edu.jas.gb.WordGroebnerBaseSeq;
021import edu.jas.gb.WordReduction;
022import edu.jas.gb.WordReductionSeq;
023import edu.jas.gbufd.PolyGBUtil;
024import edu.jas.kern.Scripting;
025import edu.jas.poly.GenWordPolynomial;
026import edu.jas.poly.GenWordPolynomialRing;
027import edu.jas.poly.PolyUtil;
028import edu.jas.poly.Word;
029import edu.jas.structure.GcdRingElem;
030import edu.jas.structure.NotInvertibleException;
031
032
033/**
034 * Word Ideal implements some methods for ideal arithmetic, for example
035 * containment, sum or product. <b>Note:</b> only two-sided ideals.
036 * @author Heinz Kredel
037 */
038public class WordIdeal<C extends GcdRingElem<C>> implements Comparable<WordIdeal<C>>, Serializable {
039
040
041    private static final Logger logger = LogManager.getLogger(WordIdeal.class);
042
043
044    private static final boolean debug = logger.isDebugEnabled();
045
046
047    /**
048     * The data structure is a list of word polynomials.
049     */
050    protected List<GenWordPolynomial<C>> list;
051
052
053    /**
054     * Reference to the word polynomial ring.
055     */
056    protected GenWordPolynomialRing<C> ring;
057
058
059    /**
060     * Indicator if list is a Groebner Base.
061     */
062    protected boolean isGB;
063
064
065    /**
066     * Indicator if test has been performed if this is a Groebner Base.
067     */
068    protected boolean testGB;
069
070
071    /**
072     * Groebner base engine.
073     */
074    protected final WordGroebnerBaseAbstract<C> bb;
075
076
077    /**
078     * Reduction engine.
079     */
080    protected final WordReduction<C> red;
081
082
083    /**
084     * Constructor.
085     * @param ring word polynomial ring
086     */
087    public WordIdeal(GenWordPolynomialRing<C> ring) {
088        this(ring, new ArrayList<GenWordPolynomial<C>>());
089    }
090
091
092    /**
093     * Constructor.
094     * @param ring word polynomial ring
095     * @param list word polynomial list
096     */
097    public WordIdeal(GenWordPolynomialRing<C> ring, List<GenWordPolynomial<C>> list) {
098        this(ring, list, false);
099    }
100
101
102    /**
103     * Constructor.
104     * @param ring word polynomial ring
105     * @param list word polynomial list
106     * @param bb Groebner Base engine
107     * @param red Reduction engine
108     */
109    public WordIdeal(GenWordPolynomialRing<C> ring, List<GenWordPolynomial<C>> list,
110                    WordGroebnerBaseAbstract<C> bb, WordReduction<C> red) {
111        this(ring, list, false, bb, red);
112    }
113
114
115    /**
116     * Constructor.
117     * @param ring word polynomial ring
118     * @param list word polynomial list
119     * @param gb true if list is known to be a Groebner Base, else false
120     */
121    public WordIdeal(GenWordPolynomialRing<C> ring, List<GenWordPolynomial<C>> list, boolean gb) {
122        this(ring, list, gb, new WordGroebnerBaseSeq<C>(), new WordReductionSeq<C>());
123        //this(list, gb, topt, GBFactory.getImplementation(list.ring.coFac));
124    }
125
126
127    /**
128     * Constructor.
129     * @param ring word polynomial ring
130     * @param list word polynomial list
131     * @param gb true if list is known to be a Groebner Base, else false
132     * @param bb Groebner Base engine
133     */
134    public WordIdeal(GenWordPolynomialRing<C> ring, List<GenWordPolynomial<C>> list, boolean gb,
135                    WordGroebnerBaseAbstract<C> bb) {
136        this(ring, list, gb, bb, bb.red);
137    }
138
139
140    /**
141     * Constructor.
142     * @param ring word polynomial ring
143     * @param list word polynomial list
144     * @param gb true if list is known to be a Groebner Base, else false
145     * @param bb Groebner Base engine
146     * @param red Reduction engine
147     */
148    public WordIdeal(GenWordPolynomialRing<C> ring, List<GenWordPolynomial<C>> list, boolean gb,
149                    WordGroebnerBaseAbstract<C> bb, WordReduction<C> red) {
150        if (ring == null) {
151            throw new IllegalArgumentException("ring may not be null");
152        }
153        if (list == null) {
154            throw new IllegalArgumentException("list may not be null");
155        }
156        this.ring = ring;
157        this.list = list;
158        this.isGB = gb;
159        this.testGB = (gb ? true : false); // ??
160        this.bb = bb;
161        this.red = red;
162        if (debug) {
163            logger.info("constructed: {}", this);
164        }
165    }
166
167
168    /**
169     * Clone this.
170     * @return a copy of this.
171     */
172    public WordIdeal<C> copy() {
173        return new WordIdeal<C>(ring, new ArrayList<GenWordPolynomial<C>>(list), isGB, bb, red);
174    }
175
176
177    /**
178     * Get the List of GenWordPolynomials.
179     * @return (cast) list.list
180     */
181    public List<GenWordPolynomial<C>> getList() {
182        return list;
183    }
184
185
186    /**
187     * Get the GenWordPolynomialRing.
188     * @return (cast) list.ring
189     */
190    public GenWordPolynomialRing<C> getRing() {
191        return ring;
192    }
193
194
195    /**
196     * Get the zero ideal.
197     * @return ideal(0)
198     */
199    public WordIdeal<C> getZERO() {
200        List<GenWordPolynomial<C>> z = new ArrayList<GenWordPolynomial<C>>(0);
201        return new WordIdeal<C>(ring, z, true, bb, red);
202    }
203
204
205    /**
206     * Get the one ideal.
207     * @return ideal(1)
208     */
209    public WordIdeal<C> getONE() {
210        List<GenWordPolynomial<C>> one = new ArrayList<GenWordPolynomial<C>>(1);
211        one.add(getRing().getONE());
212        return new WordIdeal<C>(ring, one, true, bb, red);
213    }
214
215
216    /**
217     * String representation of the word ideal.
218     * @see java.lang.Object#toString()
219     */
220    @Override
221    public String toString() {
222        StringBuffer sb = new StringBuffer("(");
223        boolean first = true;
224        for (GenWordPolynomial<C> p : list) {
225            if (!first) {
226                sb.append(",");
227            } else {
228                first = false;
229            }
230            sb.append(p.toString());
231        }
232        sb.append(")");
233        return sb.toString();
234    }
235
236
237    /**
238     * Get a scripting compatible string representation.
239     * @return script compatible representation for this Element.
240     * @see edu.jas.structure.Element#toScript()
241     */
242    public String toScript() {
243        StringBuffer s = new StringBuffer();
244        switch (Scripting.getLang()) {
245        case Ruby:
246            s.append("WordPolyIdeal.new(");
247            break;
248        case Python:
249        default:
250            s.append("WordIdeal(");
251        }
252        if (ring != null) {
253            s.append(ring.toScript());
254        }
255        if (list == null) {
256            s.append(")");
257            return s.toString();
258        }
259        switch (Scripting.getLang()) {
260        case Ruby:
261            s.append(",\"\",[");
262            break;
263        case Python:
264        default:
265            s.append(",list=[");
266        }
267        boolean first = true;
268        String sa = null;
269        for (GenWordPolynomial<C> oa : list) {
270            sa = oa.toScript();
271            if (first) {
272                first = false;
273            } else {
274                s.append(", ");
275            }
276            //s.append("( " + sa + " )");
277            s.append(sa);
278        }
279        s.append("])");
280        return s.toString();
281    }
282
283
284    /**
285     * Comparison with any other object. <b>Note:</b> If not both ideals are
286     * Groebner Bases, then false may be returned even the ideals are equal.
287     * @see java.lang.Object#equals(java.lang.Object)
288     */
289    @Override
290    @SuppressWarnings("unchecked")
291    public boolean equals(Object b) {
292        if (!(b instanceof WordIdeal)) {
293            logger.warn("equals no Ideal");
294            return false;
295        }
296        WordIdeal<C> B = null;
297        try {
298            B = (WordIdeal<C>) b;
299        } catch (ClassCastException ignored) {
300            return false;
301        }
302        SortedSet<GenWordPolynomial<C>> s = new TreeSet<GenWordPolynomial<C>>(list);
303        SortedSet<GenWordPolynomial<C>> t = new TreeSet<GenWordPolynomial<C>>(B.list);
304        if (isGB && B.isGB) { //requires also monic polys
305            return s.equals(t);
306        }
307        if (s.equals(t)) {
308            return true;
309        }
310        //System.out.println("no GBs contains");
311        return this.contains(B) && B.contains(this);
312    }
313
314
315    /**
316     * WordIdeal comparison.
317     * @param L other word ideal.
318     * @return compareTo() of polynomial lists.
319     */
320    public int compareTo(WordIdeal<C> L) {
321        int si = Math.min(L.list.size(), list.size());
322        int s = 0;
323        final Comparator<Word> wc = ring.alphabet.getAscendComparator();
324        Comparator<GenWordPolynomial<C>> cmp = new Comparator<GenWordPolynomial<C>>() {
325
326
327            public int compare(GenWordPolynomial<C> p1, GenWordPolynomial<C> p2) {
328                Word w1 = p1.leadingWord();
329                Word w2 = p2.leadingWord();
330                if (w1 == null) {
331                    return -1; // dont care
332                }
333                if (w2 == null) {
334                    return 1; // dont care
335                }
336                if (w1.length() != w2.length()) {
337                    if (w1.length() > w2.length()) {
338                        return 1; // dont care
339                    }
340                    return -1; // dont care
341                }
342                return wc.compare(w1, w2);
343            }
344        };
345
346        List<GenWordPolynomial<C>> l1 = new ArrayList<GenWordPolynomial<C>>(list);
347        List<GenWordPolynomial<C>> l2 = new ArrayList<GenWordPolynomial<C>>(L.list);
348        //Collections.sort(l1);
349        //Collections.sort(l2);
350        Collections.sort(l1, cmp);
351        Collections.sort(l2, cmp);
352        for (int i = 0; i < si; i++) {
353            GenWordPolynomial<C> a = l1.get(i);
354            GenWordPolynomial<C> b = l2.get(i);
355            s = a.compareTo(b);
356            if (s != 0) {
357                return s;
358            }
359        }
360        if (list.size() > si) {
361            return 1;
362        }
363        if (L.list.size() > si) {
364            return -1;
365        }
366        return s;
367    }
368
369
370    /**
371     * Hash code for this word ideal.
372     * @see java.lang.Object#hashCode()
373     */
374    @Override
375    public int hashCode() {
376        int h;
377        h = list.hashCode();
378        if (isGB) {
379            h = h << 1;
380        }
381        if (testGB) {
382            h += 1;
383        }
384        return h;
385    }
386
387
388    /**
389     * Test if ZERO ideal.
390     * @return true, if this is the 0 ideal, else false
391     */
392    public boolean isZERO() {
393        //return list.isZERO();
394        if (list == null) { // never true
395            return true;
396        }
397        for (GenWordPolynomial<C> p : list) {
398            if (p == null) {
399                continue;
400            }
401            if (!p.isZERO()) {
402                return false;
403            }
404        }
405        return true;
406    }
407
408
409    /**
410     * Test if ONE is contained in the ideal. To test for a proper ideal use
411     * <code>! id.isONE()</code>.
412     * @return true, if this is the 1 ideal, else false
413     */
414    public boolean isONE() {
415        //return list.isONE();
416        if (list == null) {
417            return false;
418        }
419        for (GenWordPolynomial<C> p : list) {
420            if (p == null) {
421                continue;
422            }
423            if (p.isONE()) {
424                return true;
425            }
426        }
427        return false;
428    }
429
430
431    /**
432     * Test if this is a twosided Groebner base.
433     * @return true, if this is a twosided Groebner base, else false
434     */
435    public boolean isGB() {
436        if (testGB) {
437            return isGB;
438        }
439        logger.warn("isGB computing");
440        isGB = bb.isGB(getList());
441        testGB = true;
442        return isGB;
443    }
444
445
446    /**
447     * Do Groebner Base. Compute the Groebner Base for this ideal.
448     */
449    @SuppressWarnings("unchecked")
450    public void doGB() {
451        if (isGB && testGB) {
452            return;
453        }
454        List<GenWordPolynomial<C>> G = getList();
455        logger.info("doGB computing = {}", G);
456        list = bb.GB(G);
457        isGB = true;
458        testGB = true;
459        return;
460    }
461
462
463    /**
464     * Groebner Base. Get a Groebner Base for this ideal.
465     * @return twosidedGB(this)
466     */
467    public WordIdeal<C> GB() {
468        if (isGB) {
469            return this;
470        }
471        doGB();
472        return this;
473    }
474
475
476    /**
477     * Word ideal containment. Test if B is contained in this ideal. Note: this
478     * is eventually modified to become a Groebner Base.
479     * @param B word ideal
480     * @return true, if B is contained in this, else false
481     */
482    public boolean contains(WordIdeal<C> B) {
483        if (B == null || B.isZERO()) {
484            return true;
485        }
486        return contains(B.getList());
487    }
488
489
490    /**
491     * Word ideal containment. Test if b is contained in this ideal. Note: this
492     * is eventually modified to become a Groebner Base.
493     * @param b word polynomial
494     * @return true, if b is contained in this, else false
495     */
496    public boolean contains(GenWordPolynomial<C> b) {
497        if (b == null || b.isZERO()) {
498            return true;
499        }
500        if (this.isONE()) {
501            return true;
502        }
503        if (this.isZERO()) {
504            return false;
505        }
506        if (!isGB) {
507            doGB();
508        }
509        GenWordPolynomial<C> z = red.normalform(getList(), b);
510        if (z == null || z.isZERO()) {
511            return true;
512        }
513        return false;
514    }
515
516
517    /**
518     * Word ideal containment. Test if each b in B is contained in this ideal.
519     * Note: this is eventually modified to become a Groebner Base.
520     * @param B list of word polynomials
521     * @return true, if each b in B is contained in this, else false
522     */
523    public boolean contains(List<GenWordPolynomial<C>> B) {
524        if (B == null || B.size() == 0) {
525            return true;
526        }
527        if (this.isONE()) {
528            return true;
529        }
530        if (!isGB) {
531            doGB();
532        }
533        List<GenWordPolynomial<C>> si = getList();
534        for (GenWordPolynomial<C> b : B) {
535            if (b == null) {
536                continue;
537            }
538            GenWordPolynomial<C> z = red.normalform(si, b);
539            if (!z.isZERO()) {
540                System.out.println("contains nf(b) != 0: " + b + ", z = " + z);
541                //System.out.println("contains nf(b) != 0: si = " + si);
542                return false;
543            }
544        }
545        return true;
546    }
547
548
549    /**
550     * Word ideal summation. Generators for the sum of ideals. Note: if both
551     * ideals are Groebner bases, a Groebner base is returned.
552     * @param B word ideal
553     * @return ideal(this+B)
554     */
555    public WordIdeal<C> sum(WordIdeal<C> B) {
556        if (B == null || B.isZERO()) {
557            return this;
558        }
559        if (this.isZERO()) {
560            return B;
561        }
562        int s = getList().size() + B.getList().size();
563        List<GenWordPolynomial<C>> c;
564        c = new ArrayList<GenWordPolynomial<C>>(s);
565        c.addAll(getList());
566        c.addAll(B.getList());
567        WordIdeal<C> I = new WordIdeal<C>(getRing(), c, false);
568        if (isGB && B.isGB) {
569            I.doGB();
570        }
571        return I;
572    }
573
574
575    /**
576     * Word summation. Generators for the sum of ideal and a polynomial. Note:
577     * if this ideal is a Groebner base, a Groebner base is returned.
578     * @param b word polynomial
579     * @return ideal(this+{b})
580     */
581    public WordIdeal<C> sum(GenWordPolynomial<C> b) {
582        if (b == null || b.isZERO()) {
583            return this;
584        }
585        int s = getList().size() + 1;
586        List<GenWordPolynomial<C>> c;
587        c = new ArrayList<GenWordPolynomial<C>>(s);
588        c.addAll(getList());
589        c.add(b);
590        WordIdeal<C> I = new WordIdeal<C>(getRing(), c, false);
591        if (isGB) {
592            I.doGB();
593        }
594        return I;
595    }
596
597
598    /**
599     * Word summation. Generators for the sum of this ideal and a list of
600     * polynomials. Note: if this ideal is a Groebner base, a Groebner base is
601     * returned.
602     * @param L list of word polynomials
603     * @return ideal(this+L)
604     */
605    public WordIdeal<C> sum(List<GenWordPolynomial<C>> L) {
606        if (L == null || L.isEmpty()) {
607            return this;
608        }
609        int s = getList().size() + L.size();
610        List<GenWordPolynomial<C>> c = new ArrayList<GenWordPolynomial<C>>(s);
611        c.addAll(getList());
612        c.addAll(L);
613        WordIdeal<C> I = new WordIdeal<C>(getRing(), c, false);
614        if (isGB) {
615            I.doGB();
616        }
617        return I;
618    }
619
620
621    /**
622     * Product. Generators for the product of ideals. Note: if both ideals are
623     * Groebner bases, a Groebner base is returned.
624     * @param B word ideal
625     * @return ideal(this*B)
626     */
627    public WordIdeal<C> product(WordIdeal<C> B) {
628        if (B == null || B.isZERO()) {
629            return B;
630        }
631        if (this.isZERO()) {
632            return this;
633        }
634        int s = getList().size() * B.getList().size();
635        List<GenWordPolynomial<C>> c;
636        c = new ArrayList<GenWordPolynomial<C>>(s);
637        for (GenWordPolynomial<C> p : getList()) {
638            for (GenWordPolynomial<C> q : B.getList()) {
639                q = p.multiply(q);
640                c.add(q);
641            }
642        }
643        WordIdeal<C> I = new WordIdeal<C>(getRing(), c, false);
644        if (isGB && B.isGB) {
645            I.doGB();
646        }
647        return I;
648    }
649
650
651    /**
652     * Left product. Generators for the product this by a polynomial.
653     * @param b word polynomial
654     * @return ideal(this*b)
655     */
656    public WordIdeal<C> product(GenWordPolynomial<C> b) {
657        if (b == null || b.isZERO()) {
658            return getZERO();
659        }
660        if (this.isZERO()) {
661            return this;
662        }
663        List<GenWordPolynomial<C>> c;
664        c = new ArrayList<GenWordPolynomial<C>>(getList().size());
665        for (GenWordPolynomial<C> p : getList()) {
666            GenWordPolynomial<C> q = p.multiply(b);
667            c.add(q);
668        }
669        WordIdeal<C> I = new WordIdeal<C>(getRing(), c, false);
670        if (isGB) {
671            I.doGB();
672        }
673        return I;
674    }
675
676
677    /*
678     * Intersection. Generators for the intersection of ideals. Using an
679     * iterative algorithm.
680     * @param Bl list of word ideals
681     * @return ideal(cap_i B_i), a Groebner base
682     */
683    public WordIdeal<C> intersect(List<WordIdeal<C>> Bl) {
684        if (Bl == null || Bl.size() == 0) {
685            return getZERO();
686        }
687        WordIdeal<C> I = null;
688        for (WordIdeal<C> B : Bl) {
689            if (I == null) {
690                I = B;
691                continue;
692            }
693            if (I.isONE()) {
694                return I;
695            }
696            I = I.intersect(B);
697        }
698        return I;
699    }
700
701
702    /*
703     * Intersection. Generators for the intersection of ideals.
704     * @param B word ideal
705     * @return ideal(this cap B), a Groebner base
706     */
707    public WordIdeal<C> intersect(WordIdeal<C> B) {
708        if (B == null || B.isZERO()) { // (0)
709            return B;
710        }
711        if (this.isZERO()) {
712            return this;
713        }
714        List<GenWordPolynomial<C>> c = PolyGBUtil.<C> intersect(getRing(), getList(), B.getList());
715        WordIdeal<C> I = new WordIdeal<C>(getRing(), c, true);
716        return I;
717    }
718
719
720    /*
721     * Intersection. Generators for the intersection of a ideal with a
722     * word polynomial ring. The polynomial ring of this ideal must be
723     * a contraction of R and the TermOrder must be an elimination
724     * order.
725     * @param R word polynomial ring
726     * @return ideal(this cap R)
727     */
728    public WordIdeal<C> intersect(GenWordPolynomialRing<C> R) {
729        if (R == null) {
730            throw new IllegalArgumentException("R may not be null");
731        }
732        List<GenWordPolynomial<C>> H = PolyUtil.<C> intersect(R, getList());
733        return new WordIdeal<C>(R, H, isGB);
734    }
735
736
737    /*
738     * Eliminate. Generators for the intersection of this ideal with a word
739     * polynomial ring. The word polynomial ring of this ideal must be a
740     * contraction of R and the TermOrder must be an elimination order.
741     * @param R word polynomial ring
742     * @return ideal(this cap R)
743     */
744    public WordIdeal<C> eliminate(GenWordPolynomialRing<C> R) {
745        if (R == null) {
746            throw new IllegalArgumentException("R may not be null");
747        }
748        if (getRing().equals(R)) {
749            return this;
750        }
751        return intersect(R);
752    }
753
754
755    /*
756     * Quotient. Generators for the word ideal quotient.
757     * @param h word polynomial
758     * @return ideal(this : h), a Groebner base
759    public WordIdeal<C> quotient(GenWordPolynomial<C> h) {
760        if (h == null) { // == (0)
761            return this;
762        }
763        if (h.isZERO()) {
764            return this;
765        }
766        if (this.isZERO()) {
767            return this;
768        }
769        List<GenWordPolynomial<C>> H;
770        H = new ArrayList<GenWordPolynomial<C>>(1);
771        H.add(h);
772        WordIdeal<C> Hi = new WordIdeal<C>(getRing(), H, true);
773        WordIdeal<C> I = this.intersect(Hi);
774        List<GenWordPolynomial<C>> Q;
775        Q = new ArrayList<GenWordPolynomial<C>>(I.getList().size());
776        for (GenWordPolynomial<C> q : I.getList()) {
777            q = (GenWordPolynomial<C>) q.divide(h); // remainder == 0
778            Q.add(q);
779        }
780        return new WordIdeal<C>(getRing(), Q, true);
781    }
782     */
783
784
785    /*
786     * Quotient. Generators for the word ideal quotient.
787     * @param H word ideal
788     * @return ideal(this : H), a Groebner base
789    public WordIdeal<C> quotient(WordIdeal<C> H) {
790        if (H == null || H.isZERO()) { // == (0)
791            return this;
792        }
793        if (this.isZERO()) {
794            return this;
795        }
796        WordIdeal<C> Q = null;
797        for (GenWordPolynomial<C> h : H.getList()) {
798            WordIdeal<C> Hi = this.quotient(h);
799            if (Q == null) {
800                Q = Hi;
801            } else {
802                Q = Q.intersect(Hi);
803            }
804        }
805        return Q;
806    }
807     */
808
809
810    /**
811     * Power. Generators for the power of this word ideal. Note: if this ideal
812     * is a Groebner base, a Groebner base is returned.
813     * @param d integer
814     * @return ideal(this^d)
815     */
816    public WordIdeal<C> power(int d) {
817        if (d <= 0) {
818            return getONE();
819        }
820        if (this.isZERO() || this.isONE()) {
821            return this;
822        }
823        WordIdeal<C> c = this;
824        for (int i = 1; i < d; i++) {
825            c = c.product(this);
826        }
827        return c;
828    }
829
830
831    /**
832     * Normalform for element.
833     * @param h word polynomial
834     * @return left normalform of h with respect to this
835     */
836    public GenWordPolynomial<C> normalform(GenWordPolynomial<C> h) {
837        if (h == null) {
838            return h;
839        }
840        if (h.isZERO()) {
841            return h;
842        }
843        if (this.isZERO()) {
844            return h;
845        }
846        GenWordPolynomial<C> r;
847        r = red.normalform(getList(), h);
848        return r;
849    }
850
851
852    /**
853     * Normalform for list of word elements.
854     * @param L word polynomial list
855     * @return list of left normalforms of the elements of L with respect to
856     *         this
857     */
858    public List<GenWordPolynomial<C>> normalform(List<GenWordPolynomial<C>> L) {
859        if (L == null) {
860            return L;
861        }
862        if (L.size() == 0) {
863            return L;
864        }
865        if (this.isZERO()) {
866            return L;
867        }
868        List<GenWordPolynomial<C>> M = new ArrayList<GenWordPolynomial<C>>(L.size());
869        for (GenWordPolynomial<C> h : L) {
870            GenWordPolynomial<C> r = normalform(h);
871            if (r != null && !r.isZERO()) {
872                M.add(r);
873            }
874        }
875        return M;
876    }
877
878
879    /**
880     * Inverse for element modulo this ideal.
881     * @param h word polynomial
882     * @return inverse of h with respect to this, if defined
883     */
884    public GenWordPolynomial<C> inverse(GenWordPolynomial<C> h) {
885        if (h == null || h.isZERO()) {
886            throw new NotInvertibleException("zero not invertible");
887        }
888        if (this.isZERO()) {
889            throw new NotInvertibleException("zero ideal");
890        }
891        if (h.isUnit()) {
892            return h.inverse();
893        }
894        if (isUnit(h)) {
895            logger.warn("{} is invertable, but inverse not computed", h);
896        }
897        throw new UnsupportedOperationException("inverse of " + h);
898        /* TODO compute inverse
899        doGB();
900        List<GenWordPolynomial<C>> F = new ArrayList<GenWordPolynomial<C>>(1 + list.list.size());
901        F.add(h);
902        F.addAll(getList());
903        //System.out.println("F = " + F);
904        WordExtendedGB<C> x = bb.extLeftGB(F);
905        List<GenWordPolynomial<C>> G = x.G;
906        //System.out.println("G = " + G);
907        GenWordPolynomial<C> one = null;
908        int i = -1;
909        for (GenWordPolynomial<C> p : G) {
910            i++;
911            if (p == null) {
912                continue;
913            }
914            if (p.isUnit()) {
915                one = p;
916                break;
917            }
918        }
919        if (one == null) {
920            throw new NotInvertibleException("one == null: h = " + h);
921        }
922        List<GenWordPolynomial<C>> row = x.G2F.get(i); // != -1
923        //System.out.println("row = " + row);
924        GenWordPolynomial<C> g = row.get(0);
925        if (g == null || g.isZERO()) {
926            throw new NotInvertibleException("g == 0: h = " + h);
927        }
928        GenWordPolynomial<C> gp = red.leftNormalform(getList(), g);
929        if (gp.isZERO()) { // can happen with solvable rings
930            throw new NotInvertibleException("solv|gp == 0: h = " + h + ", g = " + g);
931        }
932        // adjust leading coefficient of g to get g*h == 1
933        GenWordPolynomial<C> f = g.multiply(h);
934        //System.out.println("f = " + f);
935        GenWordPolynomial<C> k = red.leftNormalform(getList(), f);
936        //System.out.println("k = " + k);
937        if (!k.isONE()) {
938            C lbc = k.leadingBaseCoefficient();
939            lbc = lbc.inverse();
940            g = g.multiply(lbc);
941        }
942        return g;
943        */
944    }
945
946
947    /**
948     * Test if element is a unit modulo this ideal.
949     * @param h word polynomial
950     * @return true if h is a unit with respect to this, else false
951     */
952    public boolean isUnit(GenWordPolynomial<C> h) {
953        if (h == null || h.isZERO()) {
954            return false;
955        }
956        if (this.isZERO()) {
957            return false;
958        }
959        if (h.isUnit()) {
960            return true;
961        }
962        // test this + (h) == 1: then ex p, q: pp*this**qq + p*h*q = 1
963        List<GenWordPolynomial<C>> F = new ArrayList<GenWordPolynomial<C>>(1 + list.size());
964        F.add(h);
965        F.addAll(getList());
966        List<GenWordPolynomial<C>> G = bb.GB(F);
967        if (debug) {
968            logger.info("isUnit GB = {}", G);
969        }
970        for (GenWordPolynomial<C> p : G) {
971            if (p == null) {
972                continue;
973            }
974            if (p.isUnit()) {
975                return true;
976            }
977        }
978        return false;
979    }
980
981
982    /**
983     * Ideal common zero test.
984     * @return -1, 0 or 1 if dimension(this) &eq; -1, 0 or &ge; 1.
985     */
986    public int commonZeroTest() {
987        if (this.isZERO()) {
988            return 1;
989        }
990        if (!isGB) {
991            doGB();
992        }
993        if (this.isONE()) {
994            return -1;
995        }
996        return bb.commonZeroTest(getList());
997    }
998
999
1000    /**
1001     * Test if this ideal is maximal.
1002     * @return true, if this is certainly maximal and not one, 
1003     *         false, if this is one, has dimension &ge; 1 or it is not jet determined if it is maximal.
1004     */
1005    public boolean isMaximal() {
1006        if (commonZeroTest() != 0) {
1007            return false;
1008        }
1009        for (Long d : univariateDegrees()) {
1010            if (d > 1L) {
1011                return false;
1012            }
1013        }
1014        return true;
1015    }
1016
1017
1018    /**
1019     * Univariate head term degrees.
1020     * @return a list of the degrees of univariate head terms.
1021     */
1022    public List<Long> univariateDegrees() {
1023        List<Long> ud = new ArrayList<Long>();
1024        if (this.isZERO()) {
1025            return ud;
1026        }
1027        if (!isGB) {
1028            doGB();
1029        }
1030        if (this.isONE()) {
1031            return ud;
1032        }
1033        return bb.univariateDegrees(getList());
1034    }
1035
1036
1037    /*
1038     * Construct univariate polynomials of minimal degree in all variables in
1039     * zero dimensional ideal(G).
1040     * @return list of univariate word polynomial of minimal degree in each
1041     *         variable in ideal(G)
1042     */
1043
1044
1045    /*
1046     * Construct univariate polynomial of minimal degree in variable i in zero
1047     * dimensional ideal(G).
1048     * @param i variable index.
1049     * @return univariate word polynomial of minimal degree in variable i in
1050     *         ideal(G)
1051     */
1052
1053}