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

Last change on this file since 4258 was 4231, checked in by stoecker, 13 years ago

add signpost and metadata extractor code to repository directly

File size: 9.9 KB
Line 
1/*
2 * Rational.java
3 *
4 * This class is public domain software - that is, you can do whatever you want
5 * with it, and include it software that is licensed under the GNU or the
6 * BSD license, or whatever other licence you choose, including proprietary
7 * closed source licenses. Similarly, I release this Java version under the
8 * same license, though I do ask that you leave this header in tact.
9 *
10 * If you make modifications to this code that you think would benefit the
11 * wider community, please send me a copy and I'll post it on my site.
12 *
13 * If you make use of this code, I'd appreciate hearing about it.
14 * drew.noakes@drewnoakes.com
15 * Latest version of this software kept at
16 * http://drewnoakes.com/
17 *
18 * Created on 6 May 2002, 18:06
19 * Updated 26 Aug 2002 by Drew
20 * - Added toSimpleString() method, which returns a simplified and hopefully more
21 * readable version of the Rational. i.e. 2/10 -> 1/5, and 10/2 -> 5
22 * Modified 29 Oct 2002 (v1.2)
23 * - Improved toSimpleString() to factor more complex rational numbers into
24 * a simpler form
25 * i.e.
26 * 10/15 -> 2/3
27 * - toSimpleString() now accepts a boolean flag, 'allowDecimals' which will
28 * display the rational number in decimal form if it fits within 5 digits
29 * i.e.
30 * 3/4 -> 0.75 when allowDecimal == true
31 */
32
33package com.drew.lang;
34
35import java.io.Serializable;
36
37/**
38 * Immutable class for holding a rational number without loss of precision. Provides
39 * a familiar representation via toString() in form <code>numerator/denominator</code>.
40 * <p>
41 * @author Drew Noakes http://drewnoakes.com
42 */
43public class Rational extends java.lang.Number implements Serializable
44{
45 /**
46 * Holds the numerator.
47 */
48 private final int numerator;
49
50 /**
51 * Holds the denominator.
52 */
53 private final int denominator;
54
55 private int maxSimplificationCalculations = 1000;
56
57 /**
58 * Creates a new instance of Rational. Rational objects are immutable, so
59 * once you've set your numerator and denominator values here, you're stuck
60 * with them!
61 */
62 public Rational(int numerator, int denominator)
63 {
64 this.numerator = numerator;
65 this.denominator = denominator;
66 }
67
68 /**
69 * Returns the value of the specified number as a <code>double</code>.
70 * This may involve rounding.
71 *
72 * @return the numeric value represented by this object after conversion
73 * to type <code>double</code>.
74 */
75 public double doubleValue()
76 {
77 return (double)numerator / (double)denominator;
78 }
79
80 /**
81 * Returns the value of the specified number as a <code>float</code>.
82 * This may involve rounding.
83 *
84 * @return the numeric value represented by this object after conversion
85 * to type <code>float</code>.
86 */
87 public float floatValue()
88 {
89 return (float)numerator / (float)denominator;
90 }
91
92 /**
93 * Returns the value of the specified number as a <code>byte</code>.
94 * This may involve rounding or truncation. This implementation simply
95 * casts the result of <code>doubleValue()</code> to <code>byte</code>.
96 *
97 * @return the numeric value represented by this object after conversion
98 * to type <code>byte</code>.
99 */
100 public final byte byteValue()
101 {
102 return (byte)doubleValue();
103 }
104
105 /**
106 * Returns the value of the specified number as an <code>int</code>.
107 * This may involve rounding or truncation. This implementation simply
108 * casts the result of <code>doubleValue()</code> to <code>int</code>.
109 *
110 * @return the numeric value represented by this object after conversion
111 * to type <code>int</code>.
112 */
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 <code>doubleValue()</code> to <code>long</code>.
122 *
123 * @return the numeric value represented by this object after conversion
124 * to type <code>long</code>.
125 */
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 <code>doubleValue()</code> to <code>short</code>.
135 *
136 * @return the numeric value represented by this object after conversion
137 * to type <code>short</code>.
138 */
139 public final short shortValue()
140 {
141 return (short)doubleValue();
142 }
143
144
145 /**
146 * Returns the denominator.
147 */
148 public final int getDenominator()
149 {
150 return this.denominator;
151 }
152
153 /**
154 * Returns the numerator.
155 */
156 public final int getNumerator()
157 {
158 return this.numerator;
159 }
160
161 /**
162 * Returns the reciprocal value of this obejct as a new Rational.
163 * @return the reciprocal in a new object
164 */
165 public Rational getReciprocal()
166 {
167 return new Rational(this.denominator, this.numerator);
168 }
169
170 /**
171 * Checks if this rational number is an Integer, either positive or negative.
172 */
173 public boolean isInteger()
174 {
175 if (denominator == 1 ||
176 (denominator != 0 && (numerator % denominator == 0)) ||
177 (denominator == 0 && numerator == 0)
178 ) {
179 return true;
180 } else {
181 return false;
182 }
183 }
184
185 /**
186 * Returns a string representation of the object of form <code>numerator/denominator</code>.
187 * @return a string representation of the object.
188 */
189 public String toString()
190 {
191 return numerator + "/" + denominator;
192 }
193
194 /**
195 * Returns the simplest represenation of this Rational's value possible.
196 */
197 public String toSimpleString(boolean allowDecimal)
198 {
199 if (denominator == 0 && numerator != 0) {
200 return toString();
201 } else if (isInteger()) {
202 return Integer.toString(intValue());
203 } else if (numerator != 1 && denominator % numerator == 0) {
204 // common factor between denominator and numerator
205 int newDenominator = denominator / numerator;
206 return new Rational(1, newDenominator).toSimpleString(allowDecimal);
207 } else {
208 Rational simplifiedInstance = getSimplifiedInstance();
209 if (allowDecimal) {
210 String doubleString = Double.toString(simplifiedInstance.doubleValue());
211 if (doubleString.length() < 5) {
212 return doubleString;
213 }
214 }
215 return simplifiedInstance.toString();
216 }
217 }
218
219 /**
220 * Decides whether a brute-force simplification calculation should be avoided
221 * by comparing the maximum number of possible calculations with some threshold.
222 * @return true if the simplification should be performed, otherwise false
223 */
224 private boolean tooComplexForSimplification()
225 {
226 double maxPossibleCalculations = (((double)(Math.min(denominator, numerator) - 1) / 5d) + 2);
227 return maxPossibleCalculations > maxSimplificationCalculations;
228 }
229
230 /**
231 * Compares two <code>Rational</code> instances, returning true if they are mathematically
232 * equivalent.
233 * @param obj the Rational to compare this instance to.
234 * @return true if instances are mathematically equivalent, otherwise false. Will also
235 * return false if <code>obj</code> is not an instance of <code>Rational</code>.
236 */
237 public boolean equals(Object obj)
238 {
239 if (!(obj instanceof Rational)) {
240 return false;
241 }
242 Rational that = (Rational)obj;
243 return this.doubleValue() == that.doubleValue();
244 }
245
246 /**
247 * <p>
248 * Simplifies the Rational number.</p>
249 * <p>
250 * Prime number series: 1, 2, 3, 5, 7, 9, 11, 13, 17</p>
251 * <p>
252 * To reduce a rational, need to see if both numerator and denominator are divisible
253 * by a common factor. Using the prime number series in ascending order guarantees
254 * the minimun number of checks required.</p>
255 * <p>
256 * However, generating the prime number series seems to be a hefty task. Perhaps
257 * it's simpler to check if both d & n are divisible by all numbers from 2 ->
258 * (Math.min(denominator, numerator) / 2). In doing this, one can check for 2
259 * and 5 once, then ignore all even numbers, and all numbers ending in 0 or 5.
260 * This leaves four numbers from every ten to check.</p>
261 * <p>
262 * Therefore, the max number of pairs of modulus divisions required will be:</p>
263 * <code><pre>
264 * 4 Math.min(denominator, numerator) - 1
265 * -- * ------------------------------------ + 2
266 * 10 2
267 *
268 * Math.min(denominator, numerator) - 1
269 * = ------------------------------------ + 2
270 * 5
271 * </pre></code>
272 * @return a simplified instance, or if the Rational could not be simpliffied,
273 * returns itself (unchanged)
274 */
275 public Rational getSimplifiedInstance()
276 {
277 if (tooComplexForSimplification()) {
278 return this;
279 }
280 for (int factor = 2; factor <= Math.min(denominator, numerator); factor++) {
281 if ((factor % 2 == 0 && factor > 2) || (factor % 5 == 0 && factor > 5)) {
282 continue;
283 }
284 if (denominator % factor == 0 && numerator % factor == 0) {
285 // found a common factor
286 return new Rational(numerator / factor, denominator / factor);
287 }
288 }
289 return this;
290 }
291}
Note: See TracBrowser for help on using the repository browser.