001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math3.util;
018
019import org.apache.commons.math3.exception.ConvergenceException;
020import org.apache.commons.math3.exception.MaxCountExceededException;
021import org.apache.commons.math3.exception.util.LocalizedFormats;
022
023/**
024 * Provides a generic means to evaluate continued fractions.  Subclasses simply
025 * provided the a and b coefficients to evaluate the continued fraction.
026 *
027 * <p>
028 * References:
029 * <ul>
030 * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
031 * Continued Fraction</a></li>
032 * </ul>
033 * </p>
034 *
035 */
036public abstract class ContinuedFraction {
037    /** Maximum allowed numerical error. */
038    private static final double DEFAULT_EPSILON = 10e-9;
039
040    /**
041     * Default constructor.
042     */
043    protected ContinuedFraction() {
044        super();
045    }
046
047    /**
048     * Access the n-th a coefficient of the continued fraction.  Since a can be
049     * a function of the evaluation point, x, that is passed in as well.
050     * @param n the coefficient index to retrieve.
051     * @param x the evaluation point.
052     * @return the n-th a coefficient.
053     */
054    protected abstract double getA(int n, double x);
055
056    /**
057     * Access the n-th b coefficient of the continued fraction.  Since b can be
058     * a function of the evaluation point, x, that is passed in as well.
059     * @param n the coefficient index to retrieve.
060     * @param x the evaluation point.
061     * @return the n-th b coefficient.
062     */
063    protected abstract double getB(int n, double x);
064
065    /**
066     * Evaluates the continued fraction at the value x.
067     * @param x the evaluation point.
068     * @return the value of the continued fraction evaluated at x.
069     * @throws ConvergenceException if the algorithm fails to converge.
070     */
071    public double evaluate(double x) throws ConvergenceException {
072        return evaluate(x, DEFAULT_EPSILON, Integer.MAX_VALUE);
073    }
074
075    /**
076     * Evaluates the continued fraction at the value x.
077     * @param x the evaluation point.
078     * @param epsilon maximum error allowed.
079     * @return the value of the continued fraction evaluated at x.
080     * @throws ConvergenceException if the algorithm fails to converge.
081     */
082    public double evaluate(double x, double epsilon) throws ConvergenceException {
083        return evaluate(x, epsilon, Integer.MAX_VALUE);
084    }
085
086    /**
087     * Evaluates the continued fraction at the value x.
088     * @param x the evaluation point.
089     * @param maxIterations maximum number of convergents
090     * @return the value of the continued fraction evaluated at x.
091     * @throws ConvergenceException if the algorithm fails to converge.
092     * @throws MaxCountExceededException if maximal number of iterations is reached
093     */
094    public double evaluate(double x, int maxIterations)
095        throws ConvergenceException, MaxCountExceededException {
096        return evaluate(x, DEFAULT_EPSILON, maxIterations);
097    }
098
099    /**
100     * Evaluates the continued fraction at the value x.
101     * <p>
102     * The implementation of this method is based on the modified Lentz algorithm as described
103     * on page 18 ff. in:
104     * <ul>
105     *   <li>
106     *   I. J. Thompson,  A. R. Barnett. "Coulomb and Bessel Functions of Complex Arguments and Order."
107     *   <a target="_blank" href="http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf">
108     *   http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf</a>
109     *   </li>
110     * </ul>
111     * <b>Note:</b> the implementation uses the terms a<sub>i</sub> and b<sub>i</sub> as defined in
112     * <a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction @ MathWorld</a>.
113     * </p>
114     *
115     * @param x the evaluation point.
116     * @param epsilon maximum error allowed.
117     * @param maxIterations maximum number of convergents
118     * @return the value of the continued fraction evaluated at x.
119     * @throws ConvergenceException if the algorithm fails to converge.
120     * @throws MaxCountExceededException if maximal number of iterations is reached
121     */
122    public double evaluate(double x, double epsilon, int maxIterations)
123        throws ConvergenceException, MaxCountExceededException {
124        final double small = 1e-50;
125        double hPrev = getA(0, x);
126
127        // use the value of small as epsilon criteria for zero checks
128        if (Precision.equals(hPrev, 0.0, small)) {
129            hPrev = small;
130        }
131
132        int n = 1;
133        double dPrev = 0.0;
134        double cPrev = hPrev;
135        double hN = hPrev;
136
137        while (n < maxIterations) {
138            final double a = getA(n, x);
139            final double b = getB(n, x);
140
141            double dN = a + b * dPrev;
142            if (Precision.equals(dN, 0.0, small)) {
143                dN = small;
144            }
145            double cN = a + b / cPrev;
146            if (Precision.equals(cN, 0.0, small)) {
147                cN = small;
148            }
149
150            dN = 1 / dN;
151            final double deltaN = cN * dN;
152            hN = hPrev * deltaN;
153
154            if (Double.isInfinite(hN)) {
155                throw new ConvergenceException(LocalizedFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE,
156                                               x);
157            }
158            if (Double.isNaN(hN)) {
159                throw new ConvergenceException(LocalizedFormats.CONTINUED_FRACTION_NAN_DIVERGENCE,
160                                               x);
161            }
162
163            if (FastMath.abs(deltaN - 1.0) < epsilon) {
164                break;
165            }
166
167            dPrev = dN;
168            cPrev = cN;
169            hPrev = hN;
170            n++;
171        }
172
173        if (n >= maxIterations) {
174            throw new MaxCountExceededException(LocalizedFormats.NON_CONVERGENT_CONTINUED_FRACTION,
175                                                maxIterations, x);
176        }
177
178        return hN;
179    }
180
181}