001/*
002 * $Id: WordIdeal.java 5876 2018-07-25 10:17:18Z kredel $
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.Logger;
017import org.apache.logging.log4j.LogManager; 
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.Word;
028import edu.jas.poly.PolyUtil;
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("GB 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
774        WordIdeal<C> I = this.intersect(Hi);
775
776        List<GenWordPolynomial<C>> Q;
777        Q = new ArrayList<GenWordPolynomial<C>>(I.getList().size());
778        for (GenWordPolynomial<C> q : I.getList()) {
779            q = (GenWordPolynomial<C>) q.divide(h); // remainder == 0
780            Q.add(q);
781        }
782        return new WordIdeal<C>(getRing(), Q, true);
783    }
784     */
785
786
787    /*
788     * Quotient. Generators for the word ideal quotient.
789     * @param H word ideal
790     * @return ideal(this : H), a Groebner base
791    public WordIdeal<C> quotient(WordIdeal<C> H) {
792        if (H == null || H.isZERO()) { // == (0)
793            return this;
794        }
795        if (this.isZERO()) {
796            return this;
797        }
798        WordIdeal<C> Q = null;
799        for (GenWordPolynomial<C> h : H.getList()) {
800            WordIdeal<C> Hi = this.quotient(h);
801            if (Q == null) {
802                Q = Hi;
803            } else {
804                Q = Q.intersect(Hi);
805            }
806        }
807        return Q;
808    }
809     */
810
811
812    /**
813     * Power. Generators for the power of this word ideal. Note: if this
814     * ideal is a Groebner base, a Groebner base is returned.
815     * @param d integer
816     * @return ideal(this^d)
817     */
818    public WordIdeal<C> power(int d) {
819        if (d <= 0) {
820            return getONE();
821        }
822        if (this.isZERO() || this.isONE()) {
823            return this;
824        }
825        WordIdeal<C> c = this;
826        for (int i = 1; i < d; i++) {
827            c = c.product(this);
828        }
829        return c;
830    }
831
832
833    /**
834     * Normalform for element.
835     * @param h word polynomial
836     * @return left normalform of h with respect to this
837     */
838    public GenWordPolynomial<C> normalform(GenWordPolynomial<C> h) {
839        if (h == null) {
840            return h;
841        }
842        if (h.isZERO()) {
843            return h;
844        }
845        if (this.isZERO()) {
846            return h;
847        }
848        GenWordPolynomial<C> r;
849        r = red.normalform(getList(), h);
850        return r;
851    }
852
853
854    /**
855     * Normalform for list of word elements.
856     * @param L word polynomial list
857     * @return list of left normalforms of the elements of L with respect to
858     *         this
859     */
860    public List<GenWordPolynomial<C>> normalform(List<GenWordPolynomial<C>> L) {
861        if (L == null) {
862            return L;
863        }
864        if (L.size() == 0) {
865            return L;
866        }
867        if (this.isZERO()) {
868            return L;
869        }
870        List<GenWordPolynomial<C>> M = new ArrayList<GenWordPolynomial<C>>(L.size());
871        for (GenWordPolynomial<C> h : L) {
872            GenWordPolynomial<C> r = normalform(h);
873            if (r != null && !r.isZERO()) {
874                M.add(r);
875            }
876        }
877        return M;
878    }
879
880
881    /**
882     * Inverse for element modulo this ideal.
883     * @param h word polynomial
884     * @return inverse of h with respect to this, if defined
885     */
886    public GenWordPolynomial<C> inverse(GenWordPolynomial<C> h) {
887        if (h == null || h.isZERO()) {
888            throw new NotInvertibleException("zero not invertible");
889        }
890        if (this.isZERO()) {
891            throw new NotInvertibleException("zero ideal");
892        }
893        if (h.isUnit()) {
894            return h.inverse();
895        }
896        throw new UnsupportedOperationException("inverse of " + h);
897        /* TODO together with isUnit
898        doGB();
899        List<GenWordPolynomial<C>> F = new ArrayList<GenWordPolynomial<C>>(1 + list.list.size());
900        F.add(h);
901        F.addAll(getList());
902        //System.out.println("F = " + F);
903        WordExtendedGB<C> x = bb.extLeftGB(F);
904        List<GenWordPolynomial<C>> G = x.G;
905        //System.out.println("G = " + G);
906        GenWordPolynomial<C> one = null;
907        int i = -1;
908        for (GenWordPolynomial<C> p : G) {
909            i++;
910            if (p == null) {
911                continue;
912            }
913            if (p.isUnit()) {
914                one = p;
915                break;
916            }
917        }
918        if (one == null) {
919            throw new NotInvertibleException("one == null: h = " + h);
920        }
921        List<GenWordPolynomial<C>> row = x.G2F.get(i); // != -1
922        //System.out.println("row = " + row);
923        GenWordPolynomial<C> g = row.get(0);
924        if (g == null || g.isZERO()) {
925            throw new NotInvertibleException("g == 0: h = " + h);
926        }
927        GenWordPolynomial<C> gp = red.leftNormalform(getList(), g);
928        if (gp.isZERO()) { // can happen with solvable rings
929            throw new NotInvertibleException("solv|gp == 0: h = " + h + ", g = " + g);
930        }
931        // adjust leading coefficient of g to get g*h == 1
932        GenWordPolynomial<C> f = g.multiply(h);
933        //System.out.println("f = " + f);
934        GenWordPolynomial<C> k = red.leftNormalform(getList(), f);
935        //System.out.println("k = " + k);
936        if (!k.isONE()) {
937            C lbc = k.leadingBaseCoefficient();
938            lbc = lbc.inverse();
939            g = g.multiply(lbc);
940        }
941        return g;
942        */
943    }
944
945
946    /**
947     * Test if element is a unit modulo this ideal.
948     * @param h word polynomial
949     * @return true if h is a unit with respect to this, else false
950     */
951    public boolean isUnit(GenWordPolynomial<C> h) {
952        if (h == null || h.isZERO()) {
953            return false;
954        }
955        if (this.isZERO()) {
956            return false;
957        }
958        if (h.isUnit()) {
959            return true;
960        }
961        /* TODO together with inverse
962        // test this + (h) == 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        */
979        return false;
980    }
981
982
983    /**
984     * Ideal common zero test.
985     * @return -1, 0 or 1 if dimension(this) &eq; -1, 0 or &ge; 1.
986     */
987    public int commonZeroTest() {
988        if (this.isZERO()) {
989            return 1;
990        }
991        if (!isGB) {
992            doGB();
993        }
994        if (this.isONE()) {
995            return -1;
996        }
997        return bb.commonZeroTest(getList());
998    }
999
1000
1001    /**
1002     * Test if this ideal is maximal.
1003     * @return true, if this is maximal and not one, else false.
1004     */
1005    public boolean isMaximal() {
1006        if (commonZeroTest() != 0) {
1007            return false;
1008        }
1009        for (Long d : univariateDegrees()) {
1010            if (d > 1L) {
1011                // todo: test if univariate irreducible and no multiple polynomials
1012                return false;
1013            }
1014        }
1015        return true;
1016    }
1017
1018
1019    /**
1020     * Univariate head term degrees.
1021     * @return a list of the degrees of univariate head terms.
1022     */
1023    public List<Long> univariateDegrees() {
1024        List<Long> ud = new ArrayList<Long>();
1025        if (this.isZERO()) {
1026            return ud;
1027        }
1028        if (!isGB) {
1029            doGB();
1030        }
1031        if (this.isONE()) {
1032            return ud;
1033        }
1034        return bb.univariateDegrees(getList());
1035    }
1036
1037
1038    /*
1039     * Construct univariate polynomials of minimal degree in all variables in
1040     * zero dimensional ideal(G).
1041     * @return list of univariate word polynomial of minimal degree in each
1042     *         variable in ideal(G)
1043    public List<GenWordPolynomial<C>> constructUnivariate() {
1044        List<GenWordPolynomial<C>> univs = new ArrayList<GenWordPolynomial<C>>();
1045        for (int i = ring.alphabet.length() - 1; i >= 0; i--) {
1046            GenWordPolynomial<C> u = constructUnivariate(i);
1047            univs.add(u);
1048        }
1049        return univs;
1050    }
1051     */
1052
1053
1054    /*
1055     * Construct univariate polynomial of minimal degree in variable i in zero
1056     * dimensional ideal(G).
1057     * @param i variable index.
1058     * @return univariate word polynomial of minimal degree in variable i in
1059     *         ideal(G)
1060    public GenWordPolynomial<C> constructUnivariate(int i) {
1061        doGB();
1062        return bb.constructUnivariate(i, getList());
1063    }
1064     */
1065
1066}