001/*
002 * $Id$
003 */
004
005package edu.jas.arith;
006
007
008import java.io.Reader;
009import java.io.StringReader;
010import java.util.ArrayList;
011import java.util.List;
012import java.util.Random;
013import java.util.SortedMap;
014import java.util.TreeMap;
015
016import org.apache.logging.log4j.Logger;
017import org.apache.logging.log4j.LogManager; 
018
019import edu.jas.structure.RingElem;
020import edu.jas.structure.RingFactory;
021
022
023/**
024 * Direct product ring factory based on RingElem and RingFactory module. Objects
025 * of this class are <b>mutable</b>.
026 * @author Heinz Kredel
027 */
028public class ProductRing<C extends RingElem<C>> implements RingFactory<Product<C>> {
029
030
031    private static final Logger logger = LogManager.getLogger(ProductRing.class);
032
033
034    //private static final boolean debug = logger.isDebugEnabled();
035
036
037    /**
038     * Ring factory is n copies.
039     */
040    protected int nCopies;
041
042
043    /**
044     * One Ring factory.
045     */
046    protected final RingFactory<C> ring;
047
048
049    /**
050     * Ring factory list.
051     */
052    protected final List<RingFactory<C>> ringList;
053
054
055    /**
056     * A default random sequence generator.
057     */
058    protected final static Random random = new Random();
059
060
061    /**
062     * The constructor creates a ProductRing object from an ring factory and a
063     * modul.
064     * @param r ring factory.
065     * @param n number of copies.
066     */
067    public ProductRing(RingFactory<C> r, int n) {
068        ring = r;
069        nCopies = n;
070        ringList = null;
071    }
072
073
074    /**
075     * The constructor creates a ProductRing object from an ring factory and a
076     * modul.
077     * @param l list of ring factories.
078     */
079    public ProductRing(List<RingFactory<C>> l) {
080        ringList = l;
081        ring = null;
082        nCopies = 0;
083    }
084
085
086    /**
087     * Get ring factory at index i.
088     * @param i index.
089     * @return RingFactory_i.
090     */
091    public RingFactory<C> getFactory(int i) {
092        if (nCopies != 0) {
093            if (0 <= i && i < nCopies) {
094                return ring;
095            }
096            logger.info("index: {}", i);
097            throw new IllegalArgumentException("index out of bound " + this);
098        }
099        return ringList.get(i);
100    }
101
102
103    /**
104     * Add a ring factory.
105     * @param rf new ring factory.
106     */
107    public synchronized void addFactory(RingFactory<C> rf) {
108        if (nCopies != 0) {
109            if (ring.equals(rf)) {
110                nCopies++;
111                return;
112            }
113            throw new IllegalArgumentException("wrong RingFactory: " + rf + " != " + ring);
114        }
115        ringList.add(rf);
116    }
117
118
119    /**
120     * Contains a ring factory.
121     * @param rf ring factory.
122     * @return true, if rf is contained in this, else false.
123     */
124    public boolean containsFactory(RingFactory<C> rf) {
125        if (nCopies != 0) {
126            if (ring.equals(rf)) {
127                return true;
128            }
129            return false; // misleading
130        }
131        return ringList.contains(rf);
132    }
133
134
135    /**
136     * Is this structure finite or infinite.
137     * @return true if this structure is finite, else false.
138     * @see edu.jas.structure.ElemFactory#isFinite()
139     */
140    public boolean isFinite() {
141        if (nCopies != 0) {
142            return ring.isFinite();
143        }
144        for (RingFactory<C> f : ringList) {
145            boolean b = f.isFinite();
146            if (!b) {
147                return false;
148            }
149        }
150        return true;
151    }
152
153
154    /**
155     * Copy Product element c.
156     * @param c
157     * @return a copy of c.
158     */
159    public Product<C> copy(Product<C> c) {
160        return new Product<C>(c.ring, c.val, c.isunit);
161    }
162
163
164    /**
165     * Get the zero element.
166     * @return 0 as Product.
167     */
168    public Product<C> getZERO() {
169        return new Product<C>(this);
170    }
171
172
173    /**
174     * Get the one element.
175     * @return 1 as Product.
176     */
177    public Product<C> getONE() {
178        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
179        if (nCopies != 0) {
180            for (int i = 0; i < nCopies; i++) {
181                elem.put(i, ring.getONE());
182            }
183        } else {
184            int i = 0;
185            for (RingFactory<C> f : ringList) {
186                elem.put(i, f.getONE());
187                i++;
188            }
189        }
190        return new Product<C>(this, elem, 1);
191    }
192
193
194    /**
195     * Get a list of the generating elements.
196     * @return list of generators for the algebraic structure.
197     * @see edu.jas.structure.ElemFactory#generators()
198     */
199    public List<Product<C>> generators() {
200        List<Product<C>> gens = new ArrayList<Product<C>>(/*nCopies*ring.generators.size()*/);
201        int n = nCopies;
202        if (n == 0) {
203            n = ringList.size();
204        }
205        for (int i = 0; i < n; i++) {
206            //System.out.println("i = " + i + ", n = " + n);
207            RingFactory<C> f = getFactory(i);
208            List<? extends C> rgens = f.generators();
209            for (C c : rgens) {
210                SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
211                elem.put(i, c);
212                Product<C> g = new Product<C>(this, elem);
213                //g = g.fillOne();
214                gens.add(g);
215            }
216        }
217        return gens;
218    }
219
220
221    /**
222     * Get an atomic element.
223     * @param i index.
224     * @return e_i as Product.
225     */
226    public Product<C> getAtomic(int i) {
227        if (i < 0 || i >= length()) {
228            throw new IllegalArgumentException("index out of bounds " + i);
229        }
230        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
231        if (nCopies != 0) {
232            elem.put(i, ring.getONE());
233        } else {
234            RingFactory<C> f = ringList.get(i);
235            elem.put(i, f.getONE());
236        }
237        return new Product<C>(this, elem, 1);
238    }
239
240
241    /**
242     * Get the number of factors of this ring.
243     * @return nCopies or ringList.size().
244     */
245    public int length() {
246        if (nCopies != 0) {
247            return nCopies;
248        }
249        return ringList.size();
250    }
251
252
253    /**
254     * Query if this ring is commutative.
255     * @return true if this ring is commutative, else false.
256     */
257    public boolean isCommutative() {
258        if (nCopies != 0) {
259            return ring.isCommutative();
260        }
261        for (RingFactory<C> f : ringList) {
262            if (!f.isCommutative()) {
263                return false;
264            }
265        }
266        return true;
267    }
268
269
270    /**
271     * Query if this ring is associative.
272     * @return true if this ring is associative, else false.
273     */
274    public boolean isAssociative() {
275        if (nCopies != 0) {
276            return ring.isAssociative();
277        }
278        for (RingFactory<C> f : ringList) {
279            if (!f.isAssociative()) {
280                return false;
281            }
282        }
283        return true;
284    }
285
286
287    /**
288     * Query if this ring is a field.
289     * @return true or false.
290     */
291    public boolean isField() {
292        if (nCopies != 0) {
293            if (nCopies == 1) {
294                return ring.isField();
295            }
296        } else {
297            if (ringList.size() == 1) {
298                return ringList.get(0).isField();
299            }
300        }
301        return false;
302    }
303
304
305    /**
306     * Query if this ring consists only of fields.
307     * @return true or false.
308     */
309    public boolean onlyFields() {
310        if (nCopies != 0) {
311            return ring.isField();
312        }
313        for (RingFactory<C> f : ringList) {
314            if (!f.isField()) {
315                return false;
316            }
317        }
318        return true;
319    }
320
321
322    /**
323     * Characteristic of this ring.
324     * @return minimal characteristic of ring component.
325     */
326    public java.math.BigInteger characteristic() {
327        if (nCopies != 0) {
328            return ring.characteristic();
329        }
330        java.math.BigInteger c = null;
331        java.math.BigInteger d;
332        for (RingFactory<C> f : ringList) {
333            if (c == null) {
334                c = f.characteristic();
335            } else {
336                d = f.characteristic();
337                if (c.compareTo(d) > 0) { // c > d
338                    c = d;
339                }
340            }
341        }
342        return c;
343    }
344
345
346    /**
347     * Get a Product element from a BigInteger value.
348     * @param a BigInteger.
349     * @return a Product.
350     */
351    public Product<C> fromInteger(java.math.BigInteger a) {
352        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
353        if (nCopies != 0) {
354            C c = ring.fromInteger(a);
355            for (int i = 0; i < nCopies; i++) {
356                elem.put(i, c);
357            }
358        } else {
359            int i = 0;
360            for (RingFactory<C> f : ringList) {
361                elem.put(i, f.fromInteger(a));
362                i++;
363            }
364        }
365        return new Product<C>(this, elem);
366    }
367
368
369    /**
370     * Get a Product element from a long value.
371     * @param a long.
372     * @return a Product.
373     */
374    public Product<C> fromInteger(long a) {
375        return fromInteger(new java.math.BigInteger("" + a));
376    }
377
378
379    /**
380     * Get the String representation as RingFactory.
381     * @see java.lang.Object#toString()
382     */
383    @Override
384    public String toString() {
385        if (nCopies != 0) {
386            String cf = ring.toString();
387            if (cf.matches("[0-9].*")) {
388                cf = ring.getClass().getSimpleName();
389            }
390            return "ProductRing[ " + cf + "^" + nCopies + " ]";
391        }
392        StringBuffer sb = new StringBuffer("ProductRing[ ");
393        int i = 0;
394        for (RingFactory<C> f : ringList) {
395            if (i != 0) {
396                sb.append(", ");
397            }
398            String cf = f.toString();
399            if (cf.matches("[0-9].*")) {
400                cf = f.getClass().getSimpleName();
401            }
402            sb.append(cf);
403            i++;
404        }
405        sb.append(" ]");
406        return sb.toString();
407    }
408
409
410    /**
411     * Get a scripting compatible string representation.
412     * @return script compatible representation for this ElemFactory.
413     * @see edu.jas.structure.ElemFactory#toScript()
414     */
415    @Override
416    public String toScript() {
417        // Python case
418        StringBuffer s = new StringBuffer("RR( [ ");
419        for (int i = 0; i < length(); i++) {
420            if (i > 0) {
421                s.append(", ");
422            }
423            RingFactory<C> v = getFactory(i);
424            String f = null;
425            try {
426                f = ((RingElem<C>) v).toScriptFactory(); // sic
427            } catch (Exception e) {
428                f = v.toScript();
429            }
430            s.append(f);
431        }
432        s.append(" ] )");
433        return s.toString();
434    }
435
436
437    /**
438     * Comparison with any other object.
439     * @see java.lang.Object#equals(java.lang.Object)
440     */
441    @Override
442    @SuppressWarnings("unchecked")
443    public boolean equals(Object b) {
444        if (b == null) {
445            return false;
446        }
447        if (!(b instanceof ProductRing)) {
448            return false;
449        }
450        ProductRing<C> a = (ProductRing<C>) b;
451        if (nCopies != 0) {
452            if (nCopies != a.nCopies || !ring.equals(a.ring)) {
453                return false;
454            }
455        } else {
456            if (ringList.size() != a.ringList.size()) {
457                return false;
458            }
459            int i = 0;
460            for (RingFactory<C> f : ringList) {
461                if (!f.equals(a.ringList.get(i))) {
462                    return false;
463                }
464                i++;
465            }
466        }
467        return true;
468    }
469
470
471    /**
472     * Hash code for this product ring.
473     * @see java.lang.Object#hashCode()
474     */
475    @Override
476    public int hashCode() {
477        int h = 0;
478        if (nCopies != 0) {
479            h = ring.hashCode();
480            h = 37 * h + nCopies;
481        } else {
482            for (RingFactory<C> f : ringList) {
483                h = 37 * h + f.hashCode();
484            }
485        }
486        return h;
487    }
488
489
490    /**
491     * Product random.
492     * @param n such that 0 &le; v &le; (2<sup>n</sup>-1).
493     * @return a random product element v.
494     */
495    public Product<C> random(int n) {
496        return random(n, 0.5f);
497    }
498
499
500    /**
501     * Product random.
502     * @param n such that 0 &le; v &le; (2<sup>n</sup>-1).
503     * @param q density of nozero entries.
504     * @return a random product element v.
505     */
506    public Product<C> random(int n, float q) {
507        return random(n, q, random);
508    }
509
510
511    /**
512     * Product random.
513     * @param n such that 0 &le; v &le; (2<sup>n</sup>-1).
514     * @param rnd is a source for random bits.
515     * @return a random product element v.
516     */
517    public Product<C> random(int n, Random rnd) {
518        return random(n, 0.5f, random);
519    }
520
521
522    /**
523     * Product random.
524     * @param n such that 0 &le; v &le; (2<sup>n</sup>-1).
525     * @param q density of nozero entries.
526     * @param rnd is a source for random bits.
527     * @return a random product element v.
528     */
529    public Product<C> random(int n, float q, Random rnd) {
530        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
531        float d;
532        if (nCopies != 0) {
533            for (int i = 0; i < nCopies; i++) {
534                d = rnd.nextFloat();
535                if (d < q) {
536                    C r = ring.random(n, rnd);
537                    if (!r.isZERO()) {
538                        elem.put(i, r);
539                    }
540                }
541            }
542        } else {
543            int i = 0;
544            for (RingFactory<C> f : ringList) {
545                d = rnd.nextFloat();
546                if (d < q) {
547                    C r = f.random(n, rnd);
548                    if (!r.isZERO()) {
549                        elem.put(i, r);
550                    }
551                }
552                i++;
553            }
554        }
555        return new Product<C>(this, elem);
556    }
557
558
559    /**
560     * Parse Product from String.
561     * @param s String.
562     * @return Product from s.
563     */
564    public Product<C> parse(String s) {
565        StringReader sr = new StringReader(s);
566        return parse(sr);
567    }
568
569
570    /**
571     * Parse Product from Reader. Syntax: p1 ... pn (no commas)
572     * @param r Reader.
573     * @return next Product from r.
574     */
575    public Product<C> parse(Reader r) {
576        SortedMap<Integer, C> elem = new TreeMap<Integer, C>();
577        if (nCopies != 0) {
578            for (int i = 0; i < nCopies; i++) {
579                elem.put(i, ring.parse(r));
580            }
581        } else {
582            int i = 0;
583            for (RingFactory<C> f : ringList) {
584                elem.put(i, f.parse(r));
585                i++;
586            }
587        }
588        return new Product<C>(this, elem);
589    }
590
591}