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