1 /* 2 * Licensed to the Hipparchus project under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The Hipparchus project licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.hipparchus.util; 18 19 import org.hipparchus.CalculusFieldElement; 20 import org.hipparchus.exception.LocalizedCoreFormats; 21 import org.hipparchus.exception.MathIllegalStateException; 22 23 /** 24 * Provides a generic means to evaluate continued fractions. Subclasses simply 25 * provided the a and b coefficients to evaluate the continued fraction. 26 * <p> 27 * References: 28 * <ul> 29 * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html"> 30 * Continued Fraction</a></li> 31 * </ul> 32 * 33 */ 34 public abstract class FieldContinuedFraction { 35 /** Maximum allowed numerical error. */ 36 private static final double DEFAULT_EPSILON = 10e-9; 37 38 /** 39 * Default constructor. 40 */ 41 protected FieldContinuedFraction() { 42 super(); 43 } 44 45 /** 46 * Access the n-th a coefficient of the continued fraction. Since a can be 47 * a function of the evaluation point, x, that is passed in as well. 48 * @param n the coefficient index to retrieve. 49 * @param x the evaluation point. 50 * @param <T> type of the field elements. 51 * @return the n-th a coefficient. 52 */ 53 public abstract <T extends CalculusFieldElement<T>> T getA(int n, T x); 54 55 /** 56 * Access the n-th b coefficient of the continued fraction. Since b can be 57 * a function of the evaluation point, x, that is passed in as well. 58 * @param n the coefficient index to retrieve. 59 * @param x the evaluation point. 60 * @param <T> type of the field elements. 61 * @return the n-th b coefficient. 62 */ 63 public abstract <T extends CalculusFieldElement<T>> T getB(int n, T x); 64 65 /** 66 * Evaluates the continued fraction at the value x. 67 * @param x the evaluation point. 68 * @param <T> type of the field elements. 69 * @return the value of the continued fraction evaluated at x. 70 * @throws MathIllegalStateException if the algorithm fails to converge. 71 */ 72 public <T extends CalculusFieldElement<T>> T evaluate(T x) throws MathIllegalStateException { 73 return evaluate(x, DEFAULT_EPSILON, Integer.MAX_VALUE); 74 } 75 76 /** 77 * Evaluates the continued fraction at the value x. 78 * @param x the evaluation point. 79 * @param epsilon maximum error allowed. 80 * @param <T> type of the field elements. 81 * @return the value of the continued fraction evaluated at x. 82 * @throws MathIllegalStateException if the algorithm fails to converge. 83 */ 84 public <T extends CalculusFieldElement<T>> T evaluate(T x, double epsilon) throws MathIllegalStateException { 85 return evaluate(x, epsilon, Integer.MAX_VALUE); 86 } 87 88 /** 89 * Evaluates the continued fraction at the value x. 90 * @param x the evaluation point. 91 * @param maxIterations maximum number of convergents 92 * @param <T> type of the field elements. 93 * @return the value of the continued fraction evaluated at x. 94 * @throws MathIllegalStateException if the algorithm fails to converge. 95 * @throws MathIllegalStateException if maximal number of iterations is reached 96 */ 97 public <T extends CalculusFieldElement<T>> T evaluate(T x, int maxIterations) 98 throws MathIllegalStateException { 99 return evaluate(x, DEFAULT_EPSILON, maxIterations); 100 } 101 102 /** 103 * Evaluates the continued fraction at the value x. 104 * <p> 105 * The implementation of this method is based on the modified Lentz algorithm as described 106 * on page 18 ff. in: 107 * </p> 108 * <ul> 109 * <li> 110 * I. J. Thompson, A. R. Barnett. "Coulomb and Bessel Functions of Complex Arguments and Order." 111 * <a target="_blank" href="http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf"> 112 * http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf</a> 113 * </li> 114 * </ul> 115 * <p> 116 * <b>Note:</b> the implementation uses the terms a<sub>i</sub> and b<sub>i</sub> as defined in 117 * <a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction @ MathWorld</a>. 118 * </p> 119 * 120 * @param x the evaluation point. 121 * @param epsilon maximum error allowed. 122 * @param maxIterations maximum number of convergents 123 * @param <T> type of the field elements. 124 * @return the value of the continued fraction evaluated at x. 125 * @throws MathIllegalStateException if the algorithm fails to converge. 126 * @throws MathIllegalStateException if maximal number of iterations is reached 127 */ 128 public <T extends CalculusFieldElement<T>> T evaluate(T x, double epsilon, int maxIterations) 129 throws MathIllegalStateException { 130 final T zero = x.getField().getZero(); 131 final T one = x.getField().getOne(); 132 133 final double small = 1e-50; 134 final T smallField = one.multiply(small); 135 136 T hPrev = getA(0, x); 137 138 // use the value of small as epsilon criteria for zero checks 139 if (Precision.equals(hPrev.getReal(), 0.0, small)) { 140 hPrev = one.multiply(small); 141 } 142 143 int n = 1; 144 T dPrev = zero; 145 T cPrev = hPrev; 146 T hN = hPrev; 147 148 while (n < maxIterations) { 149 final T a = getA(n, x); 150 final T b = getB(n, x); 151 152 T dN = a.add(b.multiply(dPrev)); 153 if (Precision.equals(dN.getReal(), 0.0, small)) { 154 dN = smallField; 155 } 156 T cN = a.add(b.divide(cPrev)); 157 if (Precision.equals(cN.getReal(), 0.0, small)) { 158 cN = smallField; 159 } 160 161 dN = dN.reciprocal(); 162 final T deltaN = cN.multiply(dN); 163 hN = hPrev.multiply(deltaN); 164 165 if (hN.isInfinite()) { 166 throw new MathIllegalStateException(LocalizedCoreFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE, x); 167 } 168 if (hN.isNaN()) { 169 throw new MathIllegalStateException(LocalizedCoreFormats.CONTINUED_FRACTION_NAN_DIVERGENCE, x); 170 } 171 172 if (deltaN.subtract(1.0).abs().getReal() < epsilon) { 173 break; 174 } 175 176 dPrev = dN; 177 cPrev = cN; 178 hPrev = hN; 179 n++; 180 } 181 182 if (n >= maxIterations) { 183 throw new MathIllegalStateException(LocalizedCoreFormats.NON_CONVERGENT_CONTINUED_FRACTION, 184 maxIterations, x); 185 } 186 187 return hN; 188 } 189 190 }