1dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond/*
2dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Licensed to the Apache Software Foundation (ASF) under one or more
3dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * contributor license agreements.  See the NOTICE file distributed with
4dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * this work for additional information regarding copyright ownership.
5dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * The ASF licenses this file to You under the Apache License, Version 2.0
6dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * (the "License"); you may not use this file except in compliance with
7dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * the License.  You may obtain a copy of the License at
8dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond *
9dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond *      http://www.apache.org/licenses/LICENSE-2.0
10dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond *
11dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * Unless required by applicable law or agreed to in writing, software
12dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * distributed under the License is distributed on an "AS IS" BASIS,
13dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * See the License for the specific language governing permissions and
15dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * limitations under the License.
16dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */
17dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondpackage org.apache.commons.math.analysis.polynomials;
18dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
19dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport java.util.ArrayList;
20dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
21dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport org.apache.commons.math.fraction.BigFraction;
22dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondimport org.apache.commons.math.util.FastMath;
23dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
24dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond/**
25dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * A collection of static methods that operate on or return polynomials.
26dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond *
27dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
28dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond * @since 2.0
29dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond */
30dee0849a9704d532af0b550146cbafbaa6ee1d19Raymondpublic class PolynomialsUtils {
31dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
32dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /** Coefficients for Chebyshev polynomials. */
33dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private static final ArrayList<BigFraction> CHEBYSHEV_COEFFICIENTS;
34dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
35dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /** Coefficients for Hermite polynomials. */
36dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private static final ArrayList<BigFraction> HERMITE_COEFFICIENTS;
37dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
38dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /** Coefficients for Laguerre polynomials. */
39dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private static final ArrayList<BigFraction> LAGUERRE_COEFFICIENTS;
40dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
41dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /** Coefficients for Legendre polynomials. */
42dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private static final ArrayList<BigFraction> LEGENDRE_COEFFICIENTS;
43dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
44dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    static {
45dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
46dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // initialize recurrence for Chebyshev polynomials
47dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // T0(X) = 1, T1(X) = 0 + 1 * X
48dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        CHEBYSHEV_COEFFICIENTS = new ArrayList<BigFraction>();
49dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        CHEBYSHEV_COEFFICIENTS.add(BigFraction.ONE);
50dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        CHEBYSHEV_COEFFICIENTS.add(BigFraction.ZERO);
51dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        CHEBYSHEV_COEFFICIENTS.add(BigFraction.ONE);
52dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
53dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // initialize recurrence for Hermite polynomials
54dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // H0(X) = 1, H1(X) = 0 + 2 * X
55dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        HERMITE_COEFFICIENTS = new ArrayList<BigFraction>();
56dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        HERMITE_COEFFICIENTS.add(BigFraction.ONE);
57dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        HERMITE_COEFFICIENTS.add(BigFraction.ZERO);
58dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        HERMITE_COEFFICIENTS.add(BigFraction.TWO);
59dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
60dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // initialize recurrence for Laguerre polynomials
61dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // L0(X) = 1, L1(X) = 1 - 1 * X
62dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LAGUERRE_COEFFICIENTS = new ArrayList<BigFraction>();
63dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LAGUERRE_COEFFICIENTS.add(BigFraction.ONE);
64dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LAGUERRE_COEFFICIENTS.add(BigFraction.ONE);
65dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LAGUERRE_COEFFICIENTS.add(BigFraction.MINUS_ONE);
66dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
67dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // initialize recurrence for Legendre polynomials
68dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // P0(X) = 1, P1(X) = 0 + 1 * X
69dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LEGENDRE_COEFFICIENTS = new ArrayList<BigFraction>();
70dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LEGENDRE_COEFFICIENTS.add(BigFraction.ONE);
71dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LEGENDRE_COEFFICIENTS.add(BigFraction.ZERO);
72dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        LEGENDRE_COEFFICIENTS.add(BigFraction.ONE);
73dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
74dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
75dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
76dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /**
77dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * Private constructor, to prevent instantiation.
78dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     */
79dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private PolynomialsUtils() {
80dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
81dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
82dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /**
83dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * Create a Chebyshev polynomial of the first kind.
84dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <p><a href="http://mathworld.wolfram.com/ChebyshevPolynomialoftheFirstKind.html">Chebyshev
85dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * polynomials of the first kind</a> are orthogonal polynomials.
86dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * They can be defined by the following recurrence relations:
87dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <pre>
88dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  T<sub>0</sub>(X)   = 1
89dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  T<sub>1</sub>(X)   = X
90dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  T<sub>k+1</sub>(X) = 2X T<sub>k</sub>(X) - T<sub>k-1</sub>(X)
91dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * </pre></p>
92dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param degree degree of the polynomial
93dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @return Chebyshev polynomial of specified degree
94dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     */
95dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    public static PolynomialFunction createChebyshevPolynomial(final int degree) {
96dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        return buildPolynomial(degree, CHEBYSHEV_COEFFICIENTS,
97dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                new RecurrenceCoefficientsGenerator() {
98dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            private final BigFraction[] coeffs = { BigFraction.ZERO, BigFraction.TWO, BigFraction.ONE };
99dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            /** {@inheritDoc} */
100dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            public BigFraction[] generate(int k) {
101dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                return coeffs;
102dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            }
103dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        });
104dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
105dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
106dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /**
107dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * Create a Hermite polynomial.
108dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <p><a href="http://mathworld.wolfram.com/HermitePolynomial.html">Hermite
109dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * polynomials</a> are orthogonal polynomials.
110dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * They can be defined by the following recurrence relations:
111dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <pre>
112dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  H<sub>0</sub>(X)   = 1
113dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  H<sub>1</sub>(X)   = 2X
114dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  H<sub>k+1</sub>(X) = 2X H<sub>k</sub>(X) - 2k H<sub>k-1</sub>(X)
115dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * </pre></p>
116dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
117dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param degree degree of the polynomial
118dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @return Hermite polynomial of specified degree
119dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     */
120dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    public static PolynomialFunction createHermitePolynomial(final int degree) {
121dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        return buildPolynomial(degree, HERMITE_COEFFICIENTS,
122dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                new RecurrenceCoefficientsGenerator() {
123dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            /** {@inheritDoc} */
124dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            public BigFraction[] generate(int k) {
125dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                return new BigFraction[] {
126dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        BigFraction.ZERO,
127dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        BigFraction.TWO,
128dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        new BigFraction(2 * k)};
129dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            }
130dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        });
131dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
132dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
133dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /**
134dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * Create a Laguerre polynomial.
135dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <p><a href="http://mathworld.wolfram.com/LaguerrePolynomial.html">Laguerre
136dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * polynomials</a> are orthogonal polynomials.
137dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * They can be defined by the following recurrence relations:
138dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <pre>
139dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *        L<sub>0</sub>(X)   = 1
140dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *        L<sub>1</sub>(X)   = 1 - X
141dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  (k+1) L<sub>k+1</sub>(X) = (2k + 1 - X) L<sub>k</sub>(X) - k L<sub>k-1</sub>(X)
142dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * </pre></p>
143dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param degree degree of the polynomial
144dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @return Laguerre polynomial of specified degree
145dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     */
146dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    public static PolynomialFunction createLaguerrePolynomial(final int degree) {
147dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        return buildPolynomial(degree, LAGUERRE_COEFFICIENTS,
148dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                new RecurrenceCoefficientsGenerator() {
149dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            /** {@inheritDoc} */
150dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            public BigFraction[] generate(int k) {
151dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                final int kP1 = k + 1;
152dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                return new BigFraction[] {
153dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        new BigFraction(2 * k + 1, kP1),
154dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        new BigFraction(-1, kP1),
155dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        new BigFraction(k, kP1)};
156dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            }
157dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        });
158dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
159dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
160dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /**
161dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * Create a Legendre polynomial.
162dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <p><a href="http://mathworld.wolfram.com/LegendrePolynomial.html">Legendre
163dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * polynomials</a> are orthogonal polynomials.
164dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * They can be defined by the following recurrence relations:
165dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * <pre>
166dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *        P<sub>0</sub>(X)   = 1
167dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *        P<sub>1</sub>(X)   = X
168dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     *  (k+1) P<sub>k+1</sub>(X) = (2k+1) X P<sub>k</sub>(X) - k P<sub>k-1</sub>(X)
169dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * </pre></p>
170dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param degree degree of the polynomial
171dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @return Legendre polynomial of specified degree
172dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     */
173dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    public static PolynomialFunction createLegendrePolynomial(final int degree) {
174dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        return buildPolynomial(degree, LEGENDRE_COEFFICIENTS,
175dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                               new RecurrenceCoefficientsGenerator() {
176dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            /** {@inheritDoc} */
177dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            public BigFraction[] generate(int k) {
178dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                final int kP1 = k + 1;
179dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                return new BigFraction[] {
180dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        BigFraction.ZERO,
181dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        new BigFraction(k + kP1, kP1),
182dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                        new BigFraction(k, kP1)};
183dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            }
184dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        });
185dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
186dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
187dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /** Get the coefficients array for a given degree.
188dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param degree degree of the polynomial
189dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param coefficients list where the computed coefficients are stored
190dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param generator recurrence coefficients generator
191dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @return coefficients array
192dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     */
193dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private static PolynomialFunction buildPolynomial(final int degree,
194dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                                                      final ArrayList<BigFraction> coefficients,
195dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                                                      final RecurrenceCoefficientsGenerator generator) {
196dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
197dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        final int maxDegree = (int) FastMath.floor(FastMath.sqrt(2 * coefficients.size())) - 1;
198dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        synchronized (PolynomialsUtils.class) {
199dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            if (degree > maxDegree) {
200dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                computeUpToDegree(degree, maxDegree, generator, coefficients);
201dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            }
202dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        }
203dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
204dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // coefficient  for polynomial 0 is  l [0]
205dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // coefficients for polynomial 1 are l [1] ... l [2] (degrees 0 ... 1)
206dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // coefficients for polynomial 2 are l [3] ... l [5] (degrees 0 ... 2)
207dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // coefficients for polynomial 3 are l [6] ... l [9] (degrees 0 ... 3)
208dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // coefficients for polynomial 4 are l[10] ... l[14] (degrees 0 ... 4)
209dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // coefficients for polynomial 5 are l[15] ... l[20] (degrees 0 ... 5)
210dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // coefficients for polynomial 6 are l[21] ... l[27] (degrees 0 ... 6)
211dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // ...
212dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        final int start = degree * (degree + 1) / 2;
213dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
214dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        final double[] a = new double[degree + 1];
215dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        for (int i = 0; i <= degree; ++i) {
216dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            a[i] = coefficients.get(start + i).doubleValue();
217dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        }
218dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
219dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        // build the polynomial
220dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        return new PolynomialFunction(a);
221dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
222dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
223dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
224dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /** Compute polynomial coefficients up to a given degree.
225dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param degree maximal degree
226dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param maxDegree current maximal degree
227dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param generator recurrence coefficients generator
228dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     * @param coefficients list where the computed coefficients should be appended
229dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond     */
230dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private static void computeUpToDegree(final int degree, final int maxDegree,
231dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                                          final RecurrenceCoefficientsGenerator generator,
232dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                                          final ArrayList<BigFraction> coefficients) {
233dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
234dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        int startK = (maxDegree - 1) * maxDegree / 2;
235dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        for (int k = maxDegree; k < degree; ++k) {
236dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
237dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            // start indices of two previous polynomials Pk(X) and Pk-1(X)
238dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            int startKm1 = startK;
239dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            startK += k;
240dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
241dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            // Pk+1(X) = (a[0] + a[1] X) Pk(X) - a[2] Pk-1(X)
242dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            BigFraction[] ai = generator.generate(k);
243dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
244dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            BigFraction ck     = coefficients.get(startK);
245dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            BigFraction ckm1   = coefficients.get(startKm1);
246dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
247dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            // degree 0 coefficient
248dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            coefficients.add(ck.multiply(ai[0]).subtract(ckm1.multiply(ai[2])));
249dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
250dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            // degree 1 to degree k-1 coefficients
251dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            for (int i = 1; i < k; ++i) {
252dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                final BigFraction ckPrev = ck;
253dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                ck     = coefficients.get(startK + i);
254dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                ckm1   = coefficients.get(startKm1 + i);
255dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond                coefficients.add(ck.multiply(ai[0]).add(ckPrev.multiply(ai[1])).subtract(ckm1.multiply(ai[2])));
256dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            }
257dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
258dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            // degree k coefficient
259dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            final BigFraction ckPrev = ck;
260dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            ck = coefficients.get(startK + k);
261dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            coefficients.add(ck.multiply(ai[0]).add(ckPrev.multiply(ai[1])));
262dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
263dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            // degree k+1 coefficient
264dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond            coefficients.add(ck.multiply(ai[1]));
265dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
266dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        }
267dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
268dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
269dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
270dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    /** Interface for recurrence coefficients generation. */
271dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    private static interface RecurrenceCoefficientsGenerator {
272dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        /**
273dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond         * Generate recurrence coefficients.
274dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond         * @param k highest degree of the polynomials used in the recurrence
275dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond         * @return an array of three coefficients such that
276dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond         * P<sub>k+1</sub>(X) = (a[0] + a[1] X) P<sub>k</sub>(X) - a[2] P<sub>k-1</sub>(X)
277dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond         */
278dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond        BigFraction[] generate(int k);
279dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond    }
280dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond
281dee0849a9704d532af0b550146cbafbaa6ee1d19Raymond}
282