001/*
002 * $Id: Product.java 4125 2012-08-19 19:05:22Z kredel $
003 */
004
005package edu.jas.arith;
006
007
008import java.util.Iterator;
009import java.util.Map;
010import java.util.SortedMap;
011import java.util.TreeMap;
012
013import org.apache.log4j.Logger;
014
015import edu.jas.structure.NotInvertibleException;
016import edu.jas.structure.RegularRingElem;
017import edu.jas.structure.RingElem;
018import edu.jas.structure.RingFactory;
019
020
021/**
022 * Direct product element based on RingElem. Objects of this class are (nearly)
023 * immutable.
024 * @author Heinz Kredel
025 */
026public class Product<C extends RingElem<C>> implements RegularRingElem<Product<C>> {
027
028
029    private static final Logger logger = Logger.getLogger(Product.class);
030
031
032    //private final boolean debug = logger.isDebugEnabled();
033
034
035    /**
036     * Product class factory data structure.
037     */
038    public final ProductRing<C> ring;
039
040
041    /**
042     * Value part of the element data structure.
043     */
044    public final SortedMap<Integer, C> val;
045
046
047    /**
048     * Flag to remember if this product element is a unit in each cmponent. -1
049     * is unknown, 1 is unit, 0 not a unit.
050     */
051    protected int isunit = -1; // initially unknown
052
053
054    /**
055     * The constructor creates a Product object from a ring factory.
056     * @param r ring factory.
057     */
058    public Product(ProductRing<C> r) {
059        this(r, new TreeMap<Integer, C>(), 0);
060    }
061
062
063    /**
064     * The constructor creates a Product object from a ring factory and a ring
065     * element.
066     * @param r ring factory.
067     * @param a ring element.
068     */
069    public Product(ProductRing<C> r, SortedMap<Integer, C> a) {
070        this(r, a, -1);
071    }
072
073
074    /**
075     * The constructor creates a Product object from a ring factory, a ring
076     * element and an indicator if a is a unit.
077     * @param r ring factory.
078     * @param a ring element.
079     * @param u isunit indicator, -1, 0, 1.
080     */
081    public Product(ProductRing<C> r, SortedMap<Integer, C> a, int u) {
082        ring = r;
083        val = a;
084        isunit = u;
085    }
086
087
088    /**
089     * Get component.
090     * @param i index of component.
091     * @return val(i).
092     */
093    public C get(int i) {
094        return val.get(i); // auto-boxing
095    }
096
097
098    /**
099     * Get the corresponding element factory.
100     * @return factory for this Element.
101     * @see edu.jas.structure.Element#factory()
102     */
103    public ProductRing<C> factory() {
104        return ring;
105    }
106
107
108    /**
109     * Clone this.
110     * @see java.lang.Object#clone()
111     */
112    @Override
113    public Product<C> copy() {
114        return new Product<C>(ring, val, isunit);
115    }
116
117
118    /**
119     * Is Product zero.
120     * @return If this is 0 then true is returned, else false.
121     * @see edu.jas.structure.RingElem#isZERO()
122     */
123    public boolean isZERO() {
124        return val.size() == 0;
125    }
126
127
128    /**
129     * Is Product one.
130     * @return If this is 1 then true is returned, else false.
131     * @see edu.jas.structure.RingElem#isONE()
132     */
133    public boolean isONE() {
134        if (val.size() != ring.length()) {
135            return false;
136        }
137        for (C e : val.values()) {
138            if (!e.isONE()) {
139                return false;
140            }
141        }
142        return true;
143    }
144
145
146    /**
147     * Is Product full.
148     * @return If every component is non-zero, then true is returned, else
149     *         false.
150     */
151    public boolean isFull() {
152        if (val.size() != ring.length()) {
153            return false;
154        }
155        return true;
156    }
157
158
159    /**
160     * Is Product unit.
161     * @return If this is a unit then true is returned, else false.
162     * @see edu.jas.structure.RingElem#isUnit()
163     */
164    public boolean isUnit() {
165        if (isunit > 0) {
166            return true;
167        }
168        if (isunit == 0) {
169            return false;
170        }
171        if (isZERO()) {
172            isunit = 0;
173            return false;
174        }
175        for (C e : val.values()) {
176            if (!e.isUnit()) {
177                isunit = 0;
178                return false;
179            }
180        }
181        isunit = 1;
182        return true;
183    }
184
185
186    /**
187     * Is Product idempotent.
188     * @return If this is a idempotent element then true is returned, else
189     *         false.
190     */
191    public boolean isIdempotent() {
192        for (C e : val.values()) {
193            if (!e.isONE()) {
194                return false;
195            }
196        }
197        return true;
198    }
199
200
201    /**
202     * Get the String representation as RingElem.
203     * @see java.lang.Object#toString()
204     */
205    @Override
206    public String toString() {
207        return val.toString();
208    }
209
210
211    /**
212     * Get a scripting compatible string representation.
213     * @return script compatible representation for this Element.
214     * @see edu.jas.structure.Element#toScript()
215     */
216    //JAVA6only: @Override
217    public String toScript() {
218        // Python case
219        StringBuffer s = new StringBuffer("( ");
220        boolean first = true;
221        for (Integer i : val.keySet()) {
222            C v = val.get(i);
223            if (first) {
224                first = false;
225            } else {
226                if (v.signum() < 0) {
227                    s.append(" - ");
228                    v = v.negate();
229                } else {
230                    s.append(" + ");
231                }
232            }
233            if (!v.isONE()) {
234                s.append(v.toScript() + "*");
235            }
236            s.append("pg" + i);
237        }
238        s.append(" )");
239        return s.toString();
240    }
241
242
243    /**
244     * Get a scripting compatible string representation of the factory.
245     * @return script compatible representation for this ElemFactory.
246     * @see edu.jas.structure.Element#toScriptFactory()
247     */
248    //JAVA6only: @Override
249    public String toScriptFactory() {
250        // Python case
251        return factory().toScript();
252    }
253
254
255    /**
256     * Product comparison.
257     * @param b Product.
258     * @return sign(this-b).
259     */
260    //JAVA6only: @Override
261    public int compareTo(Product<C> b) {
262        if (!ring.equals(b.ring)) {
263            logger.info("other ring " + b.ring);
264            throw new IllegalArgumentException("rings not comparable " + this);
265        }
266        SortedMap<Integer, C> v = b.val;
267        Iterator<Map.Entry<Integer, C>> ti = val.entrySet().iterator();
268        Iterator<Map.Entry<Integer, C>> bi = v.entrySet().iterator();
269        int s;
270        while (ti.hasNext() && bi.hasNext()) {
271            Map.Entry<Integer, C> te = ti.next();
272            Map.Entry<Integer, C> be = bi.next();
273            s = te.getKey().compareTo(be.getKey());
274            if (s != 0) {
275                return s;
276            }
277            s = te.getValue().compareTo(be.getValue());
278            if (s != 0) {
279                return s;
280            }
281        }
282        if (ti.hasNext()) {
283            return -1;
284        }
285        if (bi.hasNext()) {
286            return 1;
287        }
288        return 0;
289    }
290
291
292    /**
293     * Comparison with any other object.
294     * @see java.lang.Object#equals(java.lang.Object)
295     */
296    @SuppressWarnings("unchecked")
297    @Override
298    public boolean equals(Object b) {
299        if (!(b instanceof Product)) {
300            return false;
301        }
302        Product<C> a = null;
303        try {
304            a = (Product<C>) b;
305        } catch (ClassCastException e) {
306        }
307        if (a == null) {
308            return false;
309        }
310        return (0 == compareTo(a));
311    }
312
313
314    /**
315     * Hash code for this local.
316     * @see java.lang.Object#hashCode()
317     */
318    @Override
319    public int hashCode() {
320        int h = ring.hashCode();
321        h = 37 * h + val.hashCode();
322        return h;
323    }
324
325
326    /**
327     * Product extend. Add new component j with value of component i.
328     * @param i from index.
329     * @param j to index.
330     * @return the extended value of this.
331     */
332    public Product<C> extend(int i, int j) {
333        RingFactory<C> rf = ring.getFactory(j);
334        SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val);
335        C v = val.get(i);
336        C w = rf.copy(v); // valueOf
337        if (!w.isZERO()) {
338            elem.put(j, w);
339        }
340        return new Product<C>(ring, elem, isunit);
341    }
342
343
344    /**
345     * Product absolute value.
346     * @return the absolute value of this.
347     * @see edu.jas.structure.RingElem#abs()
348     */
349    public Product<C> abs() {
350        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
351        for (Integer i : val.keySet()) {
352            C v = val.get(i).abs();
353            elem.put(i, v);
354        }
355        return new Product<C>(ring, elem, isunit);
356    }
357
358
359    /**
360     * Product summation.
361     * @param S Product.
362     * @return this+S.
363     */
364    public Product<C> sum(Product<C> S) {
365        if (S == null || S.isZERO()) {
366            return this;
367        }
368        if (this.isZERO()) {
369            return S;
370        }
371        SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone
372        SortedMap<Integer, C> sel = S.val;
373        for (Map.Entry<Integer, C> is : sel.entrySet()) {
374            Integer i = is.getKey();
375            C x = elem.get(i);
376            C y = is.getValue(); //sel.get( i ); // assert y != null
377            if (x != null) {
378                x = x.sum(y);
379                if (!x.isZERO()) {
380                    elem.put(i, x);
381                } else {
382                    elem.remove(i);
383                }
384            } else {
385                elem.put(i, y);
386            }
387        }
388        return new Product<C>(ring, elem);
389    }
390
391
392    /**
393     * Product negate.
394     * @return -this.
395     * @see edu.jas.structure.RingElem#negate()
396     */
397    public Product<C> negate() {
398        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
399        for (Integer i : val.keySet()) {
400            C v = val.get(i).negate();
401            elem.put(i, v);
402        }
403        return new Product<C>(ring, elem, isunit);
404    }
405
406
407    /**
408     * Product signum.
409     * @see edu.jas.structure.RingElem#signum()
410     * @return signum of first non-zero component.
411     */
412    public int signum() {
413        if (val.size() == 0) {
414            return 0;
415        }
416        C v = val.get(val.firstKey());
417        return v.signum();
418    }
419
420
421    /**
422     * Product subtraction.
423     * @param S Product.
424     * @return this-S.
425     */
426    public Product<C> subtract(Product<C> S) {
427        return sum(S.negate());
428    }
429
430
431    /**
432     * Product quasi-inverse.
433     * @see edu.jas.structure.RingElem#inverse()
434     * @return S with S = 1/this if defined.
435     */
436    public Product<C> inverse() {
437        if (this.isZERO()) {
438            return this;
439        }
440        int isu = 0;
441        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
442        for (Integer i : val.keySet()) {
443            C x = val.get(i); // is non zero
444            try {
445                x = x.inverse();
446            } catch (NotInvertibleException e) {
447                // could happen for e.g. ModInteger or AlgebraicNumber
448                x = null; //ring.getFactory(i).getZERO();
449            }
450            if (x != null && !x.isZERO()) { // can happen
451                elem.put(i, x);
452                isu = 1;
453            }
454        }
455        return new Product<C>(ring, elem, isu);
456    }
457
458
459    /**
460     * Product idempotent.
461     * @return smallest S with this*S = this.
462     */
463    public Product<C> idempotent() {
464        if (this.isZERO()) {
465            return this;
466        }
467        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
468        for (Integer i : val.keySet()) {
469            RingFactory<C> f = ring.getFactory(i);
470            C x = f.getONE();
471            elem.put(i, x);
472        }
473        return new Product<C>(ring, elem, 1);
474    }
475
476
477    /**
478     * Product idempotent complement.
479     * @return 1-this.idempotent().
480     */
481    public Product<C> idemComplement() {
482        if (this.isZERO()) {
483            return ring.getONE();
484        }
485        int isu = 0;
486        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
487        for (int i = 0; i < ring.length(); i++) {
488            C v = val.get(i);
489            if (v == null) {
490                RingFactory<C> f = ring.getFactory(i);
491                C x = f.getONE();
492                elem.put(i, x);
493                isu = 1;
494            }
495        }
496        return new Product<C>(ring, elem, isu);
497    }
498
499
500    /**
501     * Product idempotent and.
502     * @param S Product.
503     * @return this.idempotent() and S.idempotent().
504     */
505    public Product<C> idempotentAnd(Product<C> S) {
506        if (this.isZERO() && S.isZERO()) {
507            return this;
508        }
509        int isu = 0;
510        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
511        for (int i = 0; i < ring.length(); i++) {
512            C v = val.get(i);
513            C w = S.val.get(i);
514            if (v != null && w != null) {
515                RingFactory<C> f = ring.getFactory(i);
516                C x = f.getONE();
517                elem.put(i, x);
518                isu = 1;
519            }
520        }
521        return new Product<C>(ring, elem, isu);
522    }
523
524
525    /**
526     * Product idempotent or.
527     * @param S Product.
528     * @return this.idempotent() or S.idempotent().
529     */
530    public Product<C> idempotentOr(Product<C> S) {
531        if (this.isZERO() && S.isZERO()) {
532            return this;
533        }
534        int isu = 0;
535        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
536        for (int i = 0; i < ring.length(); i++) {
537            C v = val.get(i);
538            C w = S.val.get(i);
539            if (v != null || w != null) {
540                RingFactory<C> f = ring.getFactory(i);
541                C x = f.getONE();
542                elem.put(i, x);
543                isu = 1;
544            }
545        }
546        return new Product<C>(ring, elem, isu);
547    }
548
549
550    /**
551     * Product fill with idempotent.
552     * @param S Product.
553     * @return fill this with S.idempotent().
554     */
555    public Product<C> fillIdempotent(Product<C> S) {
556        if (S.isZERO()) {
557            return this;
558        }
559        SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val);
560        for (int i = 0; i < ring.length(); i++) {
561            C v = elem.get(i);
562            if (v != null) {
563                continue;
564            }
565            C w = S.val.get(i);
566            if (w != null) {
567                RingFactory<C> f = ring.getFactory(i);
568                C x = f.getONE();
569                elem.put(i, x);
570            }
571        }
572        return new Product<C>(ring, elem, isunit);
573    }
574
575
576    /**
577     * Product fill with one.
578     * @return fill this with one.
579     */
580    public Product<C> fillOne() {
581        if (this.isFull()) {
582            return this;
583        }
584        if (this.isZERO()) {
585            return ring.getONE();
586        }
587        SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val);
588        for (int i = 0; i < ring.length(); i++) {
589            C v = elem.get(i);
590            if (v != null) {
591                continue;
592            }
593            RingFactory<C> f = ring.getFactory(i);
594            C x = f.getONE();
595            elem.put(i, x);
596        }
597        return new Product<C>(ring, elem, isunit);
598    }
599
600
601    /**
602     * Product quasi-division.
603     * @param S Product.
604     * @return this/S.
605     */
606    public Product<C> divide(Product<C> S) {
607        if (S == null) {
608            return ring.getZERO();
609        }
610        if (S.isZERO()) {
611            return S;
612        }
613        if (this.isZERO()) {
614            return this;
615        }
616        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
617        SortedMap<Integer, C> sel = S.val;
618        for (Integer i : val.keySet()) {
619            C y = sel.get(i);
620            if (y != null) {
621                C x = val.get(i);
622                try {
623                    x = x.divide(y);
624                } catch (NotInvertibleException e) {
625                    // should not happen any more
626                    System.out.println("product divide error: x = " + x + ", y = " + y);
627                    // could happen for e.g. ModInteger or AlgebraicNumber
628                    x = null; //ring.getFactory(i).getZERO();
629                }
630                if (x != null && !x.isZERO()) { // can happen
631                    elem.put(i, x);
632                }
633            }
634        }
635        return new Product<C>(ring, elem);
636    }
637
638
639    /**
640     * Product quasi-remainder.
641     * @param S Product.
642     * @return this - (this/S)*S.
643     */
644    public Product<C> remainder(Product<C> S) {
645        if (S == null) {
646            return this; //ring.getZERO();
647        }
648        if (S.isZERO()) {
649            return this;
650        }
651        if (this.isZERO()) {
652            return this;
653        }
654        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
655        SortedMap<Integer, C> sel = S.val;
656        for (Integer i : val.keySet()) {
657            C y = sel.get(i);
658            if (y != null) {
659                C x = val.get(i);
660                x = x.remainder(y);
661                if (x != null && !x.isZERO()) { // can happen
662                    elem.put(i, x);
663                }
664            }
665        }
666        return new Product<C>(ring, elem);
667    }
668
669
670    /**
671     * Product multiplication.
672     * @param S Product.
673     * @return this*S.
674     */
675    public Product<C> multiply(Product<C> S) {
676        if (S == null) {
677            return ring.getZERO();
678        }
679        if (S.isZERO()) {
680            return S;
681        }
682        if (this.isZERO()) {
683            return this;
684        }
685        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
686        SortedMap<Integer, C> sel = S.val;
687        for (Integer i : val.keySet()) {
688            C y = sel.get(i);
689            if (y != null) {
690                C x = val.get(i);
691                x = x.multiply(y);
692                if (x != null && !x.isZERO()) {
693                    elem.put(i, x);
694                }
695            }
696        }
697        return new Product<C>(ring, elem);
698    }
699
700
701    /**
702     * Product multiply by coefficient.
703     * @param c coefficient.
704     * @return this*c.
705     */
706    public Product<C> multiply(C c) {
707        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
708        for (Integer i : val.keySet()) {
709            C v = val.get(i).multiply(c);
710            if (v != null && !v.isZERO()) {
711                elem.put(i, v);
712            }
713        }
714        return new Product<C>(ring, elem);
715    }
716
717
718    /**
719     * Greatest common divisor.
720     * @param S other element.
721     * @return gcd(this,S).
722     */
723    public Product<C> gcd(Product<C> S) {
724        if (S == null || S.isZERO()) {
725            return this;
726        }
727        if (this.isZERO()) {
728            return S;
729        }
730        SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone
731        SortedMap<Integer, C> sel = S.val;
732        for (Map.Entry<Integer, C> is : sel.entrySet()) {
733            Integer i = is.getKey();
734            C x = elem.get(i);
735            C y = is.getValue(); //sel.get( i ); // assert y != null
736            if (x != null) {
737                x = x.gcd(y);
738                if (x != null && !x.isZERO()) {
739                    elem.put(i, x);
740                } else {
741                    elem.remove(i);
742                }
743            } else {
744                elem.put(i, y);
745            }
746        }
747        return new Product<C>(ring, elem);
748    }
749
750
751    /**
752     * Extended greatest common divisor.
753     * @param S other element.
754     * @return [ gcd(this,S), c1, c2 ] with c1*this + c2*b = gcd(this,S).
755     */
756    @SuppressWarnings("unchecked")
757    public Product<C>[] egcd(Product<C> S) {
758        Product<C>[] ret = (Product<C>[]) new Product[3];
759        ret[0] = null;
760        ret[1] = null;
761        ret[2] = null;
762        if (S == null || S.isZERO()) {
763            ret[0] = this;
764            return ret;
765        }
766        if (this.isZERO()) {
767            ret[0] = S;
768            return ret;
769        }
770        SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone
771        SortedMap<Integer, C> elem1 = this.idempotent().val; // init with 1
772        SortedMap<Integer, C> elem2 = new TreeMap<Integer, C>(); // zero
773        SortedMap<Integer, C> sel = S.val;
774        for (Map.Entry<Integer, C> is : sel.entrySet()) {
775            Integer i = is.getKey();
776            C x = elem.get(i);
777            C y = is.getValue(); //sel.get( i ); // assert y != null
778            if (x != null) {
779                C[] g = x.egcd(y);
780                if (!g[0].isZERO()) {
781                    elem.put(i, g[0]);
782                    elem1.put(i, g[1]);
783                    elem2.put(i, g[2]);
784                } else {
785                    elem.remove(i);
786                }
787            } else {
788                elem.put(i, y);
789                elem2.put(i, ring.getFactory(i).getONE());
790            }
791        }
792        ret[0] = new Product<C>(ring, elem);
793        ret[1] = new Product<C>(ring, elem1);
794        ret[2] = new Product<C>(ring, elem2);
795        return ret;
796    }
797
798}