View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.lang3.math;
20  
21  import static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotEquals;
24  import static org.junit.jupiter.api.Assertions.assertSame;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import org.apache.commons.lang3.AbstractLangTest;
29  import org.junit.jupiter.api.Test;
30  import org.junit.jupiter.params.ParameterizedTest;
31  import org.junit.jupiter.params.provider.CsvSource;
32  
33  /**
34   * Tests {@link Fraction}.
35   */
36  class FractionTest extends AbstractLangTest {
37  
38      private static final int SKIP = 500; // 53
39  
40      @Test
41      void testAbs() {
42          Fraction f;
43  
44          f = Fraction.getFraction(50, 75);
45          f = f.abs();
46          assertEquals(50, f.getNumerator());
47          assertEquals(75, f.getDenominator());
48  
49          f = Fraction.getFraction(-50, 75);
50          f = f.abs();
51          assertEquals(50, f.getNumerator());
52          assertEquals(75, f.getDenominator());
53  
54          f = Fraction.getFraction(Integer.MAX_VALUE, 1);
55          f = f.abs();
56          assertEquals(Integer.MAX_VALUE, f.getNumerator());
57          assertEquals(1, f.getDenominator());
58  
59          f = Fraction.getFraction(Integer.MAX_VALUE, -1);
60          f = f.abs();
61          assertEquals(Integer.MAX_VALUE, f.getNumerator());
62          assertEquals(1, f.getDenominator());
63  
64          assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).abs());
65      }
66  
67      @Test
68      void testAdd() {
69          Fraction f;
70          Fraction f1;
71          Fraction f2;
72  
73          f1 = Fraction.getFraction(3, 5);
74          f2 = Fraction.getFraction(1, 5);
75          f = f1.add(f2);
76          assertEquals(4, f.getNumerator());
77          assertEquals(5, f.getDenominator());
78  
79          f1 = Fraction.getFraction(3, 5);
80          f2 = Fraction.getFraction(2, 5);
81          f = f1.add(f2);
82          assertEquals(1, f.getNumerator());
83          assertEquals(1, f.getDenominator());
84  
85          f1 = Fraction.getFraction(3, 5);
86          f2 = Fraction.getFraction(3, 5);
87          f = f1.add(f2);
88          assertEquals(6, f.getNumerator());
89          assertEquals(5, f.getDenominator());
90  
91          f1 = Fraction.getFraction(3, 5);
92          f2 = Fraction.getFraction(-4, 5);
93          f = f1.add(f2);
94          assertEquals(-1, f.getNumerator());
95          assertEquals(5, f.getDenominator());
96  
97          f1 = Fraction.getFraction(Integer.MAX_VALUE - 1, 1);
98          f2 = Fraction.ONE;
99          f = f1.add(f2);
100         assertEquals(Integer.MAX_VALUE, f.getNumerator());
101         assertEquals(1, f.getDenominator());
102 
103         f1 = Fraction.getFraction(3, 5);
104         f2 = Fraction.getFraction(1, 2);
105         f = f1.add(f2);
106         assertEquals(11, f.getNumerator());
107         assertEquals(10, f.getDenominator());
108 
109         f1 = Fraction.getFraction(3, 8);
110         f2 = Fraction.getFraction(1, 6);
111         f = f1.add(f2);
112         assertEquals(13, f.getNumerator());
113         assertEquals(24, f.getDenominator());
114 
115         f1 = Fraction.getFraction(0, 5);
116         f2 = Fraction.getFraction(1, 5);
117         f = f1.add(f2);
118         assertSame(f2, f);
119         f = f2.add(f1);
120         assertSame(f2, f);
121 
122         f1 = Fraction.getFraction(-1, 13 * 13 * 2 * 2);
123         f2 = Fraction.getFraction(-2, 13 * 17 * 2);
124         final Fraction fr = f1.add(f2);
125         assertEquals(13 * 13 * 17 * 2 * 2, fr.getDenominator());
126         assertEquals(-17 - 2 * 13 * 2, fr.getNumerator());
127 
128         assertNullPointerException(() -> fr.add(null));
129 
130         // if this fraction is added naively, it will overflow.
131         // check that it doesn't.
132         f1 = Fraction.getFraction(1, 32768 * 3);
133         f2 = Fraction.getFraction(1, 59049);
134         f = f1.add(f2);
135         assertEquals(52451, f.getNumerator());
136         assertEquals(1934917632, f.getDenominator());
137 
138         f1 = Fraction.getFraction(Integer.MIN_VALUE, 3);
139         f2 = Fraction.ONE_THIRD;
140         f = f1.add(f2);
141         assertEquals(Integer.MIN_VALUE + 1, f.getNumerator());
142         assertEquals(3, f.getDenominator());
143 
144         f1 = Fraction.getFraction(Integer.MAX_VALUE - 1, 1);
145         f2 = Fraction.ONE;
146         f = f1.add(f2);
147         assertEquals(Integer.MAX_VALUE, f.getNumerator());
148         assertEquals(1, f.getDenominator());
149 
150         final Fraction overflower = f;
151         assertThrows(ArithmeticException.class, () -> overflower.add(Fraction.ONE)); // should overflow
152 
153         // denominator should not be a multiple of 2 or 3 to trigger overflow
154         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 5).add(Fraction.getFraction(-1, 5)));
155 
156         final Fraction maxValue = Fraction.getFraction(-Integer.MAX_VALUE, 1);
157         assertThrows(ArithmeticException.class, () -> maxValue.add(maxValue));
158 
159         final Fraction negativeMaxValue = Fraction.getFraction(-Integer.MAX_VALUE, 1);
160         assertThrows(ArithmeticException.class, () -> negativeMaxValue.add(negativeMaxValue));
161 
162         final Fraction f3 = Fraction.getFraction(3, 327680);
163         final Fraction f4 = Fraction.getFraction(2, 59049);
164         assertThrows(ArithmeticException.class, () -> f3.add(f4)); // should overflow
165 
166         // the cross products u*v' and u'*v overflow an int, but the reduced result fits.
167         f1 = Fraction.getFraction(Integer.MAX_VALUE, 2);
168         f2 = Fraction.getFraction(-Integer.MAX_VALUE, 1);
169         f = f1.add(f2);
170         assertEquals(-Integer.MAX_VALUE, f.getNumerator());
171         assertEquals(2, f.getDenominator());
172 
173         f1 = Fraction.getFraction(2, 1);
174         f2 = Fraction.getFraction(-Integer.MAX_VALUE, 2114962910);
175         f = f1.add(f2);
176         assertEquals(2082442173, f.getNumerator());
177         assertEquals(2114962910, f.getDenominator());
178     }
179 
180     @Test
181     void testAddSubtractUnreducedOperands() {
182         // 1073741823/2147483646 is 1/2, and 1/2 + 3/5 is 11/10.
183         Fraction f = Fraction.getFraction(1073741823, 2147483646).add(Fraction.getFraction(3, 5));
184         assertEquals(11, f.getNumerator());
185         assertEquals(10, f.getDenominator());
186 
187         f = Fraction.getFraction(1073741823, 2147483646).subtract(Fraction.getFraction(3, 5));
188         assertEquals(-1, f.getNumerator());
189         assertEquals(10, f.getDenominator());
190 
191         // 2147483646/2147483646 is 1, and 1 + -11 is -10.
192         f = Fraction.getFraction(2147483646, 2147483646).add(Fraction.getFraction(-11, 1));
193         assertEquals(-10, f.getNumerator());
194         assertEquals(1, f.getDenominator());
195 
196         // add() returns the result in reduced form.
197         f = Fraction.getFraction(50, 100).add(Fraction.getFraction(1, 3));
198         assertEquals(5, f.getNumerator());
199         assertEquals(6, f.getDenominator());
200 
201         f = Fraction.getFraction(2, 4).add(Fraction.getFraction(1, 2));
202         assertEquals(1, f.getNumerator());
203         assertEquals(1, f.getDenominator());
204 
205         // Reducing the operands by hand must not change the answer.
206         assertEquals(Fraction.getFraction(7, 13).reduce().add(Fraction.getFraction(46341, 1073741823).reduce()),
207                 Fraction.getFraction(7, 13).add(Fraction.getFraction(46341, 1073741823)));
208 
209         // Both operands unreduced: 2/4 + 2/6 is 1/2 + 1/3.
210         f = Fraction.getFraction(2, 4).add(Fraction.getFraction(2, 6));
211         assertEquals(5, f.getNumerator());
212         assertEquals(6, f.getDenominator());
213 
214         // Reduced denominators share a factor: 2/4 - 2/12 is 1/2 - 1/6.
215         f = Fraction.getFraction(2, 4).subtract(Fraction.getFraction(2, 12));
216         assertEquals(1, f.getNumerator());
217         assertEquals(3, f.getDenominator());
218 
219         // Equal values cancel to 0/1.
220         f = Fraction.getFraction(2, 4).subtract(Fraction.getFraction(3, 6));
221         assertEquals(0, f.getNumerator());
222         assertEquals(1, f.getDenominator());
223 
224         // Integer.MIN_VALUE/2 reduces to -1073741824/1 without overflowing.
225         f = Fraction.getFraction(Integer.MIN_VALUE, 2).add(Fraction.getFraction(2, 4));
226         assertEquals(-Integer.MAX_VALUE, f.getNumerator());
227         assertEquals(2, f.getDenominator());
228 
229         // A result that genuinely does not fit an int still overflows.
230         final Fraction maxValue = Fraction.getFraction(-Integer.MAX_VALUE, 1);
231         assertThrows(ArithmeticException.class, () -> maxValue.add(maxValue));
232     }
233 
234     @Test
235     void testAddSubtractZeroOperand() {
236         // A zero operand returns the other operand in reduced form.
237         Fraction f = Fraction.ZERO.add(Fraction.getFraction(2, 4));
238         assertEquals(1, f.getNumerator());
239         assertEquals(2, f.getDenominator());
240 
241         f = Fraction.getFraction(2, 4).add(Fraction.ZERO);
242         assertEquals(1, f.getNumerator());
243         assertEquals(2, f.getDenominator());
244 
245         f = Fraction.ZERO.subtract(Fraction.getFraction(2, 4));
246         assertEquals(-1, f.getNumerator());
247         assertEquals(2, f.getDenominator());
248 
249         f = Fraction.getFraction(2, 4).subtract(Fraction.ZERO);
250         assertEquals(1, f.getNumerator());
251         assertEquals(2, f.getDenominator());
252 
253         // Integer.MIN_VALUE/2 reduces to -1073741824/1, whose negation fits an int.
254         f = Fraction.ZERO.subtract(Fraction.getFraction(Integer.MIN_VALUE, 2));
255         assertEquals(1073741824, f.getNumerator());
256         assertEquals(1, f.getDenominator());
257 
258         // Integer.MIN_VALUE/1 is in lowest terms and still cannot be negated.
259         assertThrows(ArithmeticException.class, () -> Fraction.ZERO.subtract(Fraction.getFraction(Integer.MIN_VALUE, 1)));
260 
261         // both operands being unreduced zeros
262         assertEquals(Fraction.ZERO, Fraction.getFraction(0, 2).add(Fraction.getFraction(0, 3)));
263         assertEquals(Fraction.ZERO, Fraction.getFraction(0, 2).subtract(Fraction.getFraction(0, 3)));
264     }
265 
266     @Test
267     void testCompareTo() {
268         final Fraction f1;
269         Fraction f2;
270 
271         f1 = Fraction.getFraction(3, 5);
272         assertEquals(0, f1.compareTo(f1));
273 
274         final Fraction fr = f1;
275         assertNullPointerException(() -> fr.compareTo(null));
276 
277         f2 = Fraction.getFraction(2, 5);
278         assertTrue(f1.compareTo(f2) > 0);
279         assertEquals(0, f2.compareTo(f2));
280 
281         f2 = Fraction.getFraction(4, 5);
282         assertTrue(f1.compareTo(f2) < 0);
283         assertEquals(0, f2.compareTo(f2));
284 
285         f2 = Fraction.getFraction(3, 5);
286         assertEquals(0, f1.compareTo(f2));
287         assertEquals(0, f2.compareTo(f2));
288 
289         f2 = Fraction.getFraction(6, 10);
290         assertEquals(0, f1.compareTo(f2));
291         assertEquals(0, f2.compareTo(f2));
292 
293         f2 = Fraction.getFraction(-1, 1, Integer.MAX_VALUE);
294         assertTrue(f1.compareTo(f2) > 0);
295         assertEquals(0, f2.compareTo(f2));
296 
297     }
298 
299     @Test
300     void testConstants() {
301         assertEquals(0, Fraction.ZERO.getNumerator());
302         assertEquals(1, Fraction.ZERO.getDenominator());
303 
304         assertEquals(1, Fraction.ONE.getNumerator());
305         assertEquals(1, Fraction.ONE.getDenominator());
306 
307         assertEquals(1, Fraction.ONE_HALF.getNumerator());
308         assertEquals(2, Fraction.ONE_HALF.getDenominator());
309 
310         assertEquals(1, Fraction.ONE_THIRD.getNumerator());
311         assertEquals(3, Fraction.ONE_THIRD.getDenominator());
312 
313         assertEquals(2, Fraction.TWO_THIRDS.getNumerator());
314         assertEquals(3, Fraction.TWO_THIRDS.getDenominator());
315 
316         assertEquals(1, Fraction.ONE_QUARTER.getNumerator());
317         assertEquals(4, Fraction.ONE_QUARTER.getDenominator());
318 
319         assertEquals(2, Fraction.TWO_QUARTERS.getNumerator());
320         assertEquals(4, Fraction.TWO_QUARTERS.getDenominator());
321 
322         assertEquals(3, Fraction.THREE_QUARTERS.getNumerator());
323         assertEquals(4, Fraction.THREE_QUARTERS.getDenominator());
324 
325         assertEquals(1, Fraction.ONE_FIFTH.getNumerator());
326         assertEquals(5, Fraction.ONE_FIFTH.getDenominator());
327 
328         assertEquals(2, Fraction.TWO_FIFTHS.getNumerator());
329         assertEquals(5, Fraction.TWO_FIFTHS.getDenominator());
330 
331         assertEquals(3, Fraction.THREE_FIFTHS.getNumerator());
332         assertEquals(5, Fraction.THREE_FIFTHS.getDenominator());
333 
334         assertEquals(4, Fraction.FOUR_FIFTHS.getNumerator());
335         assertEquals(5, Fraction.FOUR_FIFTHS.getDenominator());
336     }
337 
338     @Test
339     void testConversions() {
340         final Fraction f;
341 
342         f = Fraction.getFraction(3, 7, 8);
343         assertEquals(3, f.intValue());
344         assertEquals(3L, f.longValue());
345         assertEquals(3.875f, f.floatValue(), 0.00001f);
346         assertEquals(3.875d, f.doubleValue(), 0.00001d);
347     }
348 
349     @Test
350     void testDivide() {
351         Fraction f;
352         Fraction f1;
353         Fraction f2;
354 
355         f1 = Fraction.getFraction(3, 5);
356         f2 = Fraction.getFraction(2, 5);
357         f = f1.divideBy(f2);
358         assertEquals(3, f.getNumerator());
359         assertEquals(2, f.getDenominator());
360 
361         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(3, 5).divideBy(Fraction.ZERO));
362 
363         f1 = Fraction.getFraction(0, 5);
364         f2 = Fraction.getFraction(2, 7);
365         f = f1.divideBy(f2);
366         assertSame(Fraction.ZERO, f);
367 
368         f1 = Fraction.getFraction(2, 7);
369         f2 = Fraction.ONE;
370         f = f1.divideBy(f2);
371         assertEquals(2, f.getNumerator());
372         assertEquals(7, f.getDenominator());
373 
374         f1 = Fraction.getFraction(1, Integer.MAX_VALUE);
375         f = f1.divideBy(f1);
376         assertEquals(1, f.getNumerator());
377         assertEquals(1, f.getDenominator());
378 
379         f1 = Fraction.getFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
380         f2 = Fraction.getFraction(1, Integer.MAX_VALUE);
381         final Fraction fr = f1.divideBy(f2);
382         assertEquals(Integer.MIN_VALUE, fr.getNumerator());
383         assertEquals(1, fr.getDenominator());
384 
385         assertNullPointerException(() -> fr.divideBy(null));
386 
387         final Fraction smallest = Fraction.getFraction(1, Integer.MAX_VALUE);
388         assertThrows(ArithmeticException.class, () -> smallest.divideBy(smallest.invert())); // Should overflow
389 
390         final Fraction negative = Fraction.getFraction(1, -Integer.MAX_VALUE);
391         assertThrows(ArithmeticException.class, () -> negative.divideBy(negative.invert())); // Should overflow
392 
393         // An unreduced divisor must not trigger a spurious overflow when the reduced quotient fits an int.
394         f = Fraction.getFraction(-1, 46341).divideBy(Fraction.getFraction(1000000, 100));
395         assertEquals(-1, f.getNumerator());
396         assertEquals(463410000, f.getDenominator());
397     }
398 
399     @Test
400     void testEquals() {
401         Fraction f1;
402         Fraction f2;
403 
404         f1 = Fraction.getFraction(3, 5);
405         assertNotEquals(null, f1);
406         assertNotEquals(f1, new Object());
407         assertNotEquals(f1, Integer.valueOf(6));
408 
409         f1 = Fraction.getFraction(3, 5);
410         f2 = Fraction.getFraction(2, 5);
411         assertNotEquals(f1, f2);
412         assertEquals(f1, f1);
413         assertEquals(f2, f2);
414 
415         f2 = Fraction.getFraction(3, 5);
416         assertEquals(f1, f2);
417 
418         f2 = Fraction.getFraction(6, 10);
419         assertNotEquals(f1, f2);
420     }
421 
422     @Test
423     void testFactory_double() {
424         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Double.NaN));
425         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Double.POSITIVE_INFINITY));
426         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Double.NEGATIVE_INFINITY));
427         assertThrows(ArithmeticException.class, () -> Fraction.getFraction((double) Integer.MAX_VALUE + 1));
428         // near Integer.MAX_VALUE with a fractional part: numerator overflows an int, so it must throw
429         // rather than silently return a wrong fraction (previously -3/2 and -2147483647/2 respectively)
430         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(2147483646.5d));
431         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1073741824.5d));
432 
433         // zero
434         Fraction f = Fraction.getFraction(0.0d);
435         assertEquals(0, f.getNumerator());
436         assertEquals(1, f.getDenominator());
437 
438         // one
439         f = Fraction.getFraction(1.0d);
440         assertEquals(1, f.getNumerator());
441         assertEquals(1, f.getDenominator());
442 
443         // one half
444         f = Fraction.getFraction(0.5d);
445         assertEquals(1, f.getNumerator());
446         assertEquals(2, f.getDenominator());
447 
448         // negative
449         f = Fraction.getFraction(-0.875d);
450         assertEquals(-7, f.getNumerator());
451         assertEquals(8, f.getDenominator());
452 
453         // over 1
454         f = Fraction.getFraction(1.25d);
455         assertEquals(5, f.getNumerator());
456         assertEquals(4, f.getDenominator());
457 
458         // two thirds
459         f = Fraction.getFraction(0.66666d);
460         assertEquals(2, f.getNumerator());
461         assertEquals(3, f.getDenominator());
462 
463         // small
464         f = Fraction.getFraction(1.0d / 10001d);
465         assertEquals(0, f.getNumerator());
466         assertEquals(1, f.getDenominator());
467 
468         // normal
469         Fraction f2;
470         for (int i = 1; i <= 100; i++) { // denominator
471             for (int j = 1; j <= i; j++) { // numerator
472                 f = Fraction.getFraction((double) j / (double) i);
473 
474                 f2 = Fraction.getReducedFraction(j, i);
475                 assertEquals(f2.getNumerator(), f.getNumerator());
476                 assertEquals(f2.getDenominator(), f.getDenominator());
477             }
478         }
479         // save time by skipping some tests! (
480         for (int i = 1001; i <= 10000; i += SKIP) { // denominator
481             for (int j = 1; j <= i; j++) { // numerator
482                 f = Fraction.getFraction((double) j / (double) i);
483                 f2 = Fraction.getReducedFraction(j, i);
484                 assertEquals(f2.getNumerator(), f.getNumerator());
485                 assertEquals(f2.getDenominator(), f.getDenominator());
486             }
487         }
488     }
489 
490     @Test
491     void testFactory_int_int() {
492         Fraction f;
493 
494         // zero
495         f = Fraction.getFraction(0, 1);
496         assertEquals(0, f.getNumerator());
497         assertEquals(1, f.getDenominator());
498 
499         f = Fraction.getFraction(0, 2);
500         assertEquals(0, f.getNumerator());
501         assertEquals(2, f.getDenominator());
502 
503         // normal
504         f = Fraction.getFraction(1, 1);
505         assertEquals(1, f.getNumerator());
506         assertEquals(1, f.getDenominator());
507 
508         f = Fraction.getFraction(2, 1);
509         assertEquals(2, f.getNumerator());
510         assertEquals(1, f.getDenominator());
511 
512         f = Fraction.getFraction(23, 345);
513         assertEquals(23, f.getNumerator());
514         assertEquals(345, f.getDenominator());
515 
516         // improper
517         f = Fraction.getFraction(22, 7);
518         assertEquals(22, f.getNumerator());
519         assertEquals(7, f.getDenominator());
520 
521         // negatives
522         f = Fraction.getFraction(-6, 10);
523         assertEquals(-6, f.getNumerator());
524         assertEquals(10, f.getDenominator());
525 
526         f = Fraction.getFraction(6, -10);
527         assertEquals(-6, f.getNumerator());
528         assertEquals(10, f.getDenominator());
529 
530         f = Fraction.getFraction(-6, -10);
531         assertEquals(6, f.getNumerator());
532         assertEquals(10, f.getDenominator());
533 
534         // zero denominator
535         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, 0));
536         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(2, 0));
537         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(-3, 0));
538 
539         // very large: can't represent as unsimplified fraction, although
540         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(4, Integer.MIN_VALUE));
541         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, Integer.MIN_VALUE));
542     }
543 
544     @Test
545     void testFactory_int_int_int() {
546         Fraction f;
547 
548         // zero
549         f = Fraction.getFraction(0, 0, 2);
550         assertEquals(0, f.getNumerator());
551         assertEquals(2, f.getDenominator());
552 
553         f = Fraction.getFraction(2, 0, 2);
554         assertEquals(4, f.getNumerator());
555         assertEquals(2, f.getDenominator());
556 
557         f = Fraction.getFraction(0, 1, 2);
558         assertEquals(1, f.getNumerator());
559         assertEquals(2, f.getDenominator());
560 
561         // normal
562         f = Fraction.getFraction(1, 1, 2);
563         assertEquals(3, f.getNumerator());
564         assertEquals(2, f.getDenominator());
565 
566         // negatives
567         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, -6, -10));
568         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, -6, -10));
569         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, -6, -10));
570 
571         // negative whole
572         f = Fraction.getFraction(-1, 6, 10);
573         assertEquals(-16, f.getNumerator());
574         assertEquals(10, f.getDenominator());
575 
576         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(-1, -6, 10));
577         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(-1, 6, -10));
578         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(-1, -6, -10));
579 
580         // zero denominator
581         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(0, 1, 0));
582         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, 2, 0));
583         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(-1, -3, 0));
584         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MAX_VALUE, 1, 2));
585         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(-Integer.MAX_VALUE, 1, 2));
586 
587         // very large
588         f = Fraction.getFraction(-1, 0, Integer.MAX_VALUE);
589         assertEquals(-Integer.MAX_VALUE, f.getNumerator());
590         assertEquals(Integer.MAX_VALUE, f.getDenominator());
591 
592         // negative denominators not allowed in this constructor.
593         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(0, 4, Integer.MIN_VALUE));
594         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, 1, Integer.MAX_VALUE));
595         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(-1, 2, Integer.MAX_VALUE));
596     }
597 
598     @Test
599     void testFactory_String() {
600         assertNullPointerException(() -> Fraction.getFraction(null));
601     }
602 
603     @Test
604     void testFactory_String_double() {
605         Fraction f;
606 
607         f = Fraction.getFraction("0.0");
608         assertEquals(0, f.getNumerator());
609         assertEquals(1, f.getDenominator());
610 
611         f = Fraction.getFraction("0.2");
612         assertEquals(1, f.getNumerator());
613         assertEquals(5, f.getDenominator());
614 
615         f = Fraction.getFraction("0.5");
616         assertEquals(1, f.getNumerator());
617         assertEquals(2, f.getDenominator());
618 
619         f = Fraction.getFraction("0.66666");
620         assertEquals(2, f.getNumerator());
621         assertEquals(3, f.getDenominator());
622 
623         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2.3R"));
624         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2147483648")); // too big
625         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("."));
626     }
627 
628     @Test
629     void testFactory_String_improper() {
630         Fraction f;
631 
632         f = Fraction.getFraction("0/1");
633         assertEquals(0, f.getNumerator());
634         assertEquals(1, f.getDenominator());
635 
636         f = Fraction.getFraction("1/5");
637         assertEquals(1, f.getNumerator());
638         assertEquals(5, f.getDenominator());
639 
640         f = Fraction.getFraction("1/2");
641         assertEquals(1, f.getNumerator());
642         assertEquals(2, f.getDenominator());
643 
644         f = Fraction.getFraction("2/3");
645         assertEquals(2, f.getNumerator());
646         assertEquals(3, f.getDenominator());
647 
648         f = Fraction.getFraction("7/3");
649         assertEquals(7, f.getNumerator());
650         assertEquals(3, f.getDenominator());
651 
652         f = Fraction.getFraction("2/4");
653         assertEquals(2, f.getNumerator());
654         assertEquals(4, f.getDenominator());
655 
656         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2/d"));
657         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2e/3"));
658         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2/"));
659         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("/"));
660     }
661 
662     @Test
663     void testFactory_String_proper() {
664         Fraction f;
665 
666         f = Fraction.getFraction("0 0/1");
667         assertEquals(0, f.getNumerator());
668         assertEquals(1, f.getDenominator());
669 
670         f = Fraction.getFraction("1 1/5");
671         assertEquals(6, f.getNumerator());
672         assertEquals(5, f.getDenominator());
673 
674         f = Fraction.getFraction("7 1/2");
675         assertEquals(15, f.getNumerator());
676         assertEquals(2, f.getDenominator());
677 
678         f = Fraction.getFraction("1 2/4");
679         assertEquals(6, f.getNumerator());
680         assertEquals(4, f.getDenominator());
681 
682         f = Fraction.getFraction("-7 1/2");
683         assertEquals(-15, f.getNumerator());
684         assertEquals(2, f.getDenominator());
685 
686         f = Fraction.getFraction("-1 2/4");
687         assertEquals(-6, f.getNumerator());
688         assertEquals(4, f.getDenominator());
689 
690         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2 3"));
691         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("a 3"));
692         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2 b/4"));
693         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2 "));
694         assertThrows(NumberFormatException.class, () -> Fraction.getFraction(" 3"));
695         assertThrows(NumberFormatException.class, () -> Fraction.getFraction(" "));
696     }
697 
698     /**
699      * Tests that string contents that are well-formed but unrepresentable throw the documented NumberFormatException
700      * (with the ArithmeticException preserved as the cause) instead of an undeclared ArithmeticException.
701      */
702     @Test
703     void testFactory_String_unrepresentableThrowsNumberFormatException() {
704         // double delegation path: out of int range and non-convergent values
705         NumberFormatException e = assertThrows(NumberFormatException.class, () -> Fraction.getFraction("9999999999.5"));
706         assertTrue(e.getCause() instanceof ArithmeticException);
707         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2147483648.5"));
708         // Y/Z path: zero denominator and negation overflow
709         e = assertThrows(NumberFormatException.class, () -> Fraction.getFraction("1/0"));
710         assertTrue(e.getCause() instanceof ArithmeticException);
711         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("1/-2147483648"));
712         // X Y/Z path: zero denominator and combined-numerator overflow
713         e = assertThrows(NumberFormatException.class, () -> Fraction.getFraction("1 2/0"));
714         assertTrue(e.getCause() instanceof ArithmeticException);
715         assertThrows(NumberFormatException.class, () -> Fraction.getFraction("2147483647 1/2"));
716     }
717 
718     @Test
719     void testGets() {
720         Fraction f;
721 
722         f = Fraction.getFraction(3, 5, 6);
723         assertEquals(23, f.getNumerator());
724         assertEquals(3, f.getProperWhole());
725         assertEquals(5, f.getProperNumerator());
726         assertEquals(6, f.getDenominator());
727 
728         f = Fraction.getFraction(-3, 5, 6);
729         assertEquals(-23, f.getNumerator());
730         assertEquals(-3, f.getProperWhole());
731         assertEquals(5, f.getProperNumerator());
732         assertEquals(6, f.getDenominator());
733 
734         f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1);
735         assertEquals(Integer.MIN_VALUE, f.getNumerator());
736         assertEquals(Integer.MIN_VALUE, f.getProperWhole());
737         assertEquals(0, f.getProperNumerator());
738         assertEquals(1, f.getDenominator());
739     }
740 
741     @Test
742     void testHashCode() {
743         final Fraction f1 = Fraction.getFraction(3, 5);
744         Fraction f2 = Fraction.getFraction(3, 5);
745         assertEquals(f1.hashCode(), f2.hashCode());
746         f2 = Fraction.getFraction(2, 5);
747         assertTrue(f1.hashCode() != f2.hashCode());
748         f2 = Fraction.getFraction(6, 10);
749         assertTrue(f1.hashCode() != f2.hashCode());
750     }
751 
752     /**
753      * Tests https://issues.apache.org/jira/browse/LANG-1764
754      */
755     @ParameterizedTest
756     // @formatter:off
757     @CsvSource({
758         "0,          37,         -464320789,  46",
759         "0,          37,         -464320788,  9",
760         "0,          37,         1857283155,  38",
761         "0,          25185704,   1161454280,  1050304",
762         "0,          38817068,   1509581512,  18875972",
763         "0,          38817068,   -2146369536, 2145078572",
764         "1400217380, 128,        2092630052,  150535040",
765         "1400217380, 128,        -580400986,  268435638",
766         "1400217380, 2147483592, -2147483648, 268435452",
767         "1756395909, 4194598,    1174949894,  42860673"
768     })
769     // @formatter:on
770     void testHashCodeNotEquals(final int f1n, final int f1d, final int f2n, final int f2d) {
771         assertNotEquals(Fraction.getFraction(f1n, f1d), Fraction.getFraction(f2n, f2d));
772         assertNotEquals(Fraction.getFraction(f1n, f1d).hashCode(), Fraction.getFraction(f2n, f2d).hashCode());
773     }
774 
775     @Test
776     void testInvert() {
777         Fraction f;
778 
779         f = Fraction.getFraction(50, 75);
780         f = f.invert();
781         assertEquals(75, f.getNumerator());
782         assertEquals(50, f.getDenominator());
783 
784         f = Fraction.getFraction(4, 3);
785         f = f.invert();
786         assertEquals(3, f.getNumerator());
787         assertEquals(4, f.getDenominator());
788 
789         f = Fraction.getFraction(-15, 47);
790         f = f.invert();
791         assertEquals(-47, f.getNumerator());
792         assertEquals(15, f.getDenominator());
793 
794         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(0, 3).invert());
795         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).invert());
796 
797         f = Fraction.getFraction(Integer.MAX_VALUE, 1);
798         f = f.invert();
799         assertEquals(1, f.getNumerator());
800         assertEquals(Integer.MAX_VALUE, f.getDenominator());
801     }
802 
803     @Test
804     void testMultiply() {
805         Fraction f;
806         Fraction f1;
807         Fraction f2;
808 
809         f1 = Fraction.getFraction(3, 5);
810         f2 = Fraction.getFraction(2, 5);
811         f = f1.multiplyBy(f2);
812         assertEquals(6, f.getNumerator());
813         assertEquals(25, f.getDenominator());
814 
815         f1 = Fraction.getFraction(6, 10);
816         f2 = Fraction.getFraction(6, 10);
817         f = f1.multiplyBy(f2);
818         assertEquals(9, f.getNumerator());
819         assertEquals(25, f.getDenominator());
820         f = f.multiplyBy(f2);
821         assertEquals(27, f.getNumerator());
822         assertEquals(125, f.getDenominator());
823 
824         f1 = Fraction.getFraction(3, 5);
825         f2 = Fraction.getFraction(-2, 5);
826         f = f1.multiplyBy(f2);
827         assertEquals(-6, f.getNumerator());
828         assertEquals(25, f.getDenominator());
829 
830         f1 = Fraction.getFraction(-3, 5);
831         f2 = Fraction.getFraction(-2, 5);
832         f = f1.multiplyBy(f2);
833         assertEquals(6, f.getNumerator());
834         assertEquals(25, f.getDenominator());
835 
836         f1 = Fraction.getFraction(0, 5);
837         f2 = Fraction.getFraction(2, 7);
838         f = f1.multiplyBy(f2);
839         assertSame(Fraction.ZERO, f);
840 
841         f1 = Fraction.getFraction(2, 7);
842         f2 = Fraction.ONE;
843         f = f1.multiplyBy(f2);
844         assertEquals(2, f.getNumerator());
845         assertEquals(7, f.getDenominator());
846 
847         f1 = Fraction.getFraction(Integer.MAX_VALUE, 1);
848         f2 = Fraction.getFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
849         f = f1.multiplyBy(f2);
850         assertEquals(Integer.MIN_VALUE, f.getNumerator());
851         assertEquals(1, f.getDenominator());
852 
853         final Fraction fr = f;
854         assertNullPointerException(() -> fr.multiplyBy(null));
855 
856         final Fraction fr1 = Fraction.getFraction(1, Integer.MAX_VALUE);
857         assertThrows(ArithmeticException.class, () -> fr1.multiplyBy(fr1));
858 
859         final Fraction fr2 = Fraction.getFraction(1, -Integer.MAX_VALUE);
860         assertThrows(ArithmeticException.class, () -> fr2.multiplyBy(fr2));
861 
862         // An unreduced operand must not trigger a spurious overflow when the reduced product fits an int.
863         f = Fraction.getFraction(-1, 46341).multiplyBy(Fraction.getFraction(100, 1000000));
864         assertEquals(-1, f.getNumerator());
865         assertEquals(463410000, f.getDenominator());
866 
867         f = Fraction.getFraction(1, 10000).multiplyBy(Fraction.getFraction(100, 1000000));
868         assertEquals(1, f.getNumerator());
869         assertEquals(100000000, f.getDenominator());
870     }
871 
872     @Test
873     void testNegate() {
874         Fraction f;
875 
876         f = Fraction.getFraction(50, 75);
877         f = f.negate();
878         assertEquals(-50, f.getNumerator());
879         assertEquals(75, f.getDenominator());
880 
881         f = Fraction.getFraction(-50, 75);
882         f = f.negate();
883         assertEquals(50, f.getNumerator());
884         assertEquals(75, f.getDenominator());
885 
886         // large values
887         f = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
888         f = f.negate();
889         assertEquals(Integer.MIN_VALUE + 2, f.getNumerator());
890         assertEquals(Integer.MAX_VALUE, f.getDenominator());
891 
892         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).negate());
893     }
894 
895     @Test
896     void testPow() {
897         Fraction f;
898 
899         f = Fraction.getFraction(3, 5);
900         assertEquals(Fraction.ONE, f.pow(0));
901 
902         f = Fraction.getFraction(3, 5);
903         assertSame(f, f.pow(1));
904         assertEquals(f, f.pow(1));
905 
906         f = Fraction.getFraction(3, 5);
907         f = f.pow(2);
908         assertEquals(9, f.getNumerator());
909         assertEquals(25, f.getDenominator());
910 
911         f = Fraction.getFraction(3, 5);
912         f = f.pow(3);
913         assertEquals(27, f.getNumerator());
914         assertEquals(125, f.getDenominator());
915 
916         f = Fraction.getFraction(3, 5);
917         f = f.pow(-1);
918         assertEquals(5, f.getNumerator());
919         assertEquals(3, f.getDenominator());
920 
921         f = Fraction.getFraction(3, 5);
922         f = f.pow(-2);
923         assertEquals(25, f.getNumerator());
924         assertEquals(9, f.getDenominator());
925 
926         // check unreduced fractions stay that way.
927         f = Fraction.getFraction(6, 10);
928         assertEquals(Fraction.ONE, f.pow(0));
929 
930         f = Fraction.getFraction(6, 10);
931         assertEquals(f, f.pow(1));
932         assertNotEquals(f.pow(1), Fraction.getFraction(3, 5));
933 
934         f = Fraction.getFraction(6, 10);
935         f = f.pow(2);
936         assertEquals(9, f.getNumerator());
937         assertEquals(25, f.getDenominator());
938 
939         f = Fraction.getFraction(6, 10);
940         f = f.pow(3);
941         assertEquals(27, f.getNumerator());
942         assertEquals(125, f.getDenominator());
943 
944         f = Fraction.getFraction(6, 10);
945         f = f.pow(-1);
946         assertEquals(10, f.getNumerator());
947         assertEquals(6, f.getDenominator());
948 
949         f = Fraction.getFraction(6, 10);
950         f = f.pow(-2);
951         assertEquals(25, f.getNumerator());
952         assertEquals(9, f.getDenominator());
953 
954         // zero to any positive power is still zero.
955         f = Fraction.getFraction(0, 1231);
956         f = f.pow(1);
957         assertEquals(0, f.compareTo(Fraction.ZERO));
958         assertEquals(0, f.getNumerator());
959         assertEquals(1231, f.getDenominator());
960         f = f.pow(2);
961         assertEquals(0, f.compareTo(Fraction.ZERO));
962         assertEquals(0, f.getNumerator());
963         assertEquals(1, f.getDenominator());
964 
965         // zero to negative powers should throw an exception
966         final Fraction fr = f;
967         assertThrows(ArithmeticException.class, () -> fr.pow(-1));
968         assertThrows(ArithmeticException.class, () -> fr.pow(Integer.MIN_VALUE));
969 
970         // one to any power is still one.
971         f = Fraction.getFraction(1, 1);
972         f = f.pow(0);
973         assertEquals(f, Fraction.ONE);
974         f = f.pow(1);
975         assertEquals(f, Fraction.ONE);
976         f = f.pow(-1);
977         assertEquals(f, Fraction.ONE);
978         f = f.pow(Integer.MAX_VALUE);
979         assertEquals(f, Fraction.ONE);
980         f = f.pow(Integer.MIN_VALUE);
981         assertEquals(f, Fraction.ONE);
982 
983         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MAX_VALUE, 1).pow(2));
984 
985         // Numerator growing too negative during the pow operation.
986         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).pow(3));
987 
988         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(65536, 1).pow(2));
989     }
990 
991     @Test
992     void testReduce() {
993         Fraction f;
994 
995         f = Fraction.getFraction(50, 75);
996         Fraction result = f.reduce();
997         assertEquals(2, result.getNumerator());
998         assertEquals(3, result.getDenominator());
999 
1000         f = Fraction.getFraction(-2, -3);
1001         result = f.reduce();
1002         assertEquals(2, result.getNumerator());
1003         assertEquals(3, result.getDenominator());
1004 
1005         f = Fraction.getFraction(2, -3);
1006         result = f.reduce();
1007         assertEquals(-2, result.getNumerator());
1008         assertEquals(3, result.getDenominator());
1009 
1010         f = Fraction.getFraction(-2, 3);
1011         result = f.reduce();
1012         assertEquals(-2, result.getNumerator());
1013         assertEquals(3, result.getDenominator());
1014         assertSame(f, result);
1015 
1016         f = Fraction.getFraction(2, 3);
1017         result = f.reduce();
1018         assertEquals(2, result.getNumerator());
1019         assertEquals(3, result.getDenominator());
1020         assertSame(f, result);
1021 
1022         f = Fraction.getFraction(0, 1);
1023         result = f.reduce();
1024         assertEquals(0, result.getNumerator());
1025         assertEquals(1, result.getDenominator());
1026         assertSame(f, result);
1027 
1028         f = Fraction.getFraction(0, 100);
1029         result = f.reduce();
1030         assertEquals(0, result.getNumerator());
1031         assertEquals(1, result.getDenominator());
1032         assertSame(result, Fraction.ZERO);
1033 
1034         f = Fraction.getFraction(Integer.MIN_VALUE, 2);
1035         result = f.reduce();
1036         assertEquals(Integer.MIN_VALUE / 2, result.getNumerator());
1037         assertEquals(1, result.getDenominator());
1038     }
1039 
1040     @Test
1041     void testReducedFactory_int_int() {
1042         Fraction f;
1043 
1044         // zero
1045         f = Fraction.getReducedFraction(0, 1);
1046         assertEquals(0, f.getNumerator());
1047         assertEquals(1, f.getDenominator());
1048 
1049         // normal
1050         f = Fraction.getReducedFraction(1, 1);
1051         assertEquals(1, f.getNumerator());
1052         assertEquals(1, f.getDenominator());
1053 
1054         f = Fraction.getReducedFraction(2, 1);
1055         assertEquals(2, f.getNumerator());
1056         assertEquals(1, f.getDenominator());
1057 
1058         // improper
1059         f = Fraction.getReducedFraction(22, 7);
1060         assertEquals(22, f.getNumerator());
1061         assertEquals(7, f.getDenominator());
1062 
1063         // negatives
1064         f = Fraction.getReducedFraction(-6, 10);
1065         assertEquals(-3, f.getNumerator());
1066         assertEquals(5, f.getDenominator());
1067 
1068         f = Fraction.getReducedFraction(6, -10);
1069         assertEquals(-3, f.getNumerator());
1070         assertEquals(5, f.getDenominator());
1071 
1072         f = Fraction.getReducedFraction(-6, -10);
1073         assertEquals(3, f.getNumerator());
1074         assertEquals(5, f.getDenominator());
1075 
1076         // zero denominator
1077         assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(1, 0));
1078         assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(2, 0));
1079         assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(-3, 0));
1080 
1081         // reduced
1082         f = Fraction.getReducedFraction(0, 2);
1083         assertEquals(0, f.getNumerator());
1084         assertEquals(1, f.getDenominator());
1085 
1086         f = Fraction.getReducedFraction(2, 2);
1087         assertEquals(1, f.getNumerator());
1088         assertEquals(1, f.getDenominator());
1089 
1090         f = Fraction.getReducedFraction(2, 4);
1091         assertEquals(1, f.getNumerator());
1092         assertEquals(2, f.getDenominator());
1093 
1094         f = Fraction.getReducedFraction(15, 10);
1095         assertEquals(3, f.getNumerator());
1096         assertEquals(2, f.getDenominator());
1097 
1098         f = Fraction.getReducedFraction(121, 22);
1099         assertEquals(11, f.getNumerator());
1100         assertEquals(2, f.getDenominator());
1101 
1102         // Extreme values
1103         // OK, can reduce before negating
1104         f = Fraction.getReducedFraction(-2, Integer.MIN_VALUE);
1105         assertEquals(1, f.getNumerator());
1106         assertEquals(-(Integer.MIN_VALUE / 2), f.getDenominator());
1107 
1108         // Can't reduce, negation will throw
1109         assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(-7, Integer.MIN_VALUE));
1110 
1111         // LANG-662
1112         f = Fraction.getReducedFraction(Integer.MIN_VALUE, 2);
1113         assertEquals(Integer.MIN_VALUE / 2, f.getNumerator());
1114         assertEquals(1, f.getDenominator());
1115     }
1116 
1117     @Test
1118     void testReducedFactoryIntegerMinValue() {
1119         Fraction f = Fraction.getReducedFraction(Integer.MIN_VALUE, -2);
1120         assertEquals(1073741824, f.getNumerator());
1121         assertEquals(1, f.getDenominator());
1122 
1123         f = Fraction.getReducedFraction(Integer.MIN_VALUE, -6);
1124         assertEquals(1073741824, f.getNumerator());
1125         assertEquals(3, f.getDenominator());
1126 
1127         f = Fraction.getReducedFraction(Integer.MIN_VALUE, Integer.MIN_VALUE);
1128         assertEquals(1, f.getNumerator());
1129         assertEquals(1, f.getDenominator());
1130 
1131         assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(Integer.MIN_VALUE, -1));
1132         assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(Integer.MIN_VALUE, -3));
1133 
1134         f = Fraction.getReducedFraction(-2, Integer.MIN_VALUE);
1135         assertEquals(1, f.getNumerator());
1136         assertEquals(1073741824, f.getDenominator());
1137 
1138         assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(-7, Integer.MIN_VALUE));
1139     }
1140 
1141     @Test
1142     void testSubtract() {
1143         Fraction f;
1144         Fraction f1;
1145         Fraction f2;
1146 
1147         f1 = Fraction.getFraction(3, 5);
1148         f2 = Fraction.getFraction(1, 5);
1149         f = f1.subtract(f2);
1150         assertEquals(2, f.getNumerator());
1151         assertEquals(5, f.getDenominator());
1152 
1153         f1 = Fraction.getFraction(7, 5);
1154         f2 = Fraction.getFraction(2, 5);
1155         f = f1.subtract(f2);
1156         assertEquals(1, f.getNumerator());
1157         assertEquals(1, f.getDenominator());
1158 
1159         f1 = Fraction.getFraction(3, 5);
1160         f2 = Fraction.getFraction(3, 5);
1161         f = f1.subtract(f2);
1162         assertEquals(0, f.getNumerator());
1163         assertEquals(1, f.getDenominator());
1164 
1165         f1 = Fraction.getFraction(3, 5);
1166         f2 = Fraction.getFraction(-4, 5);
1167         f = f1.subtract(f2);
1168         assertEquals(7, f.getNumerator());
1169         assertEquals(5, f.getDenominator());
1170 
1171         f1 = Fraction.getFraction(0, 5);
1172         f2 = Fraction.getFraction(4, 5);
1173         f = f1.subtract(f2);
1174         assertEquals(-4, f.getNumerator());
1175         assertEquals(5, f.getDenominator());
1176 
1177         f1 = Fraction.getFraction(0, 5);
1178         f2 = Fraction.getFraction(-4, 5);
1179         f = f1.subtract(f2);
1180         assertEquals(4, f.getNumerator());
1181         assertEquals(5, f.getDenominator());
1182 
1183         f1 = Fraction.getFraction(3, 5);
1184         f2 = Fraction.getFraction(1, 2);
1185         f = f1.subtract(f2);
1186         assertEquals(1, f.getNumerator());
1187         assertEquals(10, f.getDenominator());
1188 
1189         f1 = Fraction.getFraction(0, 5);
1190         f2 = Fraction.getFraction(1, 5);
1191         f = f2.subtract(f1);
1192         assertSame(f2, f);
1193 
1194         final Fraction fr = f;
1195         assertNullPointerException(() -> fr.subtract(null));
1196 
1197         // if this fraction is subtracted naively, it will overflow.
1198         // check that it doesn't.
1199         f1 = Fraction.getFraction(1, 32768 * 3);
1200         f2 = Fraction.getFraction(1, 59049);
1201         f = f1.subtract(f2);
1202         assertEquals(-13085, f.getNumerator());
1203         assertEquals(1934917632, f.getDenominator());
1204 
1205         f1 = Fraction.getFraction(Integer.MIN_VALUE, 3);
1206         f2 = Fraction.ONE_THIRD.negate();
1207         f = f1.subtract(f2);
1208         assertEquals(Integer.MIN_VALUE + 1, f.getNumerator());
1209         assertEquals(3, f.getDenominator());
1210 
1211         f1 = Fraction.getFraction(Integer.MAX_VALUE, 1);
1212         f2 = Fraction.ONE;
1213         f = f1.subtract(f2);
1214         assertEquals(Integer.MAX_VALUE - 1, f.getNumerator());
1215         assertEquals(1, f.getDenominator());
1216 
1217         // Should overflow
1218         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, Integer.MAX_VALUE).subtract(Fraction.getFraction(1, Integer.MAX_VALUE - 1)));
1219 
1220         // denominator should not be a multiple of 2 or 3 to trigger overflow
1221         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 5).subtract(Fraction.getFraction(1, 5)));
1222 
1223         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).subtract(Fraction.ONE));
1224 
1225         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MAX_VALUE, 1).subtract(Fraction.ONE.negate()));
1226 
1227         // Should overflow
1228         assertThrows(ArithmeticException.class, () -> Fraction.getFraction(3, 327680).subtract(Fraction.getFraction(2, 59049)));
1229 
1230         // the cross products u*v' and u'*v overflow an int, but the reduced result fits.
1231         f1 = Fraction.getFraction(Integer.MAX_VALUE, 2);
1232         f2 = Fraction.getFraction(Integer.MAX_VALUE, 1);
1233         f = f1.subtract(f2);
1234         assertEquals(-Integer.MAX_VALUE, f.getNumerator());
1235         assertEquals(2, f.getDenominator());
1236     }
1237 
1238     @Test
1239     void testToProperString() {
1240         Fraction f;
1241 
1242         f = Fraction.getFraction(3, 5);
1243         final String str = f.toProperString();
1244         assertEquals("3/5", str);
1245         assertSame(str, f.toProperString());
1246 
1247         f = Fraction.getFraction(7, 5);
1248         assertEquals("1 2/5", f.toProperString());
1249 
1250         f = Fraction.getFraction(14, 10);
1251         assertEquals("1 4/10", f.toProperString());
1252 
1253         f = Fraction.getFraction(4, 2);
1254         assertEquals("2", f.toProperString());
1255 
1256         f = Fraction.getFraction(0, 2);
1257         assertEquals("0", f.toProperString());
1258 
1259         f = Fraction.getFraction(2, 2);
1260         assertEquals("1", f.toProperString());
1261 
1262         f = Fraction.getFraction(-7, 5);
1263         assertEquals("-1 2/5", f.toProperString());
1264 
1265         f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1);
1266         assertEquals("-2147483648", f.toProperString());
1267 
1268         f = Fraction.getFraction(-1, 1, Integer.MAX_VALUE);
1269         assertEquals("-1 1/2147483647", f.toProperString());
1270 
1271         assertEquals("-1", Fraction.getFraction(-1).toProperString());
1272     }
1273 
1274     @Test
1275     void testToString() {
1276         Fraction f;
1277 
1278         f = Fraction.getFraction(3, 5);
1279         final String str = f.toString();
1280         assertEquals("3/5", str);
1281         assertSame(str, f.toString());
1282 
1283         f = Fraction.getFraction(7, 5);
1284         assertEquals("7/5", f.toString());
1285 
1286         f = Fraction.getFraction(4, 2);
1287         assertEquals("4/2", f.toString());
1288 
1289         f = Fraction.getFraction(0, 2);
1290         assertEquals("0/2", f.toString());
1291 
1292         f = Fraction.getFraction(2, 2);
1293         assertEquals("2/2", f.toString());
1294 
1295         f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1);
1296         assertEquals("-2147483648/1", f.toString());
1297 
1298         f = Fraction.getFraction(-1, 1, Integer.MAX_VALUE);
1299         assertEquals("-2147483648/2147483647", f.toString());
1300     }
1301 }