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

Last change on this file since 15217 was 15217, checked in by Don-vip, 5 years ago

see #17848 - update to metadata-extractor 2.12.0

File size: 10.2 KB
Line 
1/*
2 * Copyright 2002-2019 Drew Noakes and contributors
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 */
38@SuppressWarnings("WeakerAccess")
39public class Rational extends java.lang.Number implements Comparable<Rational>, Serializable
40{
41 private static final long serialVersionUID = 510688928138848770L;
42
43 /** Holds the numerator. */
44 private final long _numerator;
45
46 /** Holds the denominator. */
47 private final long _denominator;
48
49 /**
50 * Creates a new instance of Rational. Rational objects are immutable, so
51 * once you've set your numerator and denominator values here, you're stuck
52 * with them!
53 */
54 public Rational(long numerator, long denominator)
55 {
56 _numerator = numerator;
57 _denominator = denominator;
58 }
59
60 /**
61 * Returns the value of the specified number as a <code>double</code>.
62 * This may involve rounding.
63 *
64 * @return the numeric value represented by this object after conversion
65 * to type <code>double</code>.
66 */
67 @Override
68 public double doubleValue()
69 {
70 return _numerator == 0
71 ? 0.0
72 : (double) _numerator / (double) _denominator;
73 }
74
75 /**
76 * Returns the value of the specified number as a <code>float</code>.
77 * This may involve rounding.
78 *
79 * @return the numeric value represented by this object after conversion
80 * to type <code>float</code>.
81 */
82 @Override
83 public float floatValue()
84 {
85 return _numerator == 0
86 ? 0.0f
87 : (float) _numerator / (float) _denominator;
88 }
89
90 /**
91 * Returns the value of the specified number as a <code>byte</code>.
92 * This may involve rounding or truncation. This implementation simply
93 * casts the result of {@link Rational#doubleValue} to <code>byte</code>.
94 *
95 * @return the numeric value represented by this object after conversion
96 * to type <code>byte</code>.
97 */
98 @Override
99 public final byte byteValue()
100 {
101 return (byte) doubleValue();
102 }
103
104 /**
105 * Returns the value of the specified number as an <code>int</code>.
106 * This may involve rounding or truncation. This implementation simply
107 * casts the result of {@link Rational#doubleValue} to <code>int</code>.
108 *
109 * @return the numeric value represented by this object after conversion
110 * to type <code>int</code>.
111 */
112 @Override
113 public final int intValue()
114 {
115 return (int) doubleValue();
116 }
117
118 /**
119 * Returns the value of the specified number as a <code>long</code>.
120 * This may involve rounding or truncation. This implementation simply
121 * casts the result of {@link Rational#doubleValue} to <code>long</code>.
122 *
123 * @return the numeric value represented by this object after conversion
124 * to type <code>long</code>.
125 */
126 @Override
127 public final long longValue()
128 {
129 return (long) doubleValue();
130 }
131
132 /**
133 * Returns the value of the specified number as a <code>short</code>.
134 * This may involve rounding or truncation. This implementation simply
135 * casts the result of {@link Rational#doubleValue} to <code>short</code>.
136 *
137 * @return the numeric value represented by this object after conversion
138 * to type <code>short</code>.
139 */
140 @Override
141 public final short shortValue()
142 {
143 return (short) doubleValue();
144 }
145
146
147 /** Returns the denominator. */
148 public final long getDenominator()
149 {
150 return this._denominator;
151 }
152
153 /** Returns the numerator. */
154 public final long getNumerator()
155 {
156 return this._numerator;
157 }
158
159 /**
160 * Returns the reciprocal value of this object as a new Rational.
161 *
162 * @return the reciprocal in a new object
163 */
164 @NotNull
165 public Rational getReciprocal()
166 {
167 return new Rational(this._denominator, this._numerator);
168 }
169
170 /** Checks if this {@link Rational} number is an Integer, either positive or negative. */
171 public boolean isInteger()
172 {
173 return _denominator == 1 ||
174 (_denominator != 0 && (_numerator % _denominator == 0)) ||
175 (_denominator == 0 && _numerator == 0);
176 }
177
178 /** Checks if either the numerator or denominator are zero. */
179 public boolean isZero()
180 {
181 return _numerator == 0 || _denominator == 0;
182 }
183
184 /**
185 * Returns a string representation of the object of form <code>numerator/denominator</code>.
186 *
187 * @return a string representation of the object.
188 */
189 @Override
190 @NotNull
191 public String toString()
192 {
193 return _numerator + "/" + _denominator;
194 }
195
196 /** Returns the simplest representation of this {@link Rational}'s value possible. */
197 @NotNull
198 public String toSimpleString(boolean allowDecimal)
199 {
200 if (_denominator == 0 && _numerator != 0) {
201 return toString();
202 } else if (isInteger()) {
203 return Integer.toString(intValue());
204 } else if (_numerator != 1 && _denominator % _numerator == 0) {
205 // common factor between denominator and numerator
206 long newDenominator = _denominator / _numerator;
207 return new Rational(1, newDenominator).toSimpleString(allowDecimal);
208 } else {
209 Rational simplifiedInstance = getSimplifiedInstance();
210 if (allowDecimal) {
211 String doubleString = Double.toString(simplifiedInstance.doubleValue());
212 if (doubleString.length() < 5) {
213 return doubleString;
214 }
215 }
216 return simplifiedInstance.toString();
217 }
218 }
219
220 /**
221 * Compares two {@link Rational} instances, returning true if they are mathematically
222 * equivalent (in consistence with {@link Rational#equals(Object)} method).
223 *
224 * @param that the {@link Rational} to compare this instance to.
225 * @return the value {@code 0} if this {@link Rational} is
226 * equal to the argument {@link Rational} mathematically; a value less
227 * than {@code 0} if this {@link Rational} is less
228 * than the argument {@link Rational}; and a value greater
229 * than {@code 0} if this {@link Rational} is greater than the argument
230 * {@link Rational}.
231 */
232 public int compareTo(@NotNull Rational that) {
233 return Double.compare(this.doubleValue(), that.doubleValue());
234 }
235
236 /**
237 * Indicates whether this instance and <code>other</code> are numerically equal,
238 * even if their representations differ.
239 *
240 * For example, 1/2 is equal to 10/20 by this method.
241 * Similarly, 1/0 is equal to 100/0 by this method.
242 * To test equal representations, use EqualsExact.
243 *
244 * @param other The rational value to compare with
245 */
246 public boolean equals(Rational other) {
247 return other.doubleValue() == doubleValue();
248 }
249
250 /**
251 * Indicates whether this instance and <code>other</code> have identical
252 * Numerator and Denominator.
253 * <p>
254 * For example, 1/2 is not equal to 10/20 by this method.
255 * Similarly, 1/0 is not equal to 100/0 by this method.
256 * To test numerically equivalence, use Equals(Rational).</p>
257 *
258 * @param other The rational value to compare with
259 */
260 public boolean equalsExact(Rational other) {
261 return getDenominator() == other.getDenominator() && getNumerator() == other.getNumerator();
262 }
263
264 /**
265 * Compares two {@link Rational} instances, returning true if they are mathematically
266 * equivalent.
267 *
268 * @param obj the {@link Rational} to compare this instance to.
269 * @return true if instances are mathematically equivalent, otherwise false. Will also
270 * return false if <code>obj</code> is not an instance of {@link Rational}.
271 */
272 @Override
273 public boolean equals(@Nullable Object obj)
274 {
275 if (obj==null || !(obj instanceof Rational))
276 return false;
277 Rational that = (Rational) obj;
278 return this.doubleValue() == that.doubleValue();
279 }
280
281 @Override
282 public int hashCode()
283 {
284 return (23 * (int)_denominator) + (int)_numerator;
285 }
286
287 /**
288 * <p>
289 * Simplifies the representation of this {@link Rational} number.</p>
290 * <p>
291 * For example, 5/10 simplifies to 1/2 because both Numerator
292 * and Denominator share a common factor of 5.</p>
293 * <p>
294 * Uses the Euclidean Algorithm to find the greatest common divisor.</p>
295 *
296 * @return A simplified instance if one exists, otherwise a copy of the original value.
297 */
298 @NotNull
299 public Rational getSimplifiedInstance()
300 {
301 long gcd = GCD(_numerator, _denominator);
302
303 return new Rational(_numerator / gcd, _denominator / gcd);
304 }
305
306 private static long GCD(long a, long b)
307 {
308 if (a < 0)
309 a = -a;
310 if (b < 0)
311 b = -b;
312
313 while (a != 0 && b != 0)
314 {
315 if (a > b)
316 a %= b;
317 else
318 b %= a;
319 }
320
321 return a == 0 ? b : a;
322 }
323}
Note: See TracBrowser for help on using the repository browser.