All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sanselan.common.RationalNumberUtilities Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.sanselan.common;

public abstract class RationalNumberUtilities extends Number
{

    private static class Option
    {
        public final RationalNumber rationalNumber;
        public final double error;

        private Option(final RationalNumber rationalNumber, final double error)
        {
            this.rationalNumber = rationalNumber;
            this.error = error;
        }

        public static final Option factory(final RationalNumber rationalNumber,
                final double value)
        {
            return new Option(rationalNumber, Math.abs(rationalNumber
                    .doubleValue()
                    - value));
        }

        public String toString()
        {
            return rationalNumber.toString();
        }
    }

    // int-precision tolerance
    private static final double TOLERANCE = 1E-8;

    //
    // calculate rational number using successive approximations.
    //
    public static final RationalNumber getRationalNumber(double value)
    {
        if (value >= Integer.MAX_VALUE)
            return new RationalNumber(Integer.MAX_VALUE, 1);
        else if (value <= -Integer.MAX_VALUE)
            return new RationalNumber(-Integer.MAX_VALUE, 1);

        boolean negative = false;
        if (value < 0)
        {
            negative = true;
            value = Math.abs(value);
        }

        Option low;
        Option high;
        {
            RationalNumber l, h;

            if (value == 0)
                return new RationalNumber(0, 1);
            else if (value >= 1)
            {
                int approx = (int) value;
                if (approx < value)
                {
                    l = new RationalNumber(approx, 1);
                    h = new RationalNumber(approx + 1, 1);
                }
                else
                {
                    l = new RationalNumber(approx - 1, 1);
                    h = new RationalNumber(approx, 1);
                }
            }
            else
            {
                int approx = (int) (1.0 / value);
                if ((1.0 / approx) < value)
                {
                    l = new RationalNumber(1, approx);
                    h = new RationalNumber(1, approx - 1);
                }
                else
                {
                    l = new RationalNumber(1, approx + 1);
                    h = new RationalNumber(1, approx);
                }
            }
            low = Option.factory(l, value);
            high = Option.factory(h, value);
        }

        Option bestOption = (low.error < high.error) ? low : high;

        final int MAX_ITERATIONS = 100; // value is quite high, actually.  shouldn't matter.
        for (int count = 0; bestOption.error > TOLERANCE
                && count < MAX_ITERATIONS; count++)
        {
            //            Debug.debug("bestOption: " + bestOption + ", left: " + low
            //                    + ", right: " + high + ", value: " + value + ", error: "
            //                    + bestOption.error);

            RationalNumber mediant = RationalNumber.factoryMethod(
                    (long) low.rationalNumber.numerator
                            + (long) high.rationalNumber.numerator,
                    (long) low.rationalNumber.divisor
                            + (long) high.rationalNumber.divisor);
            Option mediantOption = Option.factory(mediant, value);

            if (value < mediant.doubleValue())
            {
                if (high.error <= mediantOption.error)
                    break;

                high = mediantOption;
            }
            else
            {
                if (low.error <= mediantOption.error)
                    break;

                low = mediantOption;
            }

            if (mediantOption.error < bestOption.error)
                bestOption = mediantOption;
        }

        return negative
                ? bestOption.rationalNumber.negate()
                : bestOption.rationalNumber;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy