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 }