ConvergentsIterator.java

  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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.fraction;

  22. import java.util.Iterator;
  23. import java.util.Spliterator;
  24. import java.util.Spliterators;
  25. import java.util.function.Predicate;
  26. import java.util.stream.Stream;
  27. import java.util.stream.StreamSupport;

  28. import org.hipparchus.exception.LocalizedCoreFormats;
  29. import org.hipparchus.exception.MathIllegalStateException;
  30. import org.hipparchus.util.FastMath;
  31. import org.hipparchus.util.Pair;
  32. import org.hipparchus.util.Precision;

  33. /**
  34.  * Generator for convergents.
  35.  */
  36. class ConvergentsIterator {

  37.     /** Unused constructor.
  38.      */
  39.     private ConvergentsIterator() {
  40.     } // static use only

  41.     /** Container for one convergent step. */
  42.     static class ConvergenceStep {
  43.         /** Numerator of previous convergent. */
  44.         private final long   p0;

  45.         /** Denominator of previous convergent. */
  46.         private final long   q0;

  47.         /** Numerator of current convergent. */
  48.         private final long   p1;

  49.         /** Denominator of current convergent. */
  50.         private final long   q1;

  51.         /** Remainder of current convergent. */
  52.         private final double r1;

  53.         private ConvergenceStep(final long p0, final long q0, final long p1, final long q1, final double r1) {
  54.             this.p0 = p1;
  55.             this.q0 = q1;
  56.             final long a1 = (long) FastMath.floor(r1);
  57.             try {
  58.                 this.p1 = FastMath.addExact(Math.multiplyExact(a1, p1), p0);
  59.                 this.q1 = FastMath.addExact(Math.multiplyExact(a1, q1), q0);
  60.                 this.r1 = 1.0 / (r1 - a1);
  61.             } catch (ArithmeticException e) { // unlike the name implies FastMath's multiplyExact() is slower
  62.                 throw new MathIllegalStateException(e, LocalizedCoreFormats.FRACTION_CONVERSION_OVERFLOW, r1, p1, q1);
  63.             }
  64.         }

  65.         /** Builder from a double value.
  66.          * @param value value to approximate
  67.          * @return first step in approximation
  68.          */
  69.         public static ConvergenceStep start(double value) {
  70.             return new ConvergenceStep(0, 1, 1, 0, value);
  71.         }

  72.         /** Compute next step in convergence.
  73.          * @return next convergence step
  74.          */
  75.         public ConvergenceStep next() {
  76.             return new ConvergenceStep(p0, q0, p1, q1, r1);
  77.         }

  78.         /** Get the numerator of current convergent.
  79.          * @return numerator of current convergent
  80.          */
  81.         public long getNumerator() {
  82.             return p1;
  83.         }

  84.         /** Get the denominator of current convergent.
  85.          * @return denominator of current convergent
  86.          */
  87.         public long getDenominator() {
  88.             return q1;
  89.         }

  90.         /** Compute double value of current convergent.
  91.          * @return double value of current convergent
  92.          */
  93.         public double getFractionValue() {
  94.             return getNumerator() / (double) getDenominator();
  95.         }

  96.         /** Convert convergent to string representation.
  97.          * @return string representation of convergent
  98.          */
  99.         @Override
  100.         public String toString() {
  101.             return getNumerator() + "/" + getDenominator();
  102.         }

  103.     }

  104.     /**
  105.      * Returns the last element of the series of convergent-steps to approximate the
  106.      * given value.
  107.      * <p>
  108.      * The series terminates either at the first step that satisfies the given
  109.      * {@code convergenceTest} or after at most {@code maxConvergents} elements. The
  110.      * returned Pair consists of that terminal step and a {@link Boolean} that
  111.      * indicates if it satisfies the given convergence tests. If the returned pair's
  112.      * value is {@code false} the element at position {@code maxConvergents} was
  113.      * examined but failed to satisfy the {@code convergenceTest}.
  114.      *
  115.      * @param value           value to approximate
  116.      * @param maxConvergents  maximum number of convergents to examine
  117.      * @param convergenceTests the test if the series has converged at a step
  118.      * @return the pair of last element of the series of convergents and a boolean
  119.      *         indicating if that element satisfies the specified convergent test
  120.      */
  121.     static Pair<ConvergenceStep, Boolean> convergent(double value, int maxConvergents,
  122.             Predicate<ConvergenceStep> convergenceTests) {
  123.         ConvergenceStep step = ConvergenceStep.start(value);
  124.         for (int i = 1; i < maxConvergents; i++) { // start performs first iteration
  125.             if (convergenceTests.test(step)) {
  126.                 return Pair.create(step, Boolean.TRUE);
  127.             }
  128.             step = step.next();
  129.         }
  130.         return Pair.create(step, convergenceTests.test(step));
  131.     }

  132.     /**
  133.      * Generate a {@link Stream stream} of {@code ConvergenceStep convergent-steps}
  134.      * from a real number.
  135.      *
  136.      * @param value           value to approximate
  137.      * @param maxConvergents maximum number of convergent steps.
  138.      * @return stream of {@link ConvergenceStep convergent-steps} approximating
  139.      *         {@code value}
  140.      */
  141.     static Stream<ConvergenceStep> convergents(final double value, final int maxConvergents) {
  142.         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<ConvergenceStep>() {

  143.             /** Next convergent. */
  144.             private ConvergenceStep next = ConvergenceStep.start(value);

  145.             /** {@inheritDoc} */
  146.             @Override
  147.             public boolean hasNext() {
  148.                 return next != null;
  149.             }

  150.             /** {@inheritDoc} */
  151.             @Override
  152.             public ConvergenceStep next() {
  153.                 final ConvergenceStep ret = next;
  154.                 if (Precision.equals(ret.getFractionValue(), value, 1)) {
  155.                     next = null; // stop if precision has been reached
  156.                 } else {
  157.                     next = next.next();
  158.                 }
  159.                 return ret;
  160.             }

  161.         }, Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.IMMUTABLE | Spliterator.ORDERED),
  162.         false).
  163.         limit(maxConvergents);
  164.     }
  165. }