summaryrefslogtreecommitdiffstats
path: root/core/java/android/util/Rational.java
blob: 8d4c67fafd4294ed0ffd4842c5961bcba3c1942c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed 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 android.util;

/**
 * <p>An immutable data type representation a rational number.</p>
 *
 * <p>Contains a pair of {@code int}s representing the numerator and denominator of a
 * Rational number. </p>
 */
public final class Rational {
    private final int mNumerator;
    private final int mDenominator;

    /**
     * <p>Create a Rational with a given numerator and denominator.</p>
     *
     * <p>The signs of the numerator and the denominator may be flipped such that the denominator
     * is always positive.</p>
     *
     * <p>A rational value with a 0-denominator may be constructed, but will have similar semantics
     * as float {@code NaN} and {@code INF} values. For {@code NaN},
     * both {@link #getNumerator} and {@link #getDenominator} functions will return 0. For
     * positive or negative {@code INF}, only the {@link #getDenominator} will return 0.</p>
     *
     * @param numerator the numerator of the rational
     * @param denominator the denominator of the rational
     */
    public Rational(int numerator, int denominator) {

        if (denominator < 0) {
            numerator = -numerator;
            denominator = -denominator;
        }

        mNumerator = numerator;
        mDenominator = denominator;
    }

    /**
     * Gets the numerator of the rational.
     */
    public int getNumerator() {
        if (mDenominator == 0) {
            return 0;
        }
        return mNumerator;
    }

    /**
     * Gets the denominator of the rational
     */
    public int getDenominator() {
        return mDenominator;
    }

    private boolean isNaN() {
        return mDenominator == 0 && mNumerator == 0;
    }

    private boolean isInf() {
        return mDenominator == 0 && mNumerator > 0;
    }

    private boolean isNegInf() {
        return mDenominator == 0 && mNumerator < 0;
    }

    /**
     * <p>Compare this Rational to another object and see if they are equal.</p>
     *
     * <p>A Rational object can only be equal to another Rational object (comparing against any other
     * type will return false).</p>
     *
     * <p>A Rational object is considered equal to another Rational object if and only if one of
     * the following holds</p>:
     * <ul><li>Both are NaN</li>
     *     <li>Both are infinities of the same sign</li>
     *     <li>Both have the same numerator and denominator in their reduced form</li>
     * </ul>
     *
     * <p>A reduced form of a Rational is calculated by dividing both the numerator and the
     * denominator by their greatest common divisor.</p>
     *
     * <pre>{@code
     *      (new Rational(1, 2)).equals(new Rational(1, 2)) == true   // trivially true
     *      (new Rational(2, 3)).equals(new Rational(1, 2)) == false  // trivially false
     *      (new Rational(1, 2)).equals(new Rational(2, 4)) == true   // true after reduction
     *      (new Rational(0, 0)).equals(new Rational(0, 0)) == true   // NaN.equals(NaN)
     *      (new Rational(1, 0)).equals(new Rational(5, 0)) == true   // both are +infinity
     *      (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
     * }</pre>
     *
     * @param obj a reference to another object
     *
     * @return A boolean that determines whether or not the two Rational objects are equal.
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        } else if (obj instanceof Rational) {
            Rational other = (Rational) obj;
            if (mDenominator == 0 || other.mDenominator == 0) {
                if (isNaN() && other.isNaN()) {
                    return true;
                } else if (isInf() && other.isInf() || isNegInf() && other.isNegInf()) {
                    return true;
                } else {
                    return false;
                }
            } else if (mNumerator == other.mNumerator && mDenominator == other.mDenominator) {
                return true;
            } else {
                int thisGcd = gcd();
                int otherGcd = other.gcd();

                int thisNumerator = mNumerator / thisGcd;
                int thisDenominator = mDenominator / thisGcd;

                int otherNumerator = other.mNumerator / otherGcd;
                int otherDenominator = other.mDenominator / otherGcd;

                return (thisNumerator == otherNumerator && thisDenominator == otherDenominator);
            }
        }
        return false;
    }

    @Override
    public String toString() {
        if (isNaN()) {
            return "NaN";
        } else if (isInf()) {
            return "Infinity";
        } else if (isNegInf()) {
            return "-Infinity";
        } else {
            return mNumerator + "/" + mDenominator;
        }
    }

    /**
     * <p>Convert to a floating point representation.</p>
     *
     * @return The floating point representation of this rational number.
     * @hide
     */
    public float toFloat() {
        return (float) mNumerator / (float) mDenominator;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        // Bias the hash code for the first (2^16) values for both numerator and denominator
        int numeratorFlipped = mNumerator << 16 | mNumerator >>> 16;

        return mDenominator ^ numeratorFlipped;
    }

    /**
     * Calculates the greatest common divisor using Euclid's algorithm.
     *
     * @return An int value representing the gcd. Always positive.
     * @hide
     */
    public int gcd() {
        /**
         * Non-recursive implementation of Euclid's algorithm:
         *
         *  gcd(a, 0) := a
         *  gcd(a, b) := gcd(b, a mod b)
         *
         */

        int a = mNumerator;
        int b = mDenominator;

        while (b != 0) {
            int oldB = b;

            b = a % b;
            a = oldB;
        }

        return Math.abs(a);
    }
}