001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.math.fraction;
018    
019    import org.apache.commons.math.ConvergenceException;
020    import org.apache.commons.math.TestUtils;
021    
022    import junit.framework.TestCase;
023    
024    /**
025     * @version $Revision: 795903 $ $Date: 2009-07-20 12:29:46 -0400 (Mon, 20 Jul 2009) $
026     */
027    public class FractionTest extends TestCase {
028    
029        private void assertFraction(int expectedNumerator, int expectedDenominator, Fraction actual) {
030            assertEquals(expectedNumerator, actual.getNumerator());
031            assertEquals(expectedDenominator, actual.getDenominator());
032        }
033        
034        public void testConstructor() {
035            assertFraction(0, 1, new Fraction(0, 1));
036            assertFraction(0, 1, new Fraction(0, 2));
037            assertFraction(0, 1, new Fraction(0, -1));
038            assertFraction(1, 2, new Fraction(1, 2));
039            assertFraction(1, 2, new Fraction(2, 4));
040            assertFraction(-1, 2, new Fraction(-1, 2));
041            assertFraction(-1, 2, new Fraction(1, -2));
042            assertFraction(-1, 2, new Fraction(-2, 4));
043            assertFraction(-1, 2, new Fraction(2, -4));
044            
045            // overflow
046            try {
047                new Fraction(Integer.MIN_VALUE, -1);
048                fail();
049            } catch (ArithmeticException ex) {
050                // success
051            }
052            try {
053                new Fraction(1, Integer.MIN_VALUE);
054                fail();
055            } catch (ArithmeticException ex) {
056                // success
057            }
058            try {        
059                assertFraction(0, 1, new Fraction(0.00000000000001));
060                assertFraction(2, 5, new Fraction(0.40000000000001));
061                assertFraction(15, 1, new Fraction(15.0000000000001));
062                
063            } catch (ConvergenceException ex) {
064                fail(ex.getMessage());
065            }
066        }
067    
068        public void testGoldenRatio() {
069            try {
070                // the golden ratio is notoriously a difficult number for continuous fraction
071                new Fraction((1 + Math.sqrt(5)) / 2, 1.0e-12, 25);
072                fail("an exception should have been thrown");
073            } catch (ConvergenceException ce) {
074                // expected behavior
075            } catch (Exception e) {
076                fail("wrong exception caught");
077            }
078        }
079    
080        // MATH-179
081        public void testDoubleConstructor() throws ConvergenceException  {
082            assertFraction(1, 2, new Fraction((double)1 / (double)2));
083            assertFraction(1, 3, new Fraction((double)1 / (double)3));
084            assertFraction(2, 3, new Fraction((double)2 / (double)3));
085            assertFraction(1, 4, new Fraction((double)1 / (double)4));
086            assertFraction(3, 4, new Fraction((double)3 / (double)4));
087            assertFraction(1, 5, new Fraction((double)1 / (double)5));
088            assertFraction(2, 5, new Fraction((double)2 / (double)5));
089            assertFraction(3, 5, new Fraction((double)3 / (double)5));
090            assertFraction(4, 5, new Fraction((double)4 / (double)5));
091            assertFraction(1, 6, new Fraction((double)1 / (double)6));
092            assertFraction(5, 6, new Fraction((double)5 / (double)6));
093            assertFraction(1, 7, new Fraction((double)1 / (double)7));
094            assertFraction(2, 7, new Fraction((double)2 / (double)7));
095            assertFraction(3, 7, new Fraction((double)3 / (double)7));
096            assertFraction(4, 7, new Fraction((double)4 / (double)7));
097            assertFraction(5, 7, new Fraction((double)5 / (double)7));
098            assertFraction(6, 7, new Fraction((double)6 / (double)7));
099            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    }