/*
 * $Id$
 */

package edu.jas.ps;


import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import edu.jas.poly.GenPolynomial;
import edu.jas.poly.GenPolynomialRing;
import edu.jas.poly.Monomial;
import edu.jas.structure.RingElem;
import edu.jas.structure.RingFactory;


/**
 * Univariate power series ring implementation. Uses lazy evaluated generating
 * function for coefficients.
 * @param  ring element type
 * @author Heinz Kredel
 */

public class UnivPowerSeriesRing> implements RingFactory> {


    /**
     * A default random sequence generator.
     */
    protected final static Random random = new Random();


    /**
     * Default truncate.
     */
    public final static int DEFAULT_TRUNCATE = 11;


    /**
     * Truncate.
     */
    int truncate;


    /**
     * Default variable name.
     */
    public final static String DEFAULT_NAME = "x";


    /**
     * Variable name.
     */
    String var;


    /**
     * Coefficient ring factory.
     */
    public final RingFactory coFac;


    /**
     * The constant power series 1 for this ring.
     */
    public final UnivPowerSeries ONE;


    /**
     * The constant power series 0 for this ring.
     */
    public final UnivPowerSeries ZERO;


    /**
     * No argument constructor.
     */
    @SuppressWarnings("unused")
    private UnivPowerSeriesRing() {
        throw new IllegalArgumentException("do not use no-argument constructor");
    }


    /**
     * Constructor.
     * @param coFac coefficient ring factory.
     */
    public UnivPowerSeriesRing(RingFactory coFac) {
        this(coFac, DEFAULT_TRUNCATE, DEFAULT_NAME);
    }


    /**
     * Constructor.
     * @param coFac coefficient ring factory.
     * @param truncate index of truncation.
     */
    public UnivPowerSeriesRing(RingFactory coFac, int truncate) {
        this(coFac, truncate, DEFAULT_NAME);
    }


    /**
     * Constructor.
     * @param coFac coefficient ring factory.
     * @param name of the variable.
     */
    public UnivPowerSeriesRing(RingFactory coFac, String name) {
        this(coFac, DEFAULT_TRUNCATE, name);
    }


    /**
     * Constructor.
     * @param pfac polynomial ring factory.
     */
    public UnivPowerSeriesRing(GenPolynomialRing pfac) {
        this(pfac.coFac, DEFAULT_TRUNCATE, pfac.getVars()[0]);
    }


    /**
     * Constructor.
     * @param cofac coefficient ring factory.
     * @param truncate index of truncation.
     * @param name of the variable.
     */
    public UnivPowerSeriesRing(RingFactory cofac, int truncate, String name) {
        this.coFac = cofac;
        this.truncate = truncate;
        this.var = name;
        this.ONE = new UnivPowerSeries(this, new Coefficients() {


            @Override
            public C generate(int i) {
                if (i == 0) {
                    return coFac.getONE();
                }
                return coFac.getZERO();
            }
        });
        this.ZERO = new UnivPowerSeries(this, new Coefficients() {


            @Override
            public C generate(int i) {
                return coFac.getZERO();
            }
        });
    }


    /**
     * Fixed point construction.
     * @param map a mapping of power series.
     * @return fix point wrt map.
     */
    // Cannot be a static method because a power series ring is required.
    public UnivPowerSeries fixPoint(UnivPowerSeriesMap map) {
        UnivPowerSeries ps1 = new UnivPowerSeries(this);
        UnivPowerSeries ps2 = map.map(ps1);
        ps1.lazyCoeffs = ps2.lazyCoeffs;
        return ps2;
    }


    /**
     * To String.
     * @return string representation of this.
     */
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        String scf = coFac.getClass().getSimpleName();
        sb.append(scf + "((" + var + "))");
        return sb.toString();
    }


    /**
     * Get a scripting compatible string representation.
     * @return script compatible representation for this ElemFactory.
     * @see edu.jas.structure.ElemFactory#toScript()
     */
    @Override
    public String toScript() {
        // Python case
        StringBuffer s = new StringBuffer("PS(");
        String f = null;
        try {
            f = ((RingElem) coFac).toScriptFactory(); // sic
        } catch (Exception e) {
            f = coFac.toScript();
        }
        s.append(f + ",\"" + var + "\"," + truncate + ")");
        return s.toString();
    }


    /**
     * Comparison with any other object.
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    @SuppressWarnings("unchecked")
    public boolean equals(Object B) {
        UnivPowerSeriesRing a = null;
        try {
            a = (UnivPowerSeriesRing) B;
        } catch (ClassCastException ignored) {
        }
        if (a == null) {
            return false;
        }
        if (!coFac.equals(a.coFac)) {
            return false;
        }
        if (!var.equals(a.var)) {
            return false;
        }
        return true;
    }


    /**
     * Hash code for this .
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        int h = coFac.hashCode();
        h += (var.hashCode() << 27);
        h += truncate;
        return h;
    }


    /**
     * Get the zero element.
     * @return 0 as UnivPowerSeries.
     */
    public UnivPowerSeries getZERO() {
        return ZERO;
    }


    /**
     * Get the one element.
     * @return 1 as UnivPowerSeries.
     */
    public UnivPowerSeries getONE() {
        return ONE;
    }


    /**
     * Get a list of the generating elements.
     * @return list of generators for the algebraic structure.
     * @see edu.jas.structure.ElemFactory#generators()
     */
    public List> generators() {
        List rgens = coFac.generators();
        List> gens = new ArrayList>(rgens.size());
        for (final C cg : rgens) {
            UnivPowerSeries g = new UnivPowerSeries(this, new Coefficients() {


                @Override
                public C generate(int i) {
                    if (i == 0) {
                        return cg;
                    }
                    return coFac.getZERO();
                }
            });
            gens.add(g);
        }
        gens.add(ONE.shift(1));
        return gens;
    }


    /**
     * Is this structure finite or infinite.
     * @return true if this structure is finite, else false.
     * @see edu.jas.structure.ElemFactory#isFinite()
     */
    public boolean isFinite() {
        return false;
    }


    /**
     * Get the power series of the exponential function.
     * @return exp(x) as UnivPowerSeries.
     */
    public UnivPowerSeries getEXP() {
        return fixPoint(new UnivPowerSeriesMap() {


            public UnivPowerSeries map(UnivPowerSeries e) {
                return e.integrate(coFac.getONE());
            }
        });
    }


    /**
     * Get the power series of the sinus function.
     * @return sin(x) as UnivPowerSeries.
     */
    public UnivPowerSeries getSIN() {
        return fixPoint(new UnivPowerSeriesMap() {


            public UnivPowerSeries map(UnivPowerSeries s) {
                return s.negate().integrate(coFac.getONE()).integrate(coFac.getZERO());
            }
        });
    }


    /**
     * Get the power series of the cosine function.
     * @return cos(x) as UnivPowerSeries.
     */
    public UnivPowerSeries getCOS() {
        return fixPoint(new UnivPowerSeriesMap() {


            public UnivPowerSeries map(UnivPowerSeries c) {
                return c.negate().integrate(coFac.getZERO()).integrate(coFac.getONE());
            }
        });
    }


    /**
     * Get the power series of the tangens function.
     * @return tan(x) as UnivPowerSeries.
     */
    public UnivPowerSeries getTAN() {
        return fixPoint(new UnivPowerSeriesMap() {


            public UnivPowerSeries map(UnivPowerSeries t) {
                return t.multiply(t).sum(getONE()).integrate(coFac.getZERO());
            }
        });
    }


    /**
     * Solve an ordinary differential equation. y' = f(y) with y(0) = c.
     * @param f a UnivPowerSeries.
     * @param c integration constant.
     * @return f.integrate(c).
     */
    public UnivPowerSeries solveODE(final UnivPowerSeries f, final C c) {
        return f.integrate(c);
    }


    /**
     * Is commutative.
     * @return true, if this ring is commutative, else false.
     */
    public boolean isCommutative() {
        return coFac.isCommutative();
    }


    /**
     * Query if this ring is associative.
     * @return true if this ring is associative, else false.
     */
    public boolean isAssociative() {
        return coFac.isAssociative();
    }


    /**
     * Query if this ring is a field.
     * @return false.
     */
    public boolean isField() {
        return false;
    }


    /**
     * Characteristic of this ring.
     * @return characteristic of this ring.
     */
    public java.math.BigInteger characteristic() {
        return coFac.characteristic();
    }


    /**
     * Get a (constant) UnivPowerSeries<C> from a long value.
     * @param a long.
     * @return a UnivPowerSeries<C>.
     */
    public UnivPowerSeries fromInteger(long a) {
        return ONE.multiply(coFac.fromInteger(a));
    }


    /**
     * Get a (constant) UnivPowerSeries<C> from a java.math.BigInteger.
     * @param a BigInteger.
     * @return a UnivPowerSeries<C>.
     */
    public UnivPowerSeries fromInteger(java.math.BigInteger a) {
        return ONE.multiply(coFac.fromInteger(a));
    }


    /**
     * Get the corresponding GenPolynomialRing<C>.
     * @return GenPolynomialRing<C>.
     */
    public GenPolynomialRing polyRing() {
        return new GenPolynomialRing(coFac, 1, new String[] { var });
    }


    /**
     * Get a UnivPowerSeries<C> from a GenPolynomial<C>.
     * @param a GenPolynomial<C>.
     * @return a UnivPowerSeries<C>.
     */
    public UnivPowerSeries fromPolynomial(GenPolynomial a) {
        if (a == null || a.isZERO()) {
            return ZERO;
        }
        if (a.isONE()) {
            return ONE;
        }
        if (a.ring.nvar != 1) {
            throw new IllegalArgumentException("only for univariate polynomials");
        }
        HashMap cache = new HashMap(a.length());
        for (Monomial m : a) {
            long e = m.exponent().getVal(0);
            cache.put((int) e, m.coefficient());
        }
        return new UnivPowerSeries(this, new Coefficients(cache) {


            @Override
            public C generate(int i) {
                // cached coefficients returned by get
                return coFac.getZERO();
            }
        });
    }


    /**
     * Generate a random power series with k = 5, d = 0.7.
     * @return a random power series.
     */
    public UnivPowerSeries random() {
        return random(5, 0.7f, random);
    }


    /**
     * Generate a random power series with d = 0.7.
     * @param k bitsize of random coefficients.
     * @return a random power series.
     */
    public UnivPowerSeries random(int k) {
        return random(k, 0.7f, random);
    }


    /**
     * Generate a random power series with d = 0.7.
     * @param k bit-size of random coefficients.
     * @param rnd is a source for random bits.
     * @return a random power series.
     */
    public UnivPowerSeries random(int k, Random rnd) {
        return random(k, 0.7f, rnd);
    }


    /**
     * Generate a random power series.
     * @param k bit-size of random coefficients.
     * @param d density of non-zero coefficients.
     * @return a random power series.
     */
    public UnivPowerSeries random(int k, float d) {
        return random(k, d, random);
    }


    /**
     * Generate a random power series.
     * @param k bit-size of random coefficients.
     * @param d density of non-zero coefficients.
     * @param rnd is a source for random bits.
     * @return a random power series.
     */
    public UnivPowerSeries random(final int k, final float d, final Random rnd) {
        return new UnivPowerSeries(this, new Coefficients() {


            @Override
            public C generate(int i) {
                // cached coefficients returned by get
                C c;
                float f = rnd.nextFloat();
                if (f < d) {
                    c = coFac.random(k, rnd);
                } else {
                    c = coFac.getZERO();
                }
                return c;
            }
        });
    }


    /**
     * Copy power series.
     * @param c a power series.
     * @return a copy of c.
     */
    public UnivPowerSeries copy(UnivPowerSeries c) {
        return new UnivPowerSeries(this, c.lazyCoeffs);
    }


    /**
     * Parse a power series. Note: not implemented.
     * @param s String.
     * @return power series from s.
     */
    public UnivPowerSeries parse(String s) {
        throw new UnsupportedOperationException("parse for power series not implemented");
    }


    /**
     * Parse a power series. Note: not implemented.
     * @param r Reader.
     * @return next power series from r.
     */
    public UnivPowerSeries parse(Reader r) {
        throw new UnsupportedOperationException("parse for power series not implemented");
    }


    /**
     * Taylor power series.
     * @param f function.
     * @param a expansion point.
     * @return Taylor series of f.
     */
    public UnivPowerSeries seriesOfTaylor(final TaylorFunction f, final C a) {
        return new UnivPowerSeries(this, new Coefficients() {


            TaylorFunction der = f;


            long k = 0;


            long n = 1;


            @Override
            public C generate(int i) {
                C c;
                if (i == 0) {
                    c = der.evaluate(a);
                    der = der.deriviative();
                    return c;
                }
                if (i > 0) {
                    c = get(i - 1); // ensure deriv is updated
                }
                k++;
                n *= k;
                c = der.evaluate(a);
                //System.out.println("n = " + n + ", i = " +i);
                c = c.divide(coFac.fromInteger(n));
                der = der.deriviative();
                return c;
            }
        });
    }

}

 

Ads help maintain this website.