View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) 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 ASF 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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.fraction;
23  
24  import java.util.Iterator;
25  import java.util.Spliterator;
26  import java.util.Spliterators;
27  import java.util.function.Predicate;
28  import java.util.stream.Stream;
29  import java.util.stream.StreamSupport;
30  
31  import org.hipparchus.exception.LocalizedCoreFormats;
32  import org.hipparchus.exception.MathIllegalStateException;
33  import org.hipparchus.util.FastMath;
34  import org.hipparchus.util.Pair;
35  import org.hipparchus.util.Precision;
36  
37  /**
38   * Generator for convergents.
39   */
40  class ConvergentsIterator {
41  
42      /** Unused constructor.
43       */
44      private ConvergentsIterator() {
45      } // static use only
46  
47      /** Container for one convergent step. */
48      static class ConvergenceStep {
49          /** Numerator of previous convergent. */
50          private final long   p0;
51  
52          /** Denominator of previous convergent. */
53          private final long   q0;
54  
55          /** Numerator of current convergent. */
56          private final long   p1;
57  
58          /** Denominator of current convergent. */
59          private final long   q1;
60  
61          /** Remainder of current convergent. */
62          private final double r1;
63  
64          private ConvergenceStep(final long p0, final long q0, final long p1, final long q1, final double r1) {
65              this.p0 = p1;
66              this.q0 = q1;
67              final long a1 = (long) FastMath.floor(r1);
68              try {
69                  this.p1 = FastMath.addExact(Math.multiplyExact(a1, p1), p0);
70                  this.q1 = FastMath.addExact(Math.multiplyExact(a1, q1), q0);
71                  this.r1 = 1.0 / (r1 - a1);
72              } catch (ArithmeticException e) { // unlike the name implies FastMath's multiplyExact() is slower
73                  throw new MathIllegalStateException(e, LocalizedCoreFormats.FRACTION_CONVERSION_OVERFLOW, r1, p1, q1);
74              }
75          }
76  
77          /** Builder from a double value.
78           * @param value value to approximate
79           * @return first step in approximation
80           */
81          public static ConvergenceStep start(double value) {
82              return new ConvergenceStep(0, 1, 1, 0, value);
83          }
84  
85          /** Compute next step in convergence.
86           * @return next convergence step
87           */
88          public ConvergenceStep next() {
89              return new ConvergenceStep(p0, q0, p1, q1, r1);
90          }
91  
92          /** Get the numerator of current convergent.
93           * @return numerator of current convergent
94           */
95          public long getNumerator() {
96              return p1;
97          }
98  
99          /** Get the denominator of current convergent.
100          * @return denominator of current convergent
101          */
102         public long getDenominator() {
103             return q1;
104         }
105 
106         /** Compute double value of current convergent.
107          * @return double value of current convergent
108          */
109         public double getFractionValue() {
110             return getNumerator() / (double) getDenominator();
111         }
112 
113         /** Convert convergent to string representation.
114          * @return string representation of convergent
115          */
116         @Override
117         public String toString() {
118             return getNumerator() + "/" + getDenominator();
119         }
120 
121     }
122 
123     /**
124      * Returns the last element of the series of convergent-steps to approximate the
125      * given value.
126      * <p>
127      * The series terminates either at the first step that satisfies the given
128      * {@code convergenceTest} or after at most {@code maxConvergents} elements. The
129      * returned Pair consists of that terminal step and a {@link Boolean} that
130      * indicates if it satisfies the given convergence tests. If the returned pair's
131      * value is {@code false} the element at position {@code maxConvergents} was
132      * examined but failed to satisfy the {@code convergenceTest}.
133      *
134      * @param value           value to approximate
135      * @param maxConvergents  maximum number of convergents to examine
136      * @param convergenceTests the test if the series has converged at a step
137      * @return the pair of last element of the series of convergents and a boolean
138      *         indicating if that element satisfies the specified convergent test
139      */
140     static Pair<ConvergenceStep, Boolean> convergent(double value, int maxConvergents,
141             Predicate<ConvergenceStep> convergenceTests) {
142         ConvergenceStep step = ConvergenceStep.start(value);
143         for (int i = 1; i < maxConvergents; i++) { // start performs first iteration
144             if (convergenceTests.test(step)) {
145                 return Pair.create(step, Boolean.TRUE);
146             }
147             step = step.next();
148         }
149         return Pair.create(step, convergenceTests.test(step));
150     }
151 
152     /**
153      * Generate a {@link Stream stream} of {@code ConvergenceStep convergent-steps}
154      * from a real number.
155      *
156      * @param value           value to approximate
157      * @param maxConvergents maximum number of convergent steps.
158      * @return stream of {@link ConvergenceStep convergent-steps} approximating
159      *         {@code value}
160      */
161     static Stream<ConvergenceStep> convergents(final double value, final int maxConvergents) {
162         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<ConvergenceStep>() {
163 
164             /** Next convergent. */
165             private ConvergenceStep next = ConvergenceStep.start(value);
166 
167             /** {@inheritDoc} */
168             @Override
169             public boolean hasNext() {
170                 return next != null;
171             }
172 
173             /** {@inheritDoc} */
174             @Override
175             public ConvergenceStep next() {
176                 final ConvergenceStep ret = next;
177                 if (Precision.equals(ret.getFractionValue(), value, 1)) {
178                     next = null; // stop if precision has been reached
179                 } else {
180                     next = next.next();
181                 }
182                 return ret;
183             }
184 
185         }, Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.IMMUTABLE | Spliterator.ORDERED),
186         false).
187         limit(maxConvergents);
188     }
189 }