1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math.fraction;
18  
19  import org.apache.commons.math.ConvergenceException;
20  import org.apache.commons.math.TestUtils;
21  
22  import junit.framework.TestCase;
23  
24  /**
25   * @version $Revision: 795903 $ $Date: 2009-07-20 12:29:46 -0400 (Mon, 20 Jul 2009) $
26   */
27  public class FractionTest extends TestCase {
28  
29      private void assertFraction(int expectedNumerator, int expectedDenominator, Fraction actual) {
30          assertEquals(expectedNumerator, actual.getNumerator());
31          assertEquals(expectedDenominator, actual.getDenominator());
32      }
33      
34      public void testConstructor() {
35          assertFraction(0, 1, new Fraction(0, 1));
36          assertFraction(0, 1, new Fraction(0, 2));
37          assertFraction(0, 1, new Fraction(0, -1));
38          assertFraction(1, 2, new Fraction(1, 2));
39          assertFraction(1, 2, new Fraction(2, 4));
40          assertFraction(-1, 2, new Fraction(-1, 2));
41          assertFraction(-1, 2, new Fraction(1, -2));
42          assertFraction(-1, 2, new Fraction(-2, 4));
43          assertFraction(-1, 2, new Fraction(2, -4));
44          
45          // overflow
46          try {
47              new Fraction(Integer.MIN_VALUE, -1);
48              fail();
49          } catch (ArithmeticException ex) {
50              // success
51          }
52          try {
53              new Fraction(1, Integer.MIN_VALUE);
54              fail();
55          } catch (ArithmeticException ex) {
56              // success
57          }
58          try {        
59              assertFraction(0, 1, new Fraction(0.00000000000001));
60              assertFraction(2, 5, new Fraction(0.40000000000001));
61              assertFraction(15, 1, new Fraction(15.0000000000001));
62              
63          } catch (ConvergenceException ex) {
64              fail(ex.getMessage());
65          }
66      }
67  
68      public void testGoldenRatio() {
69          try {
70              // the golden ratio is notoriously a difficult number for continuous fraction
71              new Fraction((1 + Math.sqrt(5)) / 2, 1.0e-12, 25);
72              fail("an exception should have been thrown");
73          } catch (ConvergenceException ce) {
74              // expected behavior
75          } catch (Exception e) {
76              fail("wrong exception caught");
77          }
78      }
79  
80      // MATH-179
81      public void testDoubleConstructor() throws ConvergenceException  {
82          assertFraction(1, 2, new Fraction((double)1 / (double)2));
83          assertFraction(1, 3, new Fraction((double)1 / (double)3));
84          assertFraction(2, 3, new Fraction((double)2 / (double)3));
85          assertFraction(1, 4, new Fraction((double)1 / (double)4));
86          assertFraction(3, 4, new Fraction((double)3 / (double)4));
87          assertFraction(1, 5, new Fraction((double)1 / (double)5));
88          assertFraction(2, 5, new Fraction((double)2 / (double)5));
89          assertFraction(3, 5, new Fraction((double)3 / (double)5));
90          assertFraction(4, 5, new Fraction((double)4 / (double)5));
91          assertFraction(1, 6, new Fraction((double)1 / (double)6));
92          assertFraction(5, 6, new Fraction((double)5 / (double)6));
93          assertFraction(1, 7, new Fraction((double)1 / (double)7));
94          assertFraction(2, 7, new Fraction((double)2 / (double)7));
95          assertFraction(3, 7, new Fraction((double)3 / (double)7));
96          assertFraction(4, 7, new Fraction((double)4 / (double)7));
97          assertFraction(5, 7, new Fraction((double)5 / (double)7));
98          assertFraction(6, 7, new Fraction((double)6 / (double)7));
99          assertFraction(1, 8, new Fraction((double)1 / (double)8));
100         assertFraction(3, 8, new Fraction((double)3 / (double)8));
101         assertFraction(5, 8, new Fraction((double)5 / (double)8));
102         assertFraction(7, 8, new Fraction((double)7 / (double)8));
103         assertFraction(1, 9, new Fraction((double)1 / (double)9));
104         assertFraction(2, 9, new Fraction((double)2 / (double)9));
105         assertFraction(4, 9, new Fraction((double)4 / (double)9));
106         assertFraction(5, 9, new Fraction((double)5 / (double)9));
107         assertFraction(7, 9, new Fraction((double)7 / (double)9));
108         assertFraction(8, 9, new Fraction((double)8 / (double)9));
109         assertFraction(1, 10, new Fraction((double)1 / (double)10));
110         assertFraction(3, 10, new Fraction((double)3 / (double)10));
111         assertFraction(7, 10, new Fraction((double)7 / (double)10));
112         assertFraction(9, 10, new Fraction((double)9 / (double)10));
113         assertFraction(1, 11, new Fraction((double)1 / (double)11));
114         assertFraction(2, 11, new Fraction((double)2 / (double)11));
115         assertFraction(3, 11, new Fraction((double)3 / (double)11));
116         assertFraction(4, 11, new Fraction((double)4 / (double)11));
117         assertFraction(5, 11, new Fraction((double)5 / (double)11));
118         assertFraction(6, 11, new Fraction((double)6 / (double)11));
119         assertFraction(7, 11, new Fraction((double)7 / (double)11));
120         assertFraction(8, 11, new Fraction((double)8 / (double)11));
121         assertFraction(9, 11, new Fraction((double)9 / (double)11));
122         assertFraction(10, 11, new Fraction((double)10 / (double)11));
123     }
124 
125     // MATH-181
126     public void testDigitLimitConstructor() throws ConvergenceException  {
127         assertFraction(2, 5, new Fraction(0.4,   9));
128         assertFraction(2, 5, new Fraction(0.4,  99));
129         assertFraction(2, 5, new Fraction(0.4, 999));
130 
131         assertFraction(3, 5,      new Fraction(0.6152,    9));
132         assertFraction(8, 13,     new Fraction(0.6152,   99));
133         assertFraction(510, 829,  new Fraction(0.6152,  999));
134         assertFraction(769, 1250, new Fraction(0.6152, 9999));
135     }
136 
137     public void testIntegerOverflow() {
138         checkIntegerOverflow(0.75000000001455192);
139         checkIntegerOverflow(1.0e10);
140     }
141 
142     private void checkIntegerOverflow(double a) {
143         try {
144             new Fraction(a, 1.0e-12, 1000);
145             fail("an exception should have been thrown");
146         } catch (ConvergenceException ce) {
147             // expected behavior
148         } catch (Exception e) {
149             fail("wrong exception caught");
150         }
151     }
152 
153     public void testEpsilonLimitConstructor() throws ConvergenceException  {
154         assertFraction(2, 5, new Fraction(0.4, 1.0e-5, 100));
155 
156         assertFraction(3, 5,      new Fraction(0.6152, 0.02, 100));
157         assertFraction(8, 13,     new Fraction(0.6152, 1.0e-3, 100));
158         assertFraction(251, 408,  new Fraction(0.6152, 1.0e-4, 100));
159         assertFraction(251, 408,  new Fraction(0.6152, 1.0e-5, 100));
160         assertFraction(510, 829,  new Fraction(0.6152, 1.0e-6, 100));
161         assertFraction(769, 1250, new Fraction(0.6152, 1.0e-7, 100));
162     }
163 
164     public void testCompareTo() {
165         Fraction first = new Fraction(1, 2);
166         Fraction second = new Fraction(1, 3);
167         Fraction third = new Fraction(1, 2);
168         
169         assertEquals(0, first.compareTo(first));
170         assertEquals(0, first.compareTo(third));
171         assertEquals(1, first.compareTo(second));
172         assertEquals(-1, second.compareTo(first));
173 
174         // these two values are different approximations of PI
175         // the first  one is approximately PI - 3.07e-18
176         // the second one is approximately PI + 1.936e-17
177         Fraction pi1 = new Fraction(1068966896, 340262731);
178         Fraction pi2 = new Fraction( 411557987, 131002976);
179         assertEquals(-1, pi1.compareTo(pi2));
180         assertEquals( 1, pi2.compareTo(pi1));
181         assertEquals(0.0, pi1.doubleValue() - pi2.doubleValue(), 1.0e-20);
182     }
183     
184     public void testDoubleValue() {
185         Fraction first = new Fraction(1, 2);
186         Fraction second = new Fraction(1, 3);
187 
188         assertEquals(0.5, first.doubleValue(), 0.0);
189         assertEquals(1.0 / 3.0, second.doubleValue(), 0.0);
190     }
191     
192     public void testFloatValue() {
193         Fraction first = new Fraction(1, 2);
194         Fraction second = new Fraction(1, 3);
195 
196         assertEquals(0.5f, first.floatValue(), 0.0f);
197         assertEquals((float)(1.0 / 3.0), second.floatValue(), 0.0f);
198     }
199     
200     public void testIntValue() {
201         Fraction first = new Fraction(1, 2);
202         Fraction second = new Fraction(3, 2);
203 
204         assertEquals(0, first.intValue());
205         assertEquals(1, second.intValue());
206     }
207     
208     public void testLongValue() {
209         Fraction first = new Fraction(1, 2);
210         Fraction second = new Fraction(3, 2);
211 
212         assertEquals(0L, first.longValue());
213         assertEquals(1L, second.longValue());
214     }
215     
216     public void testConstructorDouble() {
217         try {
218             assertFraction(1, 2, new Fraction(0.5));
219             assertFraction(1, 3, new Fraction(1.0 / 3.0));
220             assertFraction(17, 100, new Fraction(17.0 / 100.0));
221             assertFraction(317, 100, new Fraction(317.0 / 100.0));
222             assertFraction(-1, 2, new Fraction(-0.5));
223             assertFraction(-1, 3, new Fraction(-1.0 / 3.0));
224             assertFraction(-17, 100, new Fraction(17.0 / -100.0));
225             assertFraction(-317, 100, new Fraction(-317.0 / 100.0));
226         } catch (ConvergenceException ex) {
227             fail(ex.getMessage());
228         }
229     }
230     
231     public void testAbs() {
232         Fraction a = new Fraction(10, 21);
233         Fraction b = new Fraction(-10, 21);
234         Fraction c = new Fraction(10, -21);
235         
236         assertFraction(10, 21, a.abs());
237         assertFraction(10, 21, b.abs());
238         assertFraction(10, 21, c.abs());
239     }
240     
241     public void testReciprocal() {
242         Fraction f = null;
243         
244         f = new Fraction(50, 75);
245         f = f.reciprocal();
246         assertEquals(3, f.getNumerator());
247         assertEquals(2, f.getDenominator());
248         
249         f = new Fraction(4, 3);
250         f = f.reciprocal();
251         assertEquals(3, f.getNumerator());
252         assertEquals(4, f.getDenominator());
253         
254         f = new Fraction(-15, 47);
255         f = f.reciprocal();
256         assertEquals(-47, f.getNumerator());
257         assertEquals(15, f.getDenominator());
258         
259         f = new Fraction(0, 3);
260         try {
261             f = f.reciprocal();
262             fail("expecting ArithmeticException");
263         } catch (ArithmeticException ex) {}
264 
265         // large values
266         f = new Fraction(Integer.MAX_VALUE, 1);
267         f = f.reciprocal();
268         assertEquals(1, f.getNumerator());
269         assertEquals(Integer.MAX_VALUE, f.getDenominator());
270     }
271     
272     public void testNegate() {
273         Fraction f = null;
274         
275         f = new Fraction(50, 75);
276         f = f.negate();
277         assertEquals(-2, f.getNumerator());
278         assertEquals(3, f.getDenominator());
279         
280         f = new Fraction(-50, 75);
281         f = f.negate();
282         assertEquals(2, f.getNumerator());
283         assertEquals(3, f.getDenominator());
284 
285         // large values
286         f = new Fraction(Integer.MAX_VALUE-1, Integer.MAX_VALUE);
287         f = f.negate();
288         assertEquals(Integer.MIN_VALUE+2, f.getNumerator());
289         assertEquals(Integer.MAX_VALUE, f.getDenominator());
290 
291         f = new Fraction(Integer.MIN_VALUE, 1);
292         try {
293             f = f.negate();
294             fail("expecting ArithmeticException");
295         } catch (ArithmeticException ex) {}
296     }
297     
298     public void testAdd() {
299         Fraction a = new Fraction(1, 2);
300         Fraction b = new Fraction(2, 3);
301         
302         assertFraction(1, 1, a.add(a));
303         assertFraction(7, 6, a.add(b));
304         assertFraction(7, 6, b.add(a));
305         assertFraction(4, 3, b.add(b));
306         
307         Fraction f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
308         Fraction f2 = Fraction.ONE;
309         Fraction f = f1.add(f2);
310         assertEquals(Integer.MAX_VALUE, f.getNumerator());
311         assertEquals(1, f.getDenominator());
312         f = f1.add(1);
313         assertEquals(Integer.MAX_VALUE, f.getNumerator());
314         assertEquals(1, f.getDenominator());
315         
316         f1 = new Fraction(-1, 13*13*2*2);
317         f2 = new Fraction(-2, 13*17*2);
318         f = f1.add(f2);
319         assertEquals(13*13*17*2*2, f.getDenominator());
320         assertEquals(-17 - 2*13*2, f.getNumerator());
321         
322         try {
323             f.add(null);
324             fail("expecting IllegalArgumentException");
325         } catch (IllegalArgumentException ex) {}
326         
327         // if this fraction is added naively, it will overflow.
328         // check that it doesn't.
329         f1 = new Fraction(1,32768*3);
330         f2 = new Fraction(1,59049);
331         f = f1.add(f2);
332         assertEquals(52451, f.getNumerator());
333         assertEquals(1934917632, f.getDenominator());
334 
335         f1 = new Fraction(Integer.MIN_VALUE, 3);
336         f2 = new Fraction(1,3);
337         f = f1.add(f2);
338         assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
339         assertEquals(3, f.getDenominator());
340         
341         f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
342         f2 = Fraction.ONE;
343         f = f1.add(f2);
344         assertEquals(Integer.MAX_VALUE, f.getNumerator());
345         assertEquals(1, f.getDenominator());
346         
347         try {
348             f = f.add(Fraction.ONE); // should overflow
349             fail("expecting ArithmeticException but got: " + f.toString());
350         } catch (ArithmeticException ex) {}
351         
352         // denominator should not be a multiple of 2 or 3 to trigger overflow
353         f1 = new Fraction(Integer.MIN_VALUE, 5);
354         f2 = new Fraction(-1,5);
355         try {
356             f = f1.add(f2); // should overflow
357             fail("expecting ArithmeticException but got: " + f.toString());
358         } catch (ArithmeticException ex) {}
359         
360         try {
361             f= new Fraction(-Integer.MAX_VALUE, 1);
362             f = f.add(f);
363             fail("expecting ArithmeticException");
364         } catch (ArithmeticException ex) {}
365         
366         try {
367             f= new Fraction(-Integer.MAX_VALUE, 1);
368             f = f.add(f);
369             fail("expecting ArithmeticException");
370         } catch (ArithmeticException ex) {}
371         
372         f1 = new Fraction(3,327680);
373         f2 = new Fraction(2,59049);
374         try {
375             f = f1.add(f2); // should overflow
376             fail("expecting ArithmeticException but got: " + f.toString());
377         } catch (ArithmeticException ex) {}
378     }
379     
380     public void testDivide() {
381         Fraction a = new Fraction(1, 2);
382         Fraction b = new Fraction(2, 3);
383         
384         assertFraction(1, 1, a.divide(a));
385         assertFraction(3, 4, a.divide(b));
386         assertFraction(4, 3, b.divide(a));
387         assertFraction(1, 1, b.divide(b));
388         
389         Fraction f1 = new Fraction(3, 5);
390         Fraction f2 = Fraction.ZERO;
391         try {
392             f1.divide(f2);
393             fail("expecting ArithmeticException");
394         } catch (ArithmeticException ex) {}
395         
396         f1 = new Fraction(0, 5);
397         f2 = new Fraction(2, 7);
398         Fraction f = f1.divide(f2);
399         assertSame(Fraction.ZERO, f);
400         
401         f1 = new Fraction(2, 7);
402         f2 = Fraction.ONE;
403         f = f1.divide(f2);
404         assertEquals(2, f.getNumerator());
405         assertEquals(7, f.getDenominator());
406         
407         f1 = new Fraction(1, Integer.MAX_VALUE);
408         f = f1.divide(f1);  
409         assertEquals(1, f.getNumerator());
410         assertEquals(1, f.getDenominator());
411         
412         f1 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
413         f2 = new Fraction(1, Integer.MAX_VALUE);
414         f = f1.divide(f2);
415         assertEquals(Integer.MIN_VALUE, f.getNumerator());
416         assertEquals(1, f.getDenominator());
417 
418         try {
419             f.divide(null);
420             fail("IllegalArgumentException");
421         } catch (IllegalArgumentException ex) {}
422         
423         try {
424             f1 = new Fraction(1, Integer.MAX_VALUE);
425             f = f1.divide(f1.reciprocal());  // should overflow
426             fail("expecting ArithmeticException");
427         } catch (ArithmeticException ex) {}
428         try {
429             f1 = new Fraction(1, -Integer.MAX_VALUE);
430             f = f1.divide(f1.reciprocal());  // should overflow
431             fail("expecting ArithmeticException");
432         } catch (ArithmeticException ex) {}
433 
434         f1 = new Fraction(6, 35);
435         f  = f1.divide(15);
436         assertEquals(2, f.getNumerator());
437         assertEquals(175, f.getDenominator());
438 
439     }
440     
441     public void testMultiply() {
442         Fraction a = new Fraction(1, 2);
443         Fraction b = new Fraction(2, 3);
444         
445         assertFraction(1, 4, a.multiply(a));
446         assertFraction(1, 3, a.multiply(b));
447         assertFraction(1, 3, b.multiply(a));
448         assertFraction(4, 9, b.multiply(b));
449         
450         Fraction f1 = new Fraction(Integer.MAX_VALUE, 1);
451         Fraction f2 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
452         Fraction f = f1.multiply(f2);
453         assertEquals(Integer.MIN_VALUE, f.getNumerator());
454         assertEquals(1, f.getDenominator());
455 
456         try {
457             f.multiply(null);
458             fail("expecting IllegalArgumentException");
459         } catch (IllegalArgumentException ex) {}
460 
461         f1 = new Fraction(6, 35);
462         f  = f1.multiply(15);
463         assertEquals(18, f.getNumerator());
464         assertEquals(7, f.getDenominator());
465     }
466     
467     public void testSubtract() {
468         Fraction a = new Fraction(1, 2);
469         Fraction b = new Fraction(2, 3);
470         
471         assertFraction(0, 1, a.subtract(a));
472         assertFraction(-1, 6, a.subtract(b));
473         assertFraction(1, 6, b.subtract(a));
474         assertFraction(0, 1, b.subtract(b));
475         
476         Fraction f = new Fraction(1,1);
477         try {
478             f.subtract(null);
479             fail("expecting IllegalArgumentException");
480         } catch (IllegalArgumentException ex) {}
481         
482         // if this fraction is subtracted naively, it will overflow.
483         // check that it doesn't.
484         Fraction f1 = new Fraction(1,32768*3);
485         Fraction f2 = new Fraction(1,59049);
486         f = f1.subtract(f2);
487         assertEquals(-13085, f.getNumerator());
488         assertEquals(1934917632, f.getDenominator());
489 
490         f1 = new Fraction(Integer.MIN_VALUE, 3);
491         f2 = new Fraction(1,3).negate();
492         f = f1.subtract(f2);
493         assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
494         assertEquals(3, f.getDenominator());
495         
496         f1 = new Fraction(Integer.MAX_VALUE, 1);
497         f2 = Fraction.ONE;
498         f = f1.subtract(f2);
499         assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
500         assertEquals(1, f.getDenominator());
501         f = f1.subtract(1);
502         assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
503         assertEquals(1, f.getDenominator());
504 
505         try {
506             f1 = new Fraction(1, Integer.MAX_VALUE);
507             f2 = new Fraction(1, Integer.MAX_VALUE - 1);
508             f = f1.subtract(f2);
509             fail("expecting ArithmeticException");  //should overflow
510         } catch (ArithmeticException ex) {}
511         
512         // denominator should not be a multiple of 2 or 3 to trigger overflow
513         f1 = new Fraction(Integer.MIN_VALUE, 5);
514         f2 = new Fraction(1,5);
515         try {
516             f = f1.subtract(f2); // should overflow
517             fail("expecting ArithmeticException but got: " + f.toString());
518         } catch (ArithmeticException ex) {}
519         
520         try {
521             f= new Fraction(Integer.MIN_VALUE, 1);
522             f = f.subtract(Fraction.ONE);
523             fail("expecting ArithmeticException");
524         } catch (ArithmeticException ex) {}
525         
526         try {
527             f= new Fraction(Integer.MAX_VALUE, 1);
528             f = f.subtract(Fraction.ONE.negate());
529             fail("expecting ArithmeticException");
530         } catch (ArithmeticException ex) {}
531         
532         f1 = new Fraction(3,327680);
533         f2 = new Fraction(2,59049);
534         try {
535             f = f1.subtract(f2); // should overflow
536             fail("expecting ArithmeticException but got: " + f.toString());
537         } catch (ArithmeticException ex) {}
538     }
539     
540     public void testEqualsAndHashCode() {
541         Fraction zero  = new Fraction(0,1);
542         Fraction nullFraction = null;
543         assertTrue( zero.equals(zero));
544         assertFalse(zero.equals(nullFraction));
545         assertFalse(zero.equals(Double.valueOf(0)));
546         Fraction zero2 = new Fraction(0,2);
547         assertTrue(zero.equals(zero2));
548         assertEquals(zero.hashCode(), zero2.hashCode());
549         Fraction one = new Fraction(1,1);
550         assertFalse((one.equals(zero) ||zero.equals(one)));
551     }
552     
553     public void testGetReducedFraction() {
554         Fraction threeFourths = new Fraction(3, 4);
555         assertTrue(threeFourths.equals(Fraction.getReducedFraction(6, 8)));
556         assertTrue(Fraction.ZERO.equals(Fraction.getReducedFraction(0, -1)));
557         try {
558             Fraction.getReducedFraction(1, 0);
559             fail("expecting ArithmeticException");
560         } catch (ArithmeticException ex) {
561             // expected
562         }
563         assertEquals(Fraction.getReducedFraction
564                 (2, Integer.MIN_VALUE).getNumerator(),-1);
565         assertEquals(Fraction.getReducedFraction
566                 (1, -1).getNumerator(), -1);
567     }
568 
569     public void testToString() {
570         assertEquals("0", new Fraction(0, 3).toString());
571         assertEquals("3", new Fraction(6, 2).toString());
572         assertEquals("2 / 3", new Fraction(18, 27).toString());
573     }
574 
575     public void testSerial() throws FractionConversionException {
576         Fraction[] fractions = {
577             new Fraction(3, 4), Fraction.ONE, Fraction.ZERO,
578             new Fraction(17), new Fraction(Math.PI, 1000),
579             new Fraction(-5, 2)
580         };
581         for (Fraction fraction : fractions) {
582             assertEquals(fraction, TestUtils.serializeAndRecover(fraction));
583         }
584     }
585 
586 }