001    /*
002     * $Id: MultiVarPowerSeries.java 3507 2011-01-26 22:23:58Z kredel $
003     */
004    
005    package edu.jas.ps;
006    
007    
008    import java.util.AbstractMap;
009    import java.util.HashMap;
010    import java.util.HashSet;
011    import java.util.BitSet;
012    import java.util.Iterator;
013    import java.util.Map;
014    import java.util.Set;
015    import java.util.List;
016    import java.util.TreeMap;
017    
018    import edu.jas.poly.ExpVector;
019    import edu.jas.poly.GenPolynomial;
020    import edu.jas.poly.AlgebraicNumber;
021    import edu.jas.structure.BinaryFunctor;
022    import edu.jas.structure.RingElem;
023    import edu.jas.structure.Selector;
024    import edu.jas.structure.UnaryFunctor;
025    import edu.jas.util.MapEntry;
026    
027    
028    /**
029     * Multivariate power series implementation. Uses inner classes and lazy
030     * evaluated generating function for coefficients. All ring element methods use
031     * lazy evaluation except where noted otherwise. Eager evaluated methods are
032     * <code>toString()</code>, <code>compareTo()</code>, <code>equals()</code>,
033     * <code>evaluate()</code>, or methods which use the <code>order()</code> or
034     * <code>orderExpVector()</code> methods, like <code>signum()</code>,
035     * <code>abs()</code>, <code>divide()</code>, <code>remainder()</code> and
036     * <code>gcd()</code>. <b>Note: </b> Currently the term order is fixed to the
037     * order defined by the iterator over exponent vectors in class
038     * <code>ExpVectorIterator</code>.
039     * @param <C> ring element type
040     * @author Heinz Kredel
041     */
042    
043    public class MultiVarPowerSeries<C extends RingElem<C>> implements RingElem<MultiVarPowerSeries<C>> {
044    
045    
046        /**
047         * Power series ring factory.
048         */
049        public final MultiVarPowerSeriesRing<C> ring;
050    
051    
052        /**
053         * Data structure / generating function for coeffcients. Cannot be final
054         * because of fixPoint, must be accessible in factory.
055         */
056        /*package*/MultiVarCoefficients<C> lazyCoeffs;
057    
058    
059        /**
060         * Truncation of computations.
061         */
062        private int truncate;
063    
064    
065        /**
066         * Order of power series.
067         */
068        private int order = -1; // == unknown
069    
070    
071        /**
072         * ExpVector of order of power series.
073         */
074        private ExpVector evorder = null; // == unknown
075    
076    
077        /**
078         * Private constructor.
079         */
080        @SuppressWarnings("unused")
081        private MultiVarPowerSeries() {
082            throw new IllegalArgumentException("do not use no-argument constructor");
083        }
084    
085    
086        /**
087         * Package constructor. Use in fixPoint only, must be accessible in factory.
088         * @param ring power series ring.
089         */
090        /*package*/MultiVarPowerSeries(MultiVarPowerSeriesRing<C> ring) {
091            this.ring = ring;
092            this.lazyCoeffs = null;
093            this.truncate = ring.truncate;
094        }
095    
096    
097        /**
098         * Constructor.
099         * @param ring power series ring.
100         * @param lazyCoeffs generating function for coefficients.
101         */
102        public MultiVarPowerSeries(MultiVarPowerSeriesRing<C> ring, MultiVarCoefficients<C> lazyCoeffs) {
103            this(ring, lazyCoeffs, ring.truncate);
104        }
105    
106    
107        /**
108         * Constructor.
109         * @param ring power series ring.
110         * @param lazyCoeffs generating function for coefficients.
111         * @param trunc truncate parameter for this power series.
112         */
113        public MultiVarPowerSeries(MultiVarPowerSeriesRing<C> ring, MultiVarCoefficients<C> lazyCoeffs, int trunc) {
114            if (lazyCoeffs == null || ring == null) {
115                throw new IllegalArgumentException("null not allowed: ring = " + ring + ", lazyCoeffs = "
116                        + lazyCoeffs);
117            }
118            this.ring = ring;
119            this.lazyCoeffs = lazyCoeffs;
120            this.truncate = Math.min(trunc,ring.truncate);
121        }
122    
123    
124        /**
125         * Get the corresponding element factory.
126         * @return factory for this Element.
127         * @see edu.jas.structure.Element#factory()
128         */
129        public MultiVarPowerSeriesRing<C> factory() {
130            return ring;
131        }
132    
133    
134        /**
135         * Clone this power series.
136         * @see java.lang.Object#clone()
137         */
138        @Override
139        public MultiVarPowerSeries<C> clone() {
140            return new MultiVarPowerSeries<C>(ring, lazyCoeffs);
141        }
142    
143    
144        /**
145         * String representation of power series.
146         * @see java.lang.Object#toString()
147         */
148        @Override
149        public String toString() {
150            return toString(truncate);
151        }
152    
153    
154        /**
155         * To String with given truncate.
156         * @param trunc truncate parameter for this power series.
157         * @return string representation of this to given truncate.
158         */
159        public String toString(int trunc) {
160            StringBuffer sb = new StringBuffer();
161            MultiVarPowerSeries<C> s = this;
162            String[] vars = ring.vars;
163            //System.out.println("cache1 = " + s.lazyCoeffs.coeffCache);
164            for (ExpVector i : new ExpVectorIterable(ring.nvar, true, trunc)) {
165                C c = s.coefficient(i);
166                //System.out.println("i = " + i + ", c = " +c);
167                int si = c.signum();
168                if (si != 0) {
169                    if (si > 0) {
170                        if (sb.length() > 0) {
171                            sb.append(" + ");
172                        }
173                    } else {
174                        c = c.negate();
175                        sb.append(" - ");
176                    }
177                    if (!c.isONE() || i.isZERO()) {
178                        if (c instanceof GenPolynomial || c instanceof AlgebraicNumber) {
179                             sb.append("{ ");
180                        }
181                        sb.append(c.toString());
182                        if (c instanceof GenPolynomial || c instanceof AlgebraicNumber) {
183                            sb.append(" }");
184                        }
185                        if (!i.isZERO()) {
186                            sb.append(" * ");
187                        }
188                    }
189                    if (i.isZERO()) {
190                        //skip; sb.append(" ");
191                    } else {
192                        sb.append(i.toString(vars));
193                    }
194                    //sb.append(c.toString() + ", ");
195                }
196                //System.out.println("cache = " + s.coeffCache);
197            }
198            if (sb.length() == 0) {
199                sb.append("0");
200            }
201            sb.append(" + BigO( (" + ring.varsToString() + ")^" + (trunc+1) + "(" + (ring.truncate+1) + ") )");
202            //sb.append("...");
203            //System.out.println("cache2 = " + s.lazyCoeffs.coeffCache);
204            return sb.toString();
205        }
206    
207    
208        /**
209         * Get a scripting compatible string representation.
210         * @return script compatible representation for this Element.
211         * @see edu.jas.structure.Element#toScript()
212         */
213        //JAVA6only: @Override
214        public String toScript() {
215            // Python case
216            StringBuffer sb = new StringBuffer("");
217            MultiVarPowerSeries<C> s = this;
218            String[] vars = ring.vars;
219            //System.out.println("cache = " + s.coeffCache);
220            for (ExpVector i : new ExpVectorIterable(ring.nvar, true, truncate)) {
221                C c = s.coefficient(i);
222                int si = c.signum();
223                if (si != 0) {
224                    if (si > 0) {
225                        if (sb.length() > 0) {
226                            sb.append(" + ");
227                        }
228                    } else {
229                        c = c.negate();
230                        sb.append(" - ");
231                    }
232                    if (!c.isONE() || i.isZERO()) {
233                        if (c instanceof GenPolynomial || c instanceof AlgebraicNumber) {
234                             sb.append("( ");
235                        }
236                        sb.append(c.toScript());
237                        if (c instanceof GenPolynomial || c instanceof AlgebraicNumber) {
238                             sb.append(" )");
239                        }
240                        if (!i.isZERO()) {
241                            sb.append(" * ");
242                        }
243                    }
244                    if (i.isZERO()) {
245                        //skip; sb.append(" ");
246                    } else {
247                        sb.append(i.toScript(vars));
248                    }
249                    //sb.append(c.toString() + ", ");
250                }
251                //System.out.println("cache = " + s.coeffCache);
252            }
253            if (sb.length() == 0) {
254                sb.append("0");
255            }
256            sb.append(" + BigO( (" + ring.varsToString() + ")**" + (truncate+1) + " )");
257            // sb.append("," + truncate + "");
258            return sb.toString();
259        }
260    
261    
262        /**
263         * Get a scripting compatible string representation of the factory.
264         * @return script compatible representation for this ElemFactory.
265         * @see edu.jas.structure.Element#toScriptFactory()
266         */
267        //JAVA6only: @Override
268        public String toScriptFactory() {
269            // Python case
270            return factory().toScript();
271        }
272    
273    
274        /**
275         * Get coefficient.
276         * @param index number of requested coefficient.
277         * @return coefficient at index.
278         */
279        public C coefficient(ExpVector index) {
280            if (index == null) {
281                throw new IndexOutOfBoundsException("null index not allowed");
282            }
283            return lazyCoeffs.get(index);
284        }
285    
286    
287        /**
288         * Homogeneous part.
289         * @param tdeg requested degree.
290         * @return polynomial part of given degree.
291         */
292        public GenPolynomial<C> homogeneousPart(long tdeg) {
293            return lazyCoeffs.getHomPart(tdeg);
294        }
295    
296    
297        /**
298         * Get a GenPolynomial&lt;C&gt; from this.
299         * @return a GenPolynomial&lt;C&gt; from this up to truncate homogeneous
300         *         parts.
301         */
302        public GenPolynomial<C> asPolynomial() {
303            GenPolynomial<C> p = homogeneousPart(0L);
304            for (int i = 1; i <= truncate; i++) {
305                p = p.sum(homogeneousPart(i));
306            }
307            return p;
308        }
309    
310    
311        /**
312         * Leading base coefficient.
313         * @return first coefficient.
314         */
315        public C leadingCoefficient() {
316            return coefficient(ring.EVZERO);
317        }
318    
319    
320        /**
321         * Reductum.
322         * @param r variable for taking the reductum.
323         * @return this - leading monomial in the direction of r.
324         */
325        public MultiVarPowerSeries<C> reductum(final int r) {
326            if (r < 0 || ring.nvar < r) {
327                throw new IllegalArgumentException("variable index out of bound");
328            }
329            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
330    
331    
332                @Override
333                public C generate(ExpVector i) {
334                    ExpVector e = i.subst(r, i.getVal(r) + 1);
335                    return coefficient(e);
336                }
337            });
338        }
339    
340    
341        /**
342         * Prepend a new leading coefficient.
343         * @param r variable for the direction.
344         * @param h new coefficient.
345         * @return new power series.
346         */
347        public MultiVarPowerSeries<C> prepend(final C h, final int r) {
348            if (r < 0 || ring.nvar < r) {
349                throw new IllegalArgumentException("variable index out of bound");
350            }
351            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
352    
353    
354                @Override
355                public C generate(ExpVector i) {
356                    if (i.isZERO()) {
357                        return h;
358                    }
359                    ExpVector e = i.subst(r, i.getVal(r) - 1);
360                    if (e.signum() < 0) {
361                        return pfac.coFac.getZERO();
362                    }
363                    return coefficient(e);
364                }
365            });
366        }
367    
368    
369        /**
370         * Shift coefficients.
371         * @param k shift index.
372         * @param r variable for the direction.
373         * @return new power series with coefficient(i) = old.coefficient(i-k).
374         */
375        public MultiVarPowerSeries<C> shift(final int k, final int r) {
376            if (r < 0 || ring.nvar < r) {
377                throw new IllegalArgumentException("variable index out of bound");
378            }
379            int nt = Math.min(truncate+k,ring.truncate);
380            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
381    
382    
383                @Override
384                public C generate(ExpVector i) {
385                    long d = i.getVal(r);
386                    if (d - k < 0) {
387                        return ring.coFac.getZERO();
388                    }
389                    ExpVector e = i.subst(r, i.getVal(r) - k);
390                    return coefficient(e);
391                }
392            },nt);
393        }
394    
395    
396        /**
397         * Reductum.
398         * @return this - leading monomial.
399         */
400        public MultiVarPowerSeries<C> reductum() {
401            Map.Entry<ExpVector, C> m = orderMonomial();
402            ExpVector e = m.getKey();
403            long d = e.totalDeg();
404            MultiVarCoefficients<C> mc = lazyCoeffs;
405            HashMap<Long, GenPolynomial<C>> cc = new HashMap<Long, GenPolynomial<C>>(mc.coeffCache);
406            GenPolynomial<C> p = cc.get(d);
407            if (p != null && !p.isZERO()) {
408                p = p.subtract(m.getValue(), e); // p contains this term after orderMonomial()
409                cc.put(d, p);
410            }
411            HashSet<ExpVector> z = new HashSet<ExpVector>(mc.zeroCache);
412            if ( !mc.homCheck.get((int)d) ) {
413                z.add(e);
414                //System.out.println("e = " + e);
415            }
416    
417            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(mc.pfac, cc, z, mc.homCheck) {
418    
419    
420                @Override
421                public C generate(ExpVector i) {
422                    return coefficient(i);
423                }
424            });
425        }
426    
427    
428        /**
429         * Shift coefficients. Multiply by exponent vector.
430         * @param k shift ExpVector.
431         * @return new power series with coefficient(i) = old.coefficient(i-k).
432         */
433        public MultiVarPowerSeries<C> shift(final ExpVector k) {
434            if (k == null) {
435                throw new IllegalArgumentException("null ExpVector not allowed");
436            }
437            if (k.signum() == 0) {
438                return this;
439            }
440            int nt = Math.min(truncate+(int)k.totalDeg(),ring.truncate);
441            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
442    
443    
444                @Override
445                public C generate(ExpVector i) {
446                    ExpVector d = i.subtract(k);
447                    if (d.signum() < 0) {
448                        return ring.coFac.getZERO();
449                    }
450                    return coefficient(d);
451                }
452            }, nt);
453        }
454    
455    
456        /**
457         * Multiply by exponent vector and coefficient.
458         * @param k shift ExpVector.
459         * @param c coefficient multiplier.
460         * @return new power series with coefficient(i) = old.coefficient(i-k)*c.
461         */
462        public MultiVarPowerSeries<C> multiply(final C c, final ExpVector k) {
463            if (k == null) {
464                throw new IllegalArgumentException("null ExpVector not allowed");
465            }
466            if (k.signum() == 0) {
467                return this.multiply(c);
468            }
469            if (c.signum() == 0) {
470                return ring.getZERO();
471            }
472            int nt = Math.min(ring.truncate,truncate+(int)k.totalDeg());
473    
474            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
475    
476    
477                @Override
478                public C generate(ExpVector i) {
479                    ExpVector d = i.subtract(k);
480                    if (d.signum() < 0) {
481                        return ring.coFac.getZERO();
482                    }
483                    long tdegd = d.totalDeg();
484                    if ( lazyCoeffs.homCheck.get((int)tdegd) ) {
485                        GenPolynomial<C> p = homogeneousPart(tdegd).multiply(c,k);
486                        long tdegi = i.totalDeg();
487                        coeffCache.put(tdegi, p); // overwrite
488                        homCheck.set((int) tdegi);
489                        C b = p.coefficient(i);
490                        //System.out.println("b = " + b + ", i = " + i + ", tdegi = " + tdegi+ ", tdegd = " + tdegd);
491                        //System.out.println("p = " + p + ", i = " + i);
492                        return b;
493                    }
494                    return coefficient(d).multiply(c);
495                }
496            }, nt);
497        }
498    
499    
500        /**
501         * Sum monomial.
502         * @param m ExpVector , coeffcient pair
503         * @return this + ONE.multiply(m.coefficient,m.exponent).
504         */
505        public MultiVarPowerSeries<C> sum(Map.Entry<ExpVector, C> m) {
506            if (m == null) {
507                throw new IllegalArgumentException("null Map.Entry not allowed");
508            }
509            return sum(m.getValue(), m.getKey());
510        }
511    
512    
513        /**
514         * Sum exponent vector and coefficient.
515         * @param k ExpVector.
516         * @param c coefficient.
517         * @return this + ONE.multiply(c,k).
518         */
519        public MultiVarPowerSeries<C> sum(final C c, final ExpVector k) {
520            if (k == null) {
521                throw new IllegalArgumentException("null ExpVector not allowed");
522            }
523            if (c.signum() == 0) {
524                return this;
525            }
526            long d = k.totalDeg();
527            MultiVarCoefficients<C> mc = lazyCoeffs;
528            HashMap<Long, GenPolynomial<C>> cc = new HashMap<Long, GenPolynomial<C>>(mc.coeffCache);
529            GenPolynomial<C> p = cc.get(d);
530            if (p == null) {
531                p = mc.pfac.getZERO();
532            }
533            p = p.sum(c, k);
534            //System.out.println("p = " + p);
535            cc.put(d, p);
536            HashSet<ExpVector> z = new HashSet<ExpVector>(mc.zeroCache);
537            //System.out.println("z = " + z);
538            if (p.coefficient(k).isZERO() && !mc.homCheck.get((int)d)) {
539                z.add(k);
540            }
541    
542            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(mc.pfac, cc, z, mc.homCheck) {
543    
544    
545                @Override
546                public C generate(ExpVector i) {
547                    return coefficient(i);
548                }
549            });
550        }
551    
552    
553        /**
554         * Subtract exponent vector and coefficient.
555         * @param k ExpVector.
556         * @param c coefficient.
557         * @return this - ONE.multiply(c,k).
558         */
559        public MultiVarPowerSeries<C> subtract(final C c, final ExpVector k) {
560            if (k == null) {
561                throw new IllegalArgumentException("null ExpVector not allowed");
562            }
563            if (c.signum() == 0) {
564                return this;
565            }
566            long d = k.totalDeg();
567            MultiVarCoefficients<C> mc = lazyCoeffs;
568            HashMap<Long, GenPolynomial<C>> cc = new HashMap<Long, GenPolynomial<C>>(mc.coeffCache);
569            GenPolynomial<C> p = cc.get(d);
570            if (p == null) {
571                p = mc.pfac.getZERO();
572            }
573            p = p.subtract(c, k);
574            cc.put(d, p);
575            HashSet<ExpVector> z = new HashSet<ExpVector>(mc.zeroCache);
576            //System.out.println("z = " + z);
577            if (p.coefficient(k).isZERO()&& !mc.homCheck.get((int)d)) {
578                z.add(k);
579            }
580            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(mc.pfac, cc, z, mc.homCheck) {
581    
582    
583                @Override
584                public C generate(ExpVector i) {
585                    return coefficient(i);
586                }
587            });
588        }
589    
590    
591        /**
592         * Sum exponent vector and coefficient.
593         * @param mvc cached coefficients.
594         * @return this + mvc.
595         */
596        public MultiVarPowerSeries<C> sum(MultiVarCoefficients<C> mvc) {
597            MultiVarCoefficients<C> mc = lazyCoeffs;
598            TreeMap<Long, GenPolynomial<C>> cc = new TreeMap<Long, GenPolynomial<C>>(mc.coeffCache);
599            TreeMap<Long, GenPolynomial<C>> ccv = new TreeMap<Long, GenPolynomial<C>>(mvc.coeffCache);
600            long d1 = (cc.size() > 0 ? cc.lastKey() : 0);
601            long d2 = (ccv.size() > 0 ? ccv.lastKey() : 0);
602            HashSet<ExpVector> z = new HashSet<ExpVector>(mc.zeroCache);
603            z.addAll(mvc.zeroCache);
604            long d = Math.max(d1, d2);
605            BitSet hc = new BitSet((int)d);
606            for (long i = 0; i <= d; i++) {
607                GenPolynomial<C> p1 = cc.get(i);
608                GenPolynomial<C> p2 = mvc.coeffCache.get(i);
609                if (p1 == null) {
610                    p1 = mc.pfac.getZERO();
611                }
612                if (p2 == null) {
613                    p2 = mc.pfac.getZERO();
614                }
615                GenPolynomial<C> p = p1.sum(p2);
616                //System.out.println("p = " + p);
617                cc.put(i, p);
618                if ( mc.homCheck.get((int)i) && mvc.homCheck.get((int)i) )  {
619                    hc.set((int)i);
620                } else {
621                    Set<ExpVector> ev = new HashSet<ExpVector>(p1.getMap().keySet());
622                    ev.addAll(p2.getMap().keySet());
623                    ev.removeAll(p.getMap().keySet());
624                    z.addAll(ev);
625                }
626            }
627            //System.out.println("cc = " + cc);
628    
629            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(mc.pfac,
630                       new HashMap<Long, GenPolynomial<C>>(cc), z, hc) {
631    
632    
633                @Override
634                public C generate(ExpVector i) {
635                    return coefficient(i);
636                }
637            });
638        }
639    
640    
641        /**
642         * Select coefficients.
643         * @param sel selector functor.
644         * @return new power series with selected coefficients.
645         */
646        public MultiVarPowerSeries<C> select(final Selector<? super C> sel) {
647            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
648    
649    
650                @Override
651                public C generate(ExpVector i) {
652                    C c = coefficient(i);
653                    if (sel.select(c)) {
654                        return c;
655                    }
656                    return ring.coFac.getZERO();
657                }
658            });
659        }
660    
661    
662        /**
663         * Shift select coefficients. Not selected coefficients are removed from the
664         * result series.
665         * @param sel selector functor.
666         * @return new power series with shifted selected coefficients.
667         */
668        public MultiVarPowerSeries<C> shiftSelect(final Selector<? super C> sel) {
669            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
670    
671    
672                ExpVectorIterable ib = new ExpVectorIterable(ring.nvar, true, truncate);
673    
674    
675                Iterator<ExpVector> pos = ib.iterator();
676    
677    
678                @Override
679                public C generate(ExpVector i) {
680                    C c;
681                    if (i.signum() > 0) {
682                        int[] deps = i.dependencyOnVariables();
683                        ExpVector x = i.subst(deps[0], i.getVal(deps[0]) - 1L);
684                        c = get(x);
685                    }
686                    do {
687                        c = null;
688                        if (pos.hasNext()) {
689                            ExpVector e = pos.next();
690                            c = coefficient(e);
691                        } else {
692                            break;
693                        }
694                    } while (!sel.select(c));
695                    if (c == null) { // not correct because not known
696                        c = ring.coFac.getZERO();
697                    }
698                    return c;
699                }
700            });
701        }
702    
703    
704        /**
705         * Map a unary function to this power series.
706         * @param f evaluation functor.
707         * @return new power series with coefficients f(this(i)).
708         */
709        public MultiVarPowerSeries<C> map(final UnaryFunctor<? super C, C> f) {
710            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
711    
712    
713                @Override
714                public C generate(ExpVector i) {
715                    return f.eval(coefficient(i));
716                }
717            });
718        }
719    
720    
721        /**
722         * Map a binary function to this and another power series.
723         * @param f evaluation functor with coefficients f(this(i),other(i)).
724         * @param ps other power series.
725         * @return new power series.
726         */
727        public MultiVarPowerSeries<C> zip(final BinaryFunctor<? super C, ? super C, C> f,
728                final MultiVarPowerSeries<C> ps) {
729            int m = Math.min(ring.truncate,Math.max(truncate,ps.truncate()));
730    
731            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
732    
733    
734                @Override
735                public C generate(ExpVector i) {
736                    return f.eval(coefficient(i), ps.coefficient(i));
737                }
738            }, m);
739        }
740    
741    
742        /**
743         * Sum of two power series, using zip().
744         * @param ps other power series.
745         * @return this + ps.
746         */
747        public MultiVarPowerSeries<C> sumZip(MultiVarPowerSeries<C> ps) {
748            return zip(new BinaryFunctor<C, C, C>() {
749    
750    
751                //JAVA6only: @Override
752                public C eval(C c1, C c2) {
753                    return c1.sum(c2);
754                }
755            }, ps);
756        }
757    
758    
759        /**
760         * Subtraction of two power series, using zip().
761         * @param ps other power series.
762         * @return this - ps.
763         */
764        public MultiVarPowerSeries<C> subtractZip(MultiVarPowerSeries<C> ps) {
765            return zip(new BinaryFunctor<C, C, C>() {
766    
767    
768                //JAVA6only: @Override
769                public C eval(C c1, C c2) {
770                    return c1.subtract(c2);
771                }
772            }, ps);
773        }
774    
775    
776        /**
777         * Multiply by coefficient.
778         * @param a coefficient.
779         * @return this * a.
780         */
781        public MultiVarPowerSeries<C> multiply(final C a) {
782            if (a.isZERO()) {
783                return ring.getZERO();
784            }
785            if (a.isONE()) {
786                return this;
787            }
788            return map(new UnaryFunctor<C, C>() {
789    
790    
791                //JAVA6only: @Override
792                public C eval(C c) {
793                    return c.multiply(a);
794                }
795            });
796        }
797    
798    
799        /**
800         * Monic.
801         * @return 1/orderCoeff() * this.
802         */
803        public MultiVarPowerSeries<C> monic() {
804            ExpVector e = orderExpVector();
805            if (e == null) {
806                return this;
807            }
808            C a = coefficient(e);
809            if (a.isONE()) {
810                return this;
811            }
812            if (a.isZERO()) { // sic
813                return this;
814            }
815            final C b = a.inverse();
816            return map(new UnaryFunctor<C, C>() {
817    
818    
819                //JAVA6only: @Override
820                public C eval(C c) {
821                    return b.multiply(c);
822                }
823            });
824        }
825    
826    
827        /**
828         * Negate.
829         * @return - this.
830         */
831        public MultiVarPowerSeries<C> negate() {
832            return map(new UnaryFunctor<C, C>() {
833    
834    
835                //JAVA6only: @Override
836                public C eval(C c) {
837                    return c.negate();
838                }
839            });
840        }
841    
842    
843        /**
844         * Absolute value.
845         * @return abs(this).
846         */
847        public MultiVarPowerSeries<C> abs() {
848            if (signum() < 0) {
849                return negate();
850            }
851            return this;
852        }
853    
854    
855        /**
856         * Evaluate at given point.
857         * @return ps(a).
858         */
859        public C evaluate(List<C> a) {
860            C v = ring.coFac.getZERO();
861            for (ExpVector i : new ExpVectorIterable(ring.nvar, true, truncate)) {
862                C c = coefficient(i);
863                if ( c.isZERO() ) {
864                    continue;
865                }
866                c = c.multiply( i.evaluate(ring.coFac,a) );
867                v = v.sum(c);
868            }
869            return v;
870        }
871    
872    
873        /**
874         * Order.
875         * @return index of first non zero coefficient.
876         */
877        public int order() {
878            if (order >= 0) { 
879                return order;
880            }
881            // must compute it
882            GenPolynomial<C> p = null;
883            int t = 0;
884            while ( lazyCoeffs.homCheck.get(t) ) {
885                p = lazyCoeffs.coeffCache.get(t);
886                if ( p == null || p.isZERO() ) { // ??
887                    t++;
888                    continue;
889                }
890                order = t; 
891                evorder = p.trailingExpVector();
892                //System.out.println("order = " + t);
893                return order;
894            }
895            ExpVector x = null;
896            for (ExpVector i : new ExpVectorIterable(ring.nvar, true, truncate)) {
897                if (!coefficient(i).isZERO()) {
898                    order = (int)i.totalDeg(); //ord;
899                    evorder = i;
900                    //System.out.println("order = " + order + ", evorder = " + evorder);
901                    return order;
902                }
903                x = i;
904            }
905            order = truncate + 1;
906            return order;
907        }
908    
909    
910        /**
911         * Order ExpVector.
912         * @return ExpVector of first non zero coefficient.
913         */
914        public ExpVector orderExpVector() {
915            @SuppressWarnings("unused")
916            int x = order(); // ensure evorder is set
917            return evorder;
918        }
919    
920    
921        /**
922         * Order monomial.
923         * @return first map entry or null.
924         */
925        public Map.Entry<ExpVector, C> orderMonomial() {
926            ExpVector e = orderExpVector();
927            if ( e == null ) {
928                return null;
929            }
930            //JAVA6only:
931            //return new AbstractMap.SimpleImmutableEntry<ExpVector, C>(e, coefficient(e));
932            return new MapEntry<ExpVector, C>(e, coefficient(e));
933        }
934    
935    
936        /**
937         * Truncate.
938         * @return truncate index of power series.
939         */
940        public int truncate() {
941            return truncate;
942        }
943    
944    
945        /**
946         * Set truncate.
947         * @param t new truncate index.
948         * @return old truncate index of power series.
949         */
950        public int setTruncate(int t) {
951            if (t < 0) {
952                throw new IllegalArgumentException("negative truncate not allowed");
953            }
954            int ot = truncate;
955            if ( order >= 0 ) {
956                if ( order > truncate ) { 
957                    order = -1; // reset
958                    evorder = null;
959                }
960            }
961            truncate = t;
962            return ot;
963        }
964    
965    
966        /**
967         * Ecart.
968         * @return ecart.
969         */
970        public long ecart() {
971            ExpVector e = orderExpVector();
972            if (e == null) {
973                return 0L;
974            }
975            long d = e.totalDeg();
976            long hd = d;
977            for (long i = d + 1L; i <= truncate; i++) {
978                if (!homogeneousPart(i).isZERO()) {
979                    hd = i;
980                }
981            }
982            //System.out.println("d = " + d + ", hd = " + hd + ", coeffCache = " + lazyCoeffs.coeffCache + ", this = " + this);
983            return hd - d;
984        }
985    
986    
987        /**
988         * Signum.
989         * @return sign of first non zero coefficient.
990         */
991        public int signum() {
992            @SuppressWarnings("unused")
993            int i = order(); // ensure evorder is defined
994            if (evorder != null) {
995                return coefficient(evorder).signum();
996            }
997            return 0;
998        }
999    
1000    
1001        /**
1002         * Compare to. <b>Note: </b> compare only up to max(truncates).
1003         * @return sign of first non zero coefficient of this-ps.
1004         */
1005        //JAVA6only: @Override
1006        public int compareTo(MultiVarPowerSeries<C> ps) {
1007            final int m = truncate();
1008            final int n = ps.truncate();
1009            final int pos = Math.min(ring.truncate,Math.min(m, n));
1010            int s = 0;
1011            //System.out.println("coeffCache_c1 = " + lazyCoeffs.coeffCache);
1012            //System.out.println("coeffCache_c2 = " + ps.lazyCoeffs.coeffCache);
1013            // test homogeneous parts first is slower
1014            for (ExpVector i : new ExpVectorIterable(ring.nvar, true, pos)) {
1015                s = coefficient(i).compareTo(ps.coefficient(i));
1016                if (s != 0) {
1017                    //System.out.println("i = " + i + ", coeff = " + coefficient(i) + ", ps.coeff = " + ps.coefficient(i));
1018                    return s;
1019                }
1020            }
1021            for (int j = pos + 1; j <= Math.min(ring.truncate,Math.max(m, n)); j++) { 
1022                for (ExpVector i : new ExpVectorIterable(ring.nvar, j)) {
1023                    s = coefficient(i).compareTo(ps.coefficient(i));
1024                    //System.out.println("i = " + i + ", coeff = " + coefficient(i) + ", ps.coeff = " + ps.coefficient(i));
1025                    if (s != 0) {
1026                        //System.out.println("i = " + i + ", coeff = " + coefficient(i) + ", ps.coeff = " + ps.coefficient(i));
1027                        return s;
1028                    }
1029                }
1030            }
1031            return s;
1032        }
1033    
1034    
1035        /**
1036         * Is power series zero. <b>Note: </b> compare only up to truncate.
1037         * @return If this is 0 then true is returned, else false.
1038         * @see edu.jas.structure.RingElem#isZERO()
1039         */
1040        public boolean isZERO() {
1041            return (signum() == 0);
1042        }
1043    
1044    
1045        /**
1046         * Is power series one. <b>Note: </b> compare only up to truncate.
1047         * @return If this is 1 then true is returned, else false.
1048         * @see edu.jas.structure.RingElem#isONE()
1049         */
1050        public boolean isONE() {
1051            if (!leadingCoefficient().isONE()) {
1052                return false;
1053            }
1054            return (compareTo(ring.ONE) == 0);
1055            //return reductum().isZERO();
1056        }
1057    
1058    
1059        /**
1060         * Comparison with any other object. <b>Note: </b> compare only up to
1061         * truncate.
1062         * @see java.lang.Object#equals(java.lang.Object)
1063         */
1064        @Override
1065        @SuppressWarnings("unchecked")
1066        public boolean equals(Object B) {
1067            if (!(B instanceof MultiVarPowerSeries)) {
1068                return false;
1069            }
1070            MultiVarPowerSeries<C> a = null;
1071            try {
1072                a = (MultiVarPowerSeries<C>) B;
1073            } catch (ClassCastException ignored) {
1074            }
1075            if (a == null) {
1076                return false;
1077            }
1078            return compareTo(a) == 0;
1079        }
1080    
1081    
1082        /**
1083         * Hash code for this polynomial. <b>Note: </b> only up to truncate.
1084         * @see java.lang.Object#hashCode()
1085         */
1086        @Override
1087        public int hashCode() {
1088            int h = 0;
1089            //h = ( ring.hashCode() << 23 );
1090            //h += val.hashCode();
1091            for (ExpVector i : new ExpVectorIterable(ring.nvar, true, truncate)) {
1092                C c = coefficient(i);
1093                if ( !c.isZERO() ) {
1094                    h += i.hashCode(); 
1095                    h = ( h << 23);
1096                }
1097                h += c.hashCode();
1098                h = (h << 23);
1099            };
1100            return h;
1101        }
1102    
1103    
1104        /**
1105         * Is unit.
1106         * @return true, if this power series is invertible, else false.
1107         */
1108        public boolean isUnit() {
1109            return leadingCoefficient().isUnit();
1110        }
1111    
1112    
1113        /**
1114         * Sum a another power series.
1115         * @param ps other power series.
1116         * @return this + ps.
1117         */
1118        public MultiVarPowerSeries<C> sum(final MultiVarPowerSeries<C> ps) {
1119            //final MultiVarPowerSeries<C> ps1 = this; // method name was ambiguous in generate
1120            int nt = Math.min(ring.truncate,Math.max(truncate(),ps.truncate()));
1121    
1122            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
1123    
1124    
1125                @Override
1126                public C generate(ExpVector e) {
1127                    long tdeg = e.totalDeg();
1128                    if ( lazyCoeffs.homCheck.get((int)tdeg) ) {
1129                        // generate respective homogeneous polynomial
1130                        GenPolynomial<C> p = homogeneousPart(tdeg).sum(ps.homogeneousPart(tdeg));
1131                        coeffCache.put(tdeg, p); // overwrite
1132                        homCheck.set((int) tdeg);
1133                        C c = p.coefficient(e);
1134                        //System.out.println("c = " + c + ", e = " + e + ", tdeg = " + tdeg);
1135                        return c;
1136                    }
1137                    return coefficient(e).sum(ps.coefficient(e));
1138                }
1139            }, nt);
1140        }
1141    
1142    
1143        /**
1144         * Subtract a another power series.
1145         * @param ps other power series.
1146         * @return this - ps.
1147         */
1148        public MultiVarPowerSeries<C> subtract(final MultiVarPowerSeries<C> ps) {
1149            //final MultiVarPowerSeries<C> ps1 = this; // method name was ambiguous in generate
1150            int nt = Math.min(ring.truncate,Math.max(truncate(),ps.truncate()));
1151    
1152            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
1153    
1154    
1155                @Override
1156                public C generate(ExpVector e) {
1157                    long tdeg = e.totalDeg();
1158                    if ( lazyCoeffs.homCheck.get((int)tdeg) ) {
1159                        // generate respective homogeneous polynomial
1160                        GenPolynomial<C> p = homogeneousPart(tdeg).subtract(ps.homogeneousPart(tdeg));
1161                        coeffCache.put(tdeg, p); // overwrite
1162                        homCheck.set((int) tdeg);
1163                        C c = p.coefficient(e);
1164                        //System.out.println("p = " + p + ", e = " + e + ", tdeg = " + tdeg);
1165                        return c;
1166                    }
1167                    return coefficient(e).subtract(ps.coefficient(e));
1168                }
1169            }, nt);
1170        }
1171    
1172    
1173        /**
1174         * Multiply by another power series.
1175         * @param ps other power series.
1176         * @return this * ps.
1177         */
1178        public MultiVarPowerSeries<C> multiply(final MultiVarPowerSeries<C> ps) {
1179            //final MultiVarPowerSeries<C> ps1 = this; // method name was ambiguous in generate
1180            int nt = Math.min(ring.truncate,truncate()+ps.truncate());
1181    
1182            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
1183    
1184    
1185                @Override
1186                public C generate(ExpVector e) {
1187                    long tdeg = e.totalDeg();
1188                    // generate respective homogeneous polynomial
1189                    GenPolynomial<C> p = null; //fac.getZERO();
1190                    for (int k = 0; k <= tdeg; k++) {
1191                        GenPolynomial<C> m = homogeneousPart(k).multiply(ps.homogeneousPart(tdeg - k));
1192                        if (p == null) {
1193                            p = m;
1194                        } else {
1195                            p = p.sum(m);
1196                        }
1197                    }
1198                    coeffCache.put(tdeg, p); // overwrite
1199                    homCheck.set((int) tdeg);
1200                    C c = p.coefficient(e);
1201                    return c;
1202                }
1203            }, nt);
1204        }
1205    
1206    
1207        /**
1208         * Inverse power series.
1209         * @return ps with this * ps = 1.
1210         */
1211        public MultiVarPowerSeries<C> inverse() {
1212            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
1213    
1214    
1215                @Override
1216                public C generate(ExpVector e) {
1217                    long tdeg = e.totalDeg();
1218                    C d = leadingCoefficient().inverse(); // may fail
1219                    if (tdeg == 0) {
1220                        return d;
1221                    }
1222                    GenPolynomial<C> p = null; //fac.getZERO();
1223                    for (int k = 0; k < tdeg; k++) {
1224                        GenPolynomial<C> m = getHomPart(k).multiply(homogeneousPart(tdeg - k));
1225                        if (p == null) {
1226                            p = m;
1227                        } else {
1228                            p = p.sum(m);
1229                        }
1230                    }
1231                    p = p.multiply(d.negate());
1232                    //System.out.println("tdeg = " + tdeg + ", p = " + p);
1233                    coeffCache.put(tdeg, p); // overwrite
1234                    homCheck.set((int) tdeg);
1235                    C c = p.coefficient(e);
1236                    return c;
1237                }
1238            });
1239        }
1240    
1241    
1242        /**
1243         * Divide by another power series.
1244         * @param ps nonzero power series with invertible coefficient.
1245         * @return this / ps.
1246         */
1247        public MultiVarPowerSeries<C> divide(MultiVarPowerSeries<C> ps) {
1248            if (ps.isUnit()) {
1249                return multiply(ps.inverse());
1250            }
1251            int m = order();
1252            int n = ps.order();
1253            if (m < n) {
1254                return ring.getZERO();
1255            }
1256            ExpVector em = orderExpVector();
1257            ExpVector en = ps.orderExpVector();
1258            if (!ps.coefficient(en).isUnit()) {
1259                throw new ArithmeticException("division by non unit coefficient " + ps.coefficient(ps.evorder)
1260                        + ", evorder = " + ps.evorder);
1261            }
1262            // now m >= n
1263            MultiVarPowerSeries<C> st, sps, q, sq;
1264            if (m == 0) {
1265                st = this;
1266            } else {
1267                st = this.shift(em.negate());
1268            }
1269            if (n == 0) {
1270                sps = ps;
1271            } else {
1272                sps = ps.shift(en.negate());
1273            }
1274            q = st.multiply(sps.inverse());
1275            sq = q.shift(em.subtract(en));
1276            return sq;
1277        }
1278    
1279    
1280        /**
1281         * Power series remainder.
1282         * @param ps nonzero power series with invertible leading coefficient.
1283         * @return remainder with this = quotient * ps + remainder.
1284         */
1285        public MultiVarPowerSeries<C> remainder(MultiVarPowerSeries<C> ps) {
1286            int m = order();
1287            int n = ps.order();
1288            if (m >= n) {
1289                return ring.getZERO();
1290            }
1291            return this;
1292        }
1293    
1294    
1295        /**
1296         * Differentiate with respect to variable r.
1297         * @param r variable for the direction.
1298         * @return differentiate(this).
1299         */
1300        public MultiVarPowerSeries<C> differentiate(final int r) {
1301            if (r < 0 || ring.nvar < r) {
1302                throw new IllegalArgumentException("variable index out of bound");
1303            }
1304            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
1305    
1306    
1307                @Override
1308                public C generate(ExpVector i) {
1309                    long d = i.getVal(r);
1310                    ExpVector e = i.subst(r, d + 1);
1311                    C v = coefficient(e);
1312                    v = v.multiply(ring.coFac.fromInteger(d + 1));
1313                    return v;
1314                }
1315            });
1316        }
1317    
1318    
1319        /**
1320         * Integrate with respect to variable r and with given constant.
1321         * @param c integration constant.
1322         * @param r variable for the direction.
1323         * @return integrate(this).
1324         */
1325        public MultiVarPowerSeries<C> integrate(final C c, final int r) {
1326            if (r < 0 || ring.nvar < r) {
1327                throw new IllegalArgumentException("variable index out of bound");
1328            }
1329            int nt = Math.min(ring.truncate,truncate+1);
1330            return new MultiVarPowerSeries<C>(ring, new MultiVarCoefficients<C>(ring) {
1331    
1332    
1333                @Override
1334                public C generate(ExpVector i) {
1335                    if (i.isZERO()) {
1336                        return c;
1337                    }
1338                    long d = i.getVal(r);
1339                    if (d > 0) {
1340                        ExpVector e = i.subst(r, d - 1);
1341                        C v = coefficient(e);
1342                        v = v.divide(ring.coFac.fromInteger(d));
1343                        return v;
1344                    }
1345                    return ring.coFac.getZERO();
1346                }
1347            }, nt);
1348        }
1349    
1350    
1351        /**
1352         * Power series greatest common divisor.
1353         * @param ps power series.
1354         * @return gcd(this,ps).
1355         */
1356        public MultiVarPowerSeries<C> gcd(MultiVarPowerSeries<C> ps) {
1357            if (ps.isZERO()) {
1358                return this;
1359            }
1360            if (this.isZERO()) {
1361                return ps;
1362            }
1363            ExpVector em = orderExpVector();
1364            ExpVector en = ps.orderExpVector();
1365            return ring.getONE().shift(em.gcd(en));
1366        }
1367    
1368    
1369        /**
1370         * Power series extended greatest common divisor. <b>Note:</b> not
1371         * implemented.
1372         * @param S power series.
1373         * @return [ gcd(this,S), a, b ] with a*this + b*S = gcd(this,S).
1374         */
1375        //SuppressWarnings("unchecked")
1376        public MultiVarPowerSeries<C>[] egcd(MultiVarPowerSeries<C> S) {
1377            throw new UnsupportedOperationException("egcd for power series not implemented");
1378        }
1379    
1380    }