1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.hardware.camera2;
17
18/**
19 * The rational data type used by CameraMetadata keys. Contains a pair of ints representing the
20 * numerator and denominator of a Rational number. This type is immutable.
21 */
22public final class Rational {
23    private final int mNumerator;
24    private final int mDenominator;
25
26    /**
27     * <p>Create a Rational with a given numerator and denominator.</p>
28     *
29     * <p>The signs of the numerator and the denominator may be flipped such that the denominator
30     * is always positive.</p>
31     *
32     * <p>A rational value with a 0-denominator may be constructed, but will have similar semantics
33     * as float NaN and INF values. The int getter functions return 0 in this case.</p>
34     *
35     * @param numerator the numerator of the rational
36     * @param denominator the denominator of the rational
37     */
38    public Rational(int numerator, int denominator) {
39
40        if (denominator < 0) {
41            numerator = -numerator;
42            denominator = -denominator;
43        }
44
45        mNumerator = numerator;
46        mDenominator = denominator;
47    }
48
49    /**
50     * Gets the numerator of the rational.
51     */
52    public int getNumerator() {
53        if (mDenominator == 0) {
54            return 0;
55        }
56        return mNumerator;
57    }
58
59    /**
60     * Gets the denominator of the rational
61     */
62    public int getDenominator() {
63        return mDenominator;
64    }
65
66    private boolean isNaN() {
67        return mDenominator == 0 && mNumerator == 0;
68    }
69
70    private boolean isInf() {
71        return mDenominator == 0 && mNumerator > 0;
72    }
73
74    private boolean isNegInf() {
75        return mDenominator == 0 && mNumerator < 0;
76    }
77
78    /**
79     * <p>Compare this Rational to another object and see if they are equal.</p>
80     *
81     * <p>A Rational object can only be equal to another Rational object (comparing against any other
82     * type will return false).</p>
83     *
84     * <p>A Rational object is considered equal to another Rational object if and only if one of
85     * the following holds</p>:
86     * <ul><li>Both are NaN</li>
87     *     <li>Both are infinities of the same sign</li>
88     *     <li>Both have the same numerator and denominator in their reduced form</li>
89     * </ul>
90     *
91     * <p>A reduced form of a Rational is calculated by dividing both the numerator and the
92     * denominator by their greatest common divisor.</p>
93     *
94     * <pre>
95     *      (new Rational(1, 2)).equals(new Rational(1, 2)) == true   // trivially true
96     *      (new Rational(2, 3)).equals(new Rational(1, 2)) == false  // trivially false
97     *      (new Rational(1, 2)).equals(new Rational(2, 4)) == true   // true after reduction
98     *      (new Rational(0, 0)).equals(new Rational(0, 0)) == true   // NaN.equals(NaN)
99     *      (new Rational(1, 0)).equals(new Rational(5, 0)) == true   // both are +infinity
100     *      (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
101     * </pre>
102     *
103     * @param obj a reference to another object
104     *
105     * @return A boolean that determines whether or not the two Rational objects are equal.
106     */
107    @Override
108    public boolean equals(Object obj) {
109        if (obj == null) {
110            return false;
111        } else if (obj instanceof Rational) {
112            Rational other = (Rational) obj;
113            if (mDenominator == 0 || other.mDenominator == 0) {
114                if (isNaN() && other.isNaN()) {
115                    return true;
116                } else if (isInf() && other.isInf() || isNegInf() && other.isNegInf()) {
117                    return true;
118                } else {
119                    return false;
120                }
121            } else if (mNumerator == other.mNumerator && mDenominator == other.mDenominator) {
122                return true;
123            } else {
124                int thisGcd = gcd();
125                int otherGcd = other.gcd();
126
127                int thisNumerator = mNumerator / thisGcd;
128                int thisDenominator = mDenominator / thisGcd;
129
130                int otherNumerator = other.mNumerator / otherGcd;
131                int otherDenominator = other.mDenominator / otherGcd;
132
133                return (thisNumerator == otherNumerator && thisDenominator == otherDenominator);
134            }
135        }
136        return false;
137    }
138
139    @Override
140    public String toString() {
141        if (isNaN()) {
142            return "NaN";
143        } else if (isInf()) {
144            return "Infinity";
145        } else if (isNegInf()) {
146            return "-Infinity";
147        } else {
148            return mNumerator + "/" + mDenominator;
149        }
150    }
151
152    /**
153     * <p>Convert to a floating point representation.</p>
154     *
155     * @return The floating point representation of this rational number.
156     * @hide
157     */
158    public float toFloat() {
159        return (float) mNumerator / (float) mDenominator;
160    }
161
162    @Override
163    public int hashCode() {
164        final long INT_MASK = 0xffffffffL;
165
166        long asLong = INT_MASK & mNumerator;
167        asLong <<= 32;
168
169        asLong |= (INT_MASK & mDenominator);
170
171        return ((Long)asLong).hashCode();
172    }
173
174    /**
175     * Calculates the greatest common divisor using Euclid's algorithm.
176     *
177     * @return An int value representing the gcd. Always positive.
178     * @hide
179     */
180    public int gcd() {
181        /**
182         * Non-recursive implementation of Euclid's algorithm:
183         *
184         *  gcd(a, 0) := a
185         *  gcd(a, b) := gcd(b, a mod b)
186         *
187         */
188
189        int a = mNumerator;
190        int b = mDenominator;
191
192        while (b != 0) {
193            int oldB = b;
194
195            b = a % b;
196            a = oldB;
197        }
198
199        return Math.abs(a);
200    }
201}
202