View Javadoc
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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.fraction;
23  
24  import org.hipparchus.UnitTestUtils;
25  import org.hipparchus.exception.MathIllegalStateException;
26  import org.hipparchus.exception.MathRuntimeException;
27  import org.hipparchus.exception.NullArgumentException;
28  import org.hipparchus.util.FastMath;
29  import org.hipparchus.util.Precision;
30  import org.junit.jupiter.api.Test;
31  
32  import java.util.List;
33  import java.util.stream.Collectors;
34  
35  import static org.junit.jupiter.api.Assertions.assertEquals;
36  import static org.junit.jupiter.api.Assertions.assertFalse;
37  import static org.junit.jupiter.api.Assertions.assertNotEquals;
38  import static org.junit.jupiter.api.Assertions.assertSame;
39  import static org.junit.jupiter.api.Assertions.assertThrows;
40  import static org.junit.jupiter.api.Assertions.assertTrue;
41  import static org.junit.jupiter.api.Assertions.fail;
42  
43  
44  /**
45   */
46  class FractionTest {
47  
48      private void customAssertFraction(int expectedNumerator, int expectedDenominator, Fraction actual) {
49          assertEquals(expectedNumerator, actual.getNumerator());
50          assertEquals(expectedDenominator, actual.getDenominator());
51      }
52  
53      @Test
54      void testConstructor() {
55          customAssertFraction(0, 1, new Fraction(0, 1));
56          customAssertFraction(0, 1, new Fraction(0, 2));
57          customAssertFraction(0, 1, new Fraction(0, -1));
58          customAssertFraction(1, 2, new Fraction(1, 2));
59          customAssertFraction(1, 2, new Fraction(2, 4));
60          customAssertFraction(-1, 2, new Fraction(-1, 2));
61          customAssertFraction(-1, 2, new Fraction(1, -2));
62          customAssertFraction(-1, 2, new Fraction(-2, 4));
63          customAssertFraction(-1, 2, new Fraction(2, -4));
64  
65          // overflow
66          try {
67              new Fraction(Integer.MIN_VALUE, -1);
68              fail();
69          } catch (MathRuntimeException ex) {
70              // success
71          }
72          try {
73              new Fraction(1, Integer.MIN_VALUE);
74              fail();
75          } catch (MathRuntimeException ex) {
76              // success
77          }
78  
79          customAssertFraction(0, 1, new Fraction(0.00000000000001));
80          customAssertFraction(2, 5, new Fraction(0.40000000000001));
81          customAssertFraction(15, 1, new Fraction(15.0000000000001));
82      }
83  
84      @Test
85      void testIsInteger() {
86          assertTrue(new Fraction(12, 12).isInteger());
87          assertTrue(new Fraction(14, 7).isInteger());
88          assertFalse(new Fraction(12, 11).isInteger());
89      }
90  
91      @Test
92      void testGoldenRatio() {
93          assertThrows(MathIllegalStateException.class, () -> {
94              // the golden ratio is notoriously a difficult number for continuous fraction
95              new Fraction((1 + FastMath.sqrt(5)) / 2, 1.0e-12, 25);
96          });
97      }
98  
99      // MATH-179
100     @Test
101     void testDoubleConstructor() {
102         customAssertFraction(1, 2, new Fraction((double)1 / (double)2));
103         customAssertFraction(1, 3, new Fraction((double)1 / (double)3));
104         customAssertFraction(2, 3, new Fraction((double)2 / (double)3));
105         customAssertFraction(1, 4, new Fraction((double)1 / (double)4));
106         customAssertFraction(3, 4, new Fraction((double)3 / (double)4));
107         customAssertFraction(1, 5, new Fraction((double)1 / (double)5));
108         customAssertFraction(2, 5, new Fraction((double)2 / (double)5));
109         customAssertFraction(3, 5, new Fraction((double)3 / (double)5));
110         customAssertFraction(4, 5, new Fraction((double)4 / (double)5));
111         customAssertFraction(1, 6, new Fraction((double)1 / (double)6));
112         customAssertFraction(5, 6, new Fraction((double)5 / (double)6));
113         customAssertFraction(1, 7, new Fraction((double)1 / (double)7));
114         customAssertFraction(2, 7, new Fraction((double)2 / (double)7));
115         customAssertFraction(3, 7, new Fraction((double)3 / (double)7));
116         customAssertFraction(4, 7, new Fraction((double)4 / (double)7));
117         customAssertFraction(5, 7, new Fraction((double)5 / (double)7));
118         customAssertFraction(6, 7, new Fraction((double)6 / (double)7));
119         customAssertFraction(1, 8, new Fraction((double)1 / (double)8));
120         customAssertFraction(3, 8, new Fraction((double)3 / (double)8));
121         customAssertFraction(5, 8, new Fraction((double)5 / (double)8));
122         customAssertFraction(7, 8, new Fraction((double)7 / (double)8));
123         customAssertFraction(1, 9, new Fraction((double)1 / (double)9));
124         customAssertFraction(2, 9, new Fraction((double)2 / (double)9));
125         customAssertFraction(4, 9, new Fraction((double)4 / (double)9));
126         customAssertFraction(5, 9, new Fraction((double)5 / (double)9));
127         customAssertFraction(7, 9, new Fraction((double)7 / (double)9));
128         customAssertFraction(8, 9, new Fraction((double)8 / (double)9));
129         customAssertFraction(1, 10, new Fraction((double)1 / (double)10));
130         customAssertFraction(3, 10, new Fraction((double)3 / (double)10));
131         customAssertFraction(7, 10, new Fraction((double)7 / (double)10));
132         customAssertFraction(9, 10, new Fraction((double)9 / (double)10));
133         customAssertFraction(1, 11, new Fraction((double)1 / (double)11));
134         customAssertFraction(2, 11, new Fraction((double)2 / (double)11));
135         customAssertFraction(3, 11, new Fraction((double)3 / (double)11));
136         customAssertFraction(4, 11, new Fraction((double)4 / (double)11));
137         customAssertFraction(5, 11, new Fraction((double)5 / (double)11));
138         customAssertFraction(6, 11, new Fraction((double)6 / (double)11));
139         customAssertFraction(7, 11, new Fraction((double)7 / (double)11));
140         customAssertFraction(8, 11, new Fraction((double)8 / (double)11));
141         customAssertFraction(9, 11, new Fraction((double)9 / (double)11));
142         customAssertFraction(10, 11, new Fraction((double)10 / (double)11));
143     }
144 
145     // MATH-181
146     @Test
147     void testDigitLimitConstructor() {
148         customAssertFraction(2, 5, new Fraction(0.4, 9));
149         customAssertFraction(2, 5, new Fraction(0.4, 99));
150         customAssertFraction(2, 5, new Fraction(0.4, 999));
151 
152         customAssertFraction(3, 5, new Fraction(0.6152, 9));
153         customAssertFraction(8, 13, new Fraction(0.6152, 99));
154         customAssertFraction(510, 829, new Fraction(0.6152, 999));
155         customAssertFraction(769, 1250, new Fraction(0.6152, 9999));
156 
157         // MATH-996
158         customAssertFraction(1, 2, new Fraction(0.5000000001, 10));
159     }
160 
161     @Test
162     void testIntegerOverflow() {
163         checkIntegerOverflow(0.75000000001455192);
164         checkIntegerOverflow(1.0e10);
165         checkIntegerOverflow(-1.0e10);
166     }
167 
168     @Test
169     void testSignum() {
170         assertEquals(-1, new Fraction(4, -5).signum());
171         assertEquals(-1, new Fraction(-4, 5).signum());
172         assertEquals( 0, new Fraction(0).signum());
173         assertEquals(+1, new Fraction(-4, -5).signum());
174         assertEquals(+1, new Fraction(4, 5).signum());
175     }
176 
177     private void checkIntegerOverflow(double a) {
178         assertThrows(MathIllegalStateException.class, () -> new Fraction(a, 1.0e-12, 1000));
179     }
180 
181     @Test
182     void testEpsilonLimitConstructor() {
183         customAssertFraction(2, 5, new Fraction(0.4, 1.0e-5, 100));
184 
185         customAssertFraction(3, 5, new Fraction(0.6152, 0.02, 100));
186         customAssertFraction(8, 13, new Fraction(0.6152, 1.0e-3, 100));
187         customAssertFraction(251, 408, new Fraction(0.6152, 1.0e-4, 100));
188         customAssertFraction(251, 408, new Fraction(0.6152, 1.0e-5, 100));
189         customAssertFraction(510, 829, new Fraction(0.6152, 1.0e-6, 100));
190         customAssertFraction(769, 1250, new Fraction(0.6152, 1.0e-7, 100));
191     }
192 
193     @Test
194     void testCompareTo() {
195         Fraction first = new Fraction(1, 2);
196         Fraction second = new Fraction(1, 3);
197         Fraction third = new Fraction(1, 2);
198 
199         assertEquals(0, first.compareTo(first));
200         assertEquals(0, first.compareTo(third));
201         assertEquals(1, first.compareTo(second));
202         assertEquals(-1, second.compareTo(first));
203 
204         // these two values are different approximations of PI
205         // the first  one is approximately PI - 3.07e-18
206         // the second one is approximately PI + 1.936e-17
207         Fraction pi1 = new Fraction(1068966896, 340262731);
208         Fraction pi2 = new Fraction( 411557987, 131002976);
209         assertEquals(-1, pi1.compareTo(pi2));
210         assertEquals( 1, pi2.compareTo(pi1));
211         assertEquals(0.0, pi1.doubleValue() - pi2.doubleValue(), 1.0e-20);
212     }
213 
214     @Test
215     void testDoubleValue() {
216         Fraction first = new Fraction(1, 2);
217         Fraction second = new Fraction(1, 3);
218 
219         assertEquals(0.5, first.doubleValue(), 0.0);
220         assertEquals(1.0 / 3.0, second.doubleValue(), 0.0);
221     }
222 
223     @Test
224     void testFloatValue() {
225         Fraction first = new Fraction(1, 2);
226         Fraction second = new Fraction(1, 3);
227 
228         assertEquals(0.5f, first.floatValue(), 0.0f);
229         assertEquals((float)(1.0 / 3.0), second.floatValue(), 0.0f);
230     }
231 
232     @Test
233     void testIntValue() {
234         Fraction first = new Fraction(1, 2);
235         Fraction second = new Fraction(3, 2);
236 
237         assertEquals(0, first.intValue());
238         assertEquals(1, second.intValue());
239     }
240 
241     @Test
242     void testLongValue() {
243         Fraction first = new Fraction(1, 2);
244         Fraction second = new Fraction(3, 2);
245 
246         assertEquals(0L, first.longValue());
247         assertEquals(1L, second.longValue());
248     }
249 
250     @Test
251     void testConstructorDouble() {
252         customAssertFraction(1, 2, new Fraction(0.5));
253         customAssertFraction(1, 3, new Fraction(1.0 / 3.0));
254         customAssertFraction(17, 100, new Fraction(17.0 / 100.0));
255         customAssertFraction(317, 100, new Fraction(317.0 / 100.0));
256         customAssertFraction(-1, 2, new Fraction(-0.5));
257         customAssertFraction(-1, 3, new Fraction(-1.0 / 3.0));
258         customAssertFraction(-17, 100, new Fraction(17.0 / -100.0));
259         customAssertFraction(-317, 100, new Fraction(-317.0 / 100.0));
260     }
261 
262     @Test
263     void testAbs() {
264         Fraction a = new Fraction(10, 21);
265         Fraction b = new Fraction(-10, 21);
266         Fraction c = new Fraction(10, -21);
267 
268         customAssertFraction(10, 21, a.abs());
269         customAssertFraction(10, 21, b.abs());
270         customAssertFraction(10, 21, c.abs());
271     }
272 
273     @Test
274     void testPercentage() {
275         assertEquals(50.0, new Fraction(1, 2).percentageValue(), 1.0e-15);
276     }
277 
278     @Test
279     void testMath835() {
280         final int numer = Integer.MAX_VALUE / 99;
281         final int denom = 1;
282         final double percentage = 100 * ((double) numer) / denom;
283         final Fraction frac = new Fraction(numer, denom);
284         // With the implementation that preceded the fix suggested in MATH-835,
285         // this test was failing, due to overflow.
286         assertEquals(percentage, frac.percentageValue(), Math.ulp(percentage));
287     }
288 
289     @Test
290     void testMath1261() {
291         final Fraction a = new Fraction(Integer.MAX_VALUE, 2);
292         final Fraction b = a.multiply(2);
293         assertEquals(b, new Fraction(Integer.MAX_VALUE));
294 
295         final Fraction c = new Fraction(2, Integer.MAX_VALUE);
296         final Fraction d = c.divide(2);
297         assertEquals(d, new Fraction(1, Integer.MAX_VALUE));
298     }
299 
300     @Test
301     void testReciprocal() {
302         Fraction f = null;
303 
304         f = new Fraction(50, 75);
305         f = f.reciprocal();
306         assertEquals(3, f.getNumerator());
307         assertEquals(2, f.getDenominator());
308 
309         f = new Fraction(4, 3);
310         f = f.reciprocal();
311         assertEquals(3, f.getNumerator());
312         assertEquals(4, f.getDenominator());
313 
314         f = new Fraction(-15, 47);
315         f = f.reciprocal();
316         assertEquals(-47, f.getNumerator());
317         assertEquals(15, f.getDenominator());
318 
319         f = new Fraction(0, 3);
320         try {
321             f = f.reciprocal();
322             fail("expecting MathRuntimeException");
323         } catch (MathRuntimeException ex) {}
324 
325         // large values
326         f = new Fraction(Integer.MAX_VALUE, 1);
327         f = f.reciprocal();
328         assertEquals(1, f.getNumerator());
329         assertEquals(Integer.MAX_VALUE, f.getDenominator());
330     }
331 
332     @Test
333     void testNegate() {
334         Fraction f = null;
335 
336         f = new Fraction(50, 75);
337         f = f.negate();
338         assertEquals(-2, f.getNumerator());
339         assertEquals(3, f.getDenominator());
340 
341         f = new Fraction(-50, 75);
342         f = f.negate();
343         assertEquals(2, f.getNumerator());
344         assertEquals(3, f.getDenominator());
345 
346         // large values
347         f = new Fraction(Integer.MAX_VALUE-1, Integer.MAX_VALUE);
348         f = f.negate();
349         assertEquals(Integer.MIN_VALUE+2, f.getNumerator());
350         assertEquals(Integer.MAX_VALUE, f.getDenominator());
351 
352         f = new Fraction(Integer.MIN_VALUE, 1);
353         try {
354             f = f.negate();
355             fail("expecting MathRuntimeException");
356         } catch (MathRuntimeException ex) {}
357     }
358 
359     @Test
360     void testAdd() {
361         Fraction a = new Fraction(1, 2);
362         Fraction b = new Fraction(2, 3);
363 
364         customAssertFraction(1, 1, a.add(a));
365         customAssertFraction(7, 6, a.add(b));
366         customAssertFraction(7, 6, b.add(a));
367         customAssertFraction(4, 3, b.add(b));
368 
369         Fraction f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
370         Fraction f2 = Fraction.ONE;
371         Fraction f = f1.add(f2);
372         assertEquals(Integer.MAX_VALUE, f.getNumerator());
373         assertEquals(1, f.getDenominator());
374         f = f1.add(1);
375         assertEquals(Integer.MAX_VALUE, f.getNumerator());
376         assertEquals(1, f.getDenominator());
377 
378         f1 = new Fraction(-1, 13*13*2*2);
379         f2 = new Fraction(-2, 13*17*2);
380         f = f1.add(f2);
381         assertEquals(13*13*17*2*2, f.getDenominator());
382         assertEquals(-17 - 2*13*2, f.getNumerator());
383 
384         try {
385             f.add(null);
386             fail("expecting NullArgumentException");
387         } catch (NullArgumentException ex) {}
388 
389         // if this fraction is added naively, it will overflow.
390         // check that it doesn't.
391         f1 = new Fraction(1,32768*3);
392         f2 = new Fraction(1,59049);
393         f = f1.add(f2);
394         assertEquals(52451, f.getNumerator());
395         assertEquals(1934917632, f.getDenominator());
396 
397         f1 = new Fraction(Integer.MIN_VALUE, 3);
398         f2 = new Fraction(1,3);
399         f = f1.add(f2);
400         assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
401         assertEquals(3, f.getDenominator());
402 
403         f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
404         f2 = Fraction.ONE;
405         f = f1.add(f2);
406         assertEquals(Integer.MAX_VALUE, f.getNumerator());
407         assertEquals(1, f.getDenominator());
408 
409         try {
410             f = f.add(Fraction.ONE); // should overflow
411             fail("expecting MathRuntimeException but got: " + f.toString());
412         } catch (MathRuntimeException ex) {}
413 
414         // denominator should not be a multiple of 2 or 3 to trigger overflow
415         f1 = new Fraction(Integer.MIN_VALUE, 5);
416         f2 = new Fraction(-1,5);
417         try {
418             f = f1.add(f2); // should overflow
419             fail("expecting MathRuntimeException but got: " + f.toString());
420         } catch (MathRuntimeException ex) {}
421 
422         try {
423             f= new Fraction(-Integer.MAX_VALUE, 1);
424             f = f.add(f);
425             fail("expecting MathRuntimeException");
426         } catch (MathRuntimeException ex) {}
427 
428         try {
429             f= new Fraction(-Integer.MAX_VALUE, 1);
430             f = f.add(f);
431             fail("expecting MathRuntimeException");
432         } catch (MathRuntimeException ex) {}
433 
434         f1 = new Fraction(3,327680);
435         f2 = new Fraction(2,59049);
436         try {
437             f = f1.add(f2); // should overflow
438             fail("expecting MathRuntimeException but got: " + f.toString());
439         } catch (MathRuntimeException ex) {}
440     }
441 
442     @Test
443     void testDivide() {
444         Fraction a = new Fraction(1, 2);
445         Fraction b = new Fraction(2, 3);
446 
447         customAssertFraction(1, 1, a.divide(a));
448         customAssertFraction(3, 4, a.divide(b));
449         customAssertFraction(4, 3, b.divide(a));
450         customAssertFraction(1, 1, b.divide(b));
451 
452         Fraction f1 = new Fraction(3, 5);
453         Fraction f2 = Fraction.ZERO;
454         try {
455             f1.divide(f2);
456             fail("expecting MathRuntimeException");
457         } catch (MathRuntimeException ex) {}
458 
459         f1 = new Fraction(0, 5);
460         f2 = new Fraction(2, 7);
461         Fraction f = f1.divide(f2);
462         assertSame(Fraction.ZERO, f);
463 
464         f1 = new Fraction(2, 7);
465         f2 = Fraction.ONE;
466         f = f1.divide(f2);
467         assertEquals(2, f.getNumerator());
468         assertEquals(7, f.getDenominator());
469 
470         f1 = new Fraction(1, Integer.MAX_VALUE);
471         f = f1.divide(f1);
472         assertEquals(1, f.getNumerator());
473         assertEquals(1, f.getDenominator());
474 
475         f1 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
476         f2 = new Fraction(1, Integer.MAX_VALUE);
477         f = f1.divide(f2);
478         assertEquals(Integer.MIN_VALUE, f.getNumerator());
479         assertEquals(1, f.getDenominator());
480 
481         try {
482             f.divide(null);
483             fail("NullArgumentException");
484         } catch (NullArgumentException ex) {}
485 
486         try {
487             f1 = new Fraction(1, Integer.MAX_VALUE);
488             f = f1.divide(f1.reciprocal());  // should overflow
489             fail("expecting MathRuntimeException");
490         } catch (MathRuntimeException ex) {}
491         try {
492             f1 = new Fraction(1, -Integer.MAX_VALUE);
493             f = f1.divide(f1.reciprocal());  // should overflow
494             fail("expecting MathRuntimeException");
495         } catch (MathRuntimeException ex) {}
496 
497         f1 = new Fraction(6, 35);
498         f  = f1.divide(15);
499         assertEquals(2, f.getNumerator());
500         assertEquals(175, f.getDenominator());
501 
502     }
503 
504     @Test
505     void testMultiply() {
506         Fraction a = new Fraction(1, 2);
507         Fraction b = new Fraction(2, 3);
508 
509         customAssertFraction(1, 4, a.multiply(a));
510         customAssertFraction(1, 3, a.multiply(b));
511         customAssertFraction(1, 3, b.multiply(a));
512         customAssertFraction(4, 9, b.multiply(b));
513 
514         Fraction f1 = new Fraction(Integer.MAX_VALUE, 1);
515         Fraction f2 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
516         Fraction f = f1.multiply(f2);
517         assertEquals(Integer.MIN_VALUE, f.getNumerator());
518         assertEquals(1, f.getDenominator());
519 
520         try {
521             f.multiply(null);
522             fail("expecting NullArgumentException");
523         } catch (NullArgumentException ex) {}
524 
525         f1 = new Fraction(6, 35);
526         f  = f1.multiply(15);
527         assertEquals(18, f.getNumerator());
528         assertEquals(7, f.getDenominator());
529     }
530 
531     @Test
532     void testSubtract() {
533         Fraction a = new Fraction(1, 2);
534         Fraction b = new Fraction(2, 3);
535 
536         customAssertFraction(0, 1, a.subtract(a));
537         customAssertFraction(-1, 6, a.subtract(b));
538         customAssertFraction(1, 6, b.subtract(a));
539         customAssertFraction(0, 1, b.subtract(b));
540 
541         Fraction f = new Fraction(1,1);
542         try {
543             f.subtract(null);
544             fail("expecting NullArgumentException");
545         } catch (NullArgumentException ex) {}
546 
547         // if this fraction is subtracted naively, it will overflow.
548         // check that it doesn't.
549         Fraction f1 = new Fraction(1,32768*3);
550         Fraction f2 = new Fraction(1,59049);
551         f = f1.subtract(f2);
552         assertEquals(-13085, f.getNumerator());
553         assertEquals(1934917632, f.getDenominator());
554 
555         f1 = new Fraction(Integer.MIN_VALUE, 3);
556         f2 = new Fraction(1,3).negate();
557         f = f1.subtract(f2);
558         assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
559         assertEquals(3, f.getDenominator());
560 
561         f1 = new Fraction(Integer.MAX_VALUE, 1);
562         f2 = Fraction.ONE;
563         f = f1.subtract(f2);
564         assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
565         assertEquals(1, f.getDenominator());
566         f = f1.subtract(1);
567         assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
568         assertEquals(1, f.getDenominator());
569 
570         try {
571             f1 = new Fraction(1, Integer.MAX_VALUE);
572             f2 = new Fraction(1, Integer.MAX_VALUE - 1);
573             f = f1.subtract(f2);
574             fail("expecting MathRuntimeException");  //should overflow
575         } catch (MathRuntimeException ex) {}
576 
577         // denominator should not be a multiple of 2 or 3 to trigger overflow
578         f1 = new Fraction(Integer.MIN_VALUE, 5);
579         f2 = new Fraction(1,5);
580         try {
581             f = f1.subtract(f2); // should overflow
582             fail("expecting MathRuntimeException but got: " + f.toString());
583         } catch (MathRuntimeException ex) {}
584 
585         try {
586             f= new Fraction(Integer.MIN_VALUE, 1);
587             f = f.subtract(Fraction.ONE);
588             fail("expecting MathRuntimeException");
589         } catch (MathRuntimeException ex) {}
590 
591         try {
592             f= new Fraction(Integer.MAX_VALUE, 1);
593             f = f.subtract(Fraction.ONE.negate());
594             fail("expecting MathRuntimeException");
595         } catch (MathRuntimeException ex) {}
596 
597         f1 = new Fraction(3,327680);
598         f2 = new Fraction(2,59049);
599         try {
600             f = f1.subtract(f2); // should overflow
601             fail("expecting MathRuntimeException but got: " + f.toString());
602         } catch (MathRuntimeException ex) {}
603     }
604 
605     @SuppressWarnings("unlikely-arg-type")
606     @Test
607     void testEqualsAndHashCode() {
608         Fraction zero  = new Fraction(0,1);
609         Fraction nullFraction = null;
610         assertEquals(zero, zero);
611         assertNotEquals(zero, nullFraction);
612         assertNotEquals(zero, Double.valueOf(0));
613         Fraction zero2 = new Fraction(0,2);
614         assertEquals(zero, zero2);
615         assertEquals(zero.hashCode(), zero2.hashCode());
616         Fraction one = new Fraction(1,1);
617         assertFalse((one.equals(zero) ||zero.equals(one)));
618     }
619 
620     @Test
621     void testGCD() {
622       Fraction first = new Fraction(1, 3);
623       Fraction second = new Fraction(2, 5);
624       Fraction third = new Fraction(3, 7);
625       Fraction gcd1 = first.gcd(second);
626         assertEquals(gcd1, Fraction.getReducedFraction(1, 15));
627       Fraction gcd2 = gcd1.gcd(third);
628         assertEquals(gcd2, Fraction.getReducedFraction(1, 105));
629 
630       // example from https://math.stackexchange.com/a/151089
631       Fraction x = new Fraction(3, 7);
632       Fraction y = new Fraction(12, 22);
633       Fraction gcd = x.gcd(y);
634         assertEquals(gcd, Fraction.getReducedFraction(3, 77));
635 
636       x = new Fraction(13, 6);
637       y = new Fraction(3, 4);
638       gcd = x.gcd(y);
639         assertEquals(gcd, Fraction.getReducedFraction(1, 12));
640 
641     }
642 
643     @Test
644     void testLCM() {
645       Fraction first = new Fraction(1, 3);
646       Fraction second = new Fraction(2, 5);
647       Fraction third = new Fraction(3, 7);
648       Fraction lcm1 = first.lcm(second);
649         assertEquals(lcm1, Fraction.getReducedFraction(2, 1));
650       Fraction lcm2 = lcm1.lcm(third);
651         assertEquals(lcm2, Fraction.getReducedFraction(6, 1));
652     }
653 
654     @Test
655     void testGetReducedFraction() {
656         Fraction threeFourths = new Fraction(3, 4);
657         assertEquals(threeFourths, Fraction.getReducedFraction(6, 8));
658         assertEquals(Fraction.ZERO, Fraction.getReducedFraction(0, -1));
659         try {
660             Fraction.getReducedFraction(1, 0);
661             fail("expecting MathRuntimeException");
662         } catch (MathRuntimeException ex) {
663             // expected
664         }
665         assertEquals(-1, Fraction.getReducedFraction
666             (2, Integer.MIN_VALUE).getNumerator());
667         assertEquals(-1, Fraction.getReducedFraction
668             (1, -1).getNumerator());
669     }
670 
671     @Test
672     void testNormalizedEquals() {
673         assertEquals(new Fraction(237, -3871), new Fraction(-51, 833));
674     }
675 
676     @Test
677     void testToString() {
678         assertEquals("0", new Fraction(0, 3).toString());
679         assertEquals("3", new Fraction(6, 2).toString());
680         assertEquals("2 / 3", new Fraction(18, 27).toString());
681     }
682 
683     @Test
684     void testSerial() {
685         Fraction[] fractions = {
686             new Fraction(3, 4), Fraction.ONE, Fraction.ZERO,
687             new Fraction(17), new Fraction(FastMath.PI, 1000),
688             new Fraction(-5, 2)
689         };
690         for (Fraction fraction : fractions) {
691             assertEquals(fraction, UnitTestUtils.serializeAndRecover(fraction));
692         }
693     }
694 
695     @Test
696     void testConvergents() {
697         // OEIS A002485, Numerators of convergents to Pi (https://oeis.org/A002485)
698         // 0, 1, 3, 22, 333, 355, 103993, 104348, 208341, 312689, 833719, 1146408, 4272943, 5419351, 80143857, 165707065, 245850922
699         // OEIS A002486, Apart from two leading terms (which are present by convention), denominators of convergents to Pi (https://oeis.org/A002486)
700         // 1, 0, 1,  7, 106, 113,  33102,  33215,  66317,  99532, 265381,  364913, 1360120, 1725033, 25510582,  52746197, 78256779
701         List<Fraction> convergents = Fraction.convergents(FastMath.PI, 20).collect(Collectors.toList());
702         assertEquals(13, convergents.size());
703         assertEquals(new Fraction(       3,        1), convergents.get( 0));
704         assertEquals(new Fraction(      22,        7), convergents.get( 1));
705         assertEquals(new Fraction(     333,      106), convergents.get( 2));
706         assertEquals(new Fraction(     355,      113), convergents.get( 3));
707         assertEquals(new Fraction(  103993,    33102), convergents.get( 4));
708         assertEquals(new Fraction(  104348,    33215), convergents.get( 5));
709         assertEquals(new Fraction(  208341,    66317), convergents.get( 6));
710         assertEquals(new Fraction(  312689,    99532), convergents.get( 7));
711         assertEquals(new Fraction(  833719,   265381), convergents.get( 8));
712         assertEquals(new Fraction( 1146408,   364913), convergents.get( 9));
713         assertEquals(new Fraction( 4272943,  1360120), convergents.get(10));
714         assertEquals(new Fraction( 5419351,  1725033), convergents.get(11));
715         assertEquals(new Fraction(80143857, 25510582), convergents.get(12));
716     }
717 
718     @Test
719     void testLimitedConvergents() {
720         double value = FastMath.PI;
721         assertEquals(new Fraction(  208341,    66317),
722                 Fraction.convergent(value, 7, (p, q) -> Precision.equals(p / (double) q, value, 1)).getKey());
723     }
724 
725     @Test
726     void testTruncatedConvergents() {
727         final double value = FastMath.PI;
728         assertEquals(new Fraction(   355,   113),
729                 Fraction.convergent(value, 20, (p, q) -> FastMath.abs(p / (double) q - value) < 1.0e-6).getKey());
730         assertEquals(new Fraction(312689, 99532),
731                 Fraction.convergent(value, 20, (p, q) -> FastMath.abs(p / (double) q - value) < 1.0e-10).getKey());
732     }
733 
734 }