source: josm/trunk/src/com/drew/lang/Rational.java@ 7152

Last change on this file since 7152 was 6127, checked in by bastiK, 11 years ago

applied #8895 - Upgrade metadata-extractor to v. 2.6.4 (patch by ebourg)

File size: 9.6 KB
Line 
1/*
2 * Copyright 2002-2012 Drew Noakes
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 *
16 * More information about this project is available at:
17 *
18 * http://drewnoakes.com/code/exif/
19 * http://code.google.com/p/metadata-extractor/
20 */
21
22package com.drew.lang;
23
24import com.drew.lang.annotations.NotNull;
25import com.drew.lang.annotations.Nullable;
26
27import java.io.Serializable;
28
29/**
30 * Immutable class for holding a rational number without loss of precision. Provides
31 * a familiar representation via toString() in form <code>numerator/denominator</code>.
32 *
33 * @author Drew Noakes http://drewnoakes.com
34 */
35public class Rational extends java.lang.Number implements Serializable
36{
37 private static final long serialVersionUID = 510688928138848770L;
38
39 /** Holds the numerator. */
40 private final long _numerator;
41
42 /** Holds the denominator. */
43 private final long _denominator;
44
45 /**
46 * Creates a new instance of Rational. Rational objects are immutable, so
47 * once you've set your numerator and denominator values here, you're stuck
48 * with them!
49 */
50 public Rational(long numerator, long denominator)
51 {
52 _numerator = numerator;
53 _denominator = denominator;
54 }
55
56 /**
57 * Returns the value of the specified number as a <code>double</code>.
58 * This may involve rounding.
59 *
60 * @return the numeric value represented by this object after conversion
61 * to type <code>double</code>.
62 */
63 public double doubleValue()
64 {
65 return (double) _numerator / (double) _denominator;
66 }
67
68 /**
69 * Returns the value of the specified number as a <code>float</code>.
70 * This may involve rounding.
71 *
72 * @return the numeric value represented by this object after conversion
73 * to type <code>float</code>.
74 */
75 public float floatValue()
76 {
77 return (float) _numerator / (float) _denominator;
78 }
79
80 /**
81 * Returns the value of the specified number as a <code>byte</code>.
82 * This may involve rounding or truncation. This implementation simply
83 * casts the result of <code>doubleValue()</code> to <code>byte</code>.
84 *
85 * @return the numeric value represented by this object after conversion
86 * to type <code>byte</code>.
87 */
88 public final byte byteValue()
89 {
90 return (byte) doubleValue();
91 }
92
93 /**
94 * Returns the value of the specified number as an <code>int</code>.
95 * This may involve rounding or truncation. This implementation simply
96 * casts the result of <code>doubleValue()</code> to <code>int</code>.
97 *
98 * @return the numeric value represented by this object after conversion
99 * to type <code>int</code>.
100 */
101 public final int intValue()
102 {
103 return (int) doubleValue();
104 }
105
106 /**
107 * Returns the value of the specified number as a <code>long</code>.
108 * This may involve rounding or truncation. This implementation simply
109 * casts the result of <code>doubleValue()</code> to <code>long</code>.
110 *
111 * @return the numeric value represented by this object after conversion
112 * to type <code>long</code>.
113 */
114 public final long longValue()
115 {
116 return (long) doubleValue();
117 }
118
119 /**
120 * Returns the value of the specified number as a <code>short</code>.
121 * This may involve rounding or truncation. This implementation simply
122 * casts the result of <code>doubleValue()</code> to <code>short</code>.
123 *
124 * @return the numeric value represented by this object after conversion
125 * to type <code>short</code>.
126 */
127 public final short shortValue()
128 {
129 return (short) doubleValue();
130 }
131
132
133 /** Returns the denominator. */
134 public final long getDenominator()
135 {
136 return this._denominator;
137 }
138
139 /** Returns the numerator. */
140 public final long getNumerator()
141 {
142 return this._numerator;
143 }
144
145 /**
146 * Returns the reciprocal value of this object as a new Rational.
147 *
148 * @return the reciprocal in a new object
149 */
150 @NotNull
151 public Rational getReciprocal()
152 {
153 return new Rational(this._denominator, this._numerator);
154 }
155
156 /** Checks if this rational number is an Integer, either positive or negative. */
157 public boolean isInteger()
158 {
159 return _denominator == 1 ||
160 (_denominator != 0 && (_numerator % _denominator == 0)) ||
161 (_denominator == 0 && _numerator == 0);
162 }
163
164 /**
165 * Returns a string representation of the object of form <code>numerator/denominator</code>.
166 *
167 * @return a string representation of the object.
168 */
169 @NotNull
170 public String toString()
171 {
172 return _numerator + "/" + _denominator;
173 }
174
175 /** Returns the simplest representation of this Rational's value possible. */
176 @NotNull
177 public String toSimpleString(boolean allowDecimal)
178 {
179 if (_denominator == 0 && _numerator != 0) {
180 return toString();
181 } else if (isInteger()) {
182 return Integer.toString(intValue());
183 } else if (_numerator != 1 && _denominator % _numerator == 0) {
184 // common factor between denominator and numerator
185 long newDenominator = _denominator / _numerator;
186 return new Rational(1, newDenominator).toSimpleString(allowDecimal);
187 } else {
188 Rational simplifiedInstance = getSimplifiedInstance();
189 if (allowDecimal) {
190 String doubleString = Double.toString(simplifiedInstance.doubleValue());
191 if (doubleString.length() < 5) {
192 return doubleString;
193 }
194 }
195 return simplifiedInstance.toString();
196 }
197 }
198
199 /**
200 * Decides whether a brute-force simplification calculation should be avoided
201 * by comparing the maximum number of possible calculations with some threshold.
202 *
203 * @return true if the simplification should be performed, otherwise false
204 */
205 private boolean tooComplexForSimplification()
206 {
207 double maxPossibleCalculations = (((double) (Math.min(_denominator, _numerator) - 1) / 5d) + 2);
208 final int maxSimplificationCalculations = 1000;
209 return maxPossibleCalculations > maxSimplificationCalculations;
210 }
211
212 /**
213 * Compares two <code>Rational</code> instances, returning true if they are mathematically
214 * equivalent.
215 *
216 * @param obj the Rational to compare this instance to.
217 * @return true if instances are mathematically equivalent, otherwise false. Will also
218 * return false if <code>obj</code> is not an instance of <code>Rational</code>.
219 */
220 @Override
221 public boolean equals(@Nullable Object obj)
222 {
223 if (obj==null || !(obj instanceof Rational))
224 return false;
225 Rational that = (Rational) obj;
226 return this.doubleValue() == that.doubleValue();
227 }
228
229 @Override
230 public int hashCode()
231 {
232 return (23 * (int)_denominator) + (int)_numerator;
233 }
234
235 /**
236 * <p>
237 * Simplifies the Rational number.</p>
238 * <p>
239 * Prime number series: 1, 2, 3, 5, 7, 9, 11, 13, 17</p>
240 * <p>
241 * To reduce a rational, need to see if both numerator and denominator are divisible
242 * by a common factor. Using the prime number series in ascending order guarantees
243 * the minimum number of checks required.</p>
244 * <p>
245 * However, generating the prime number series seems to be a hefty task. Perhaps
246 * it's simpler to check if both d & n are divisible by all numbers from 2 ->
247 * (Math.min(denominator, numerator) / 2). In doing this, one can check for 2
248 * and 5 once, then ignore all even numbers, and all numbers ending in 0 or 5.
249 * This leaves four numbers from every ten to check.</p>
250 * <p>
251 * Therefore, the max number of pairs of modulus divisions required will be:</p>
252 * <code><pre>
253 * 4 Math.min(denominator, numerator) - 1
254 * -- * ------------------------------------ + 2
255 * 10 2
256 * <p/>
257 * Math.min(denominator, numerator) - 1
258 * = ------------------------------------ + 2
259 * 5
260 * </pre></code>
261 *
262 * @return a simplified instance, or if the Rational could not be simplified,
263 * returns itself (unchanged)
264 */
265 @NotNull
266 public Rational getSimplifiedInstance()
267 {
268 if (tooComplexForSimplification()) {
269 return this;
270 }
271 for (int factor = 2; factor <= Math.min(_denominator, _numerator); factor++) {
272 if ((factor % 2 == 0 && factor > 2) || (factor % 5 == 0 && factor > 5)) {
273 continue;
274 }
275 if (_denominator % factor == 0 && _numerator % factor == 0) {
276 // found a common factor
277 return new Rational(_numerator / factor, _denominator / factor);
278 }
279 }
280 return this;
281 }
282}
Note: See TracBrowser for help on using the repository browser.