Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/drew/lang/Rational.java


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  
33  package com.drew.lang;
34  
35  import 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   */
43  public 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 }