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

Last change on this file since 12187 was 10862, checked in by Don-vip, 8 years ago

update to metadata-extractor 2.9.1

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