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 java.io.Serializable;
25 import java.math.BigInteger;
26 import java.util.function.Function;
27 import java.util.function.Predicate;
28 import java.util.stream.Stream;
29
30 import org.hipparchus.FieldElement;
31 import org.hipparchus.exception.LocalizedCoreFormats;
32 import org.hipparchus.exception.MathIllegalStateException;
33 import org.hipparchus.exception.MathRuntimeException;
34 import org.hipparchus.fraction.ConvergentsIterator.ConvergenceStep;
35 import org.hipparchus.util.ArithmeticUtils;
36 import org.hipparchus.util.FastMath;
37 import org.hipparchus.util.MathUtils;
38 import org.hipparchus.util.Pair;
39 import org.hipparchus.util.Precision;
40
41 /**
42 * Representation of a rational number.
43 */
44 public class Fraction
45 extends Number
46 implements FieldElement<Fraction>, Comparable<Fraction>, Serializable {
47
48 /** A fraction representing "2 / 1". */
49 public static final Fraction TWO = new Fraction(2, 1);
50
51 /** A fraction representing "1". */
52 public static final Fraction ONE = new Fraction(1, 1);
53
54 /** A fraction representing "0". */
55 public static final Fraction ZERO = new Fraction(0, 1);
56
57 /** A fraction representing "4/5". */
58 public static final Fraction FOUR_FIFTHS = new Fraction(4, 5);
59
60 /** A fraction representing "1/5". */
61 public static final Fraction ONE_FIFTH = new Fraction(1, 5);
62
63 /** A fraction representing "1/2". */
64 public static final Fraction ONE_HALF = new Fraction(1, 2);
65
66 /** A fraction representing "1/4". */
67 public static final Fraction ONE_QUARTER = new Fraction(1, 4);
68
69 /** A fraction representing "1/3". */
70 public static final Fraction ONE_THIRD = new Fraction(1, 3);
71
72 /** A fraction representing "3/5". */
73 public static final Fraction THREE_FIFTHS = new Fraction(3, 5);
74
75 /** A fraction representing "3/4". */
76 public static final Fraction THREE_QUARTERS = new Fraction(3, 4);
77
78 /** A fraction representing "2/5". */
79 public static final Fraction TWO_FIFTHS = new Fraction(2, 5);
80
81 /** A fraction representing "2/4". */
82 public static final Fraction TWO_QUARTERS = new Fraction(2, 4);
83
84 /** A fraction representing "2/3". */
85 public static final Fraction TWO_THIRDS = new Fraction(2, 3);
86
87 /** A fraction representing "-1 / 1". */
88 public static final Fraction MINUS_ONE = new Fraction(-1, 1);
89
90 /** Serializable version identifier */
91 private static final long serialVersionUID = 3698073679419233275L;
92
93 /** The default epsilon used for convergence. */
94 private static final double DEFAULT_EPSILON = 1e-5;
95
96 /** Convert a convergence step to the corresponding double fraction. */
97 private static final Function<ConvergenceStep, Fraction> STEP_TO_FRACTION = //
98 s -> new Fraction((int) s.getNumerator(), (int) s.getDenominator());
99
100 /** The denominator. */
101 private final int denominator;
102
103 /** The numerator. */
104 private final int numerator;
105
106 /**
107 * Create a fraction given the double value.
108 * @param value the double value to convert to a fraction.
109 * @throws MathIllegalStateException if the continued fraction failed to
110 * converge.
111 */
112 public Fraction(double value) throws MathIllegalStateException {
113 this(value, DEFAULT_EPSILON, 100);
114 }
115
116 /**
117 * Create a fraction given the double value and maximum error allowed.
118 * <p>
119 * References:
120 * <ul>
121 * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
122 * Continued Fraction</a> equations (11) and (22)-(26)</li>
123 * </ul>
124 *
125 * @param value the double value to convert to a fraction.
126 * @param epsilon maximum error allowed. The resulting fraction is within
127 * {@code epsilon} of {@code value}, in absolute terms.
128 * @param maxIterations maximum number of convergents
129 * @throws MathIllegalStateException if the continued fraction failed to
130 * converge.
131 */
132 public Fraction(double value, double epsilon, int maxIterations)
133 throws MathIllegalStateException {
134 ConvergenceStep converged = convergent(value, maxIterations, s -> {
135 double quotient = s.getFractionValue();
136 return Precision.equals(quotient, value, 1) || FastMath.abs(quotient - value) < epsilon;
137 }).getKey();
138 if (FastMath.abs(converged.getFractionValue() - value) < epsilon) {
139 this.numerator = (int) converged.getNumerator();
140 this.denominator = (int) converged.getDenominator();
141 } else {
142 throw new MathIllegalStateException(LocalizedCoreFormats.FAILED_FRACTION_CONVERSION,
143 value, maxIterations);
144 }
145 }
146
147 /**
148 * Create a fraction given the double value and maximum denominator.
149 * <p>
150 * References:
151 * <ul>
152 * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
153 * Continued Fraction</a> equations (11) and (22)-(26)</li>
154 * </ul>
155 *
156 * @param value the double value to convert to a fraction.
157 * @param maxDenominator The maximum allowed value for denominator
158 * @throws MathIllegalStateException if the continued fraction failed to
159 * converge
160 */
161 public Fraction(double value, int maxDenominator)
162 throws MathIllegalStateException {
163 final int maxIterations = 100;
164 ConvergenceStep[] lastValid = new ConvergenceStep[1];
165 try {
166 convergent(value, maxIterations, s -> {
167 if (s.getDenominator() < maxDenominator) {
168 lastValid[0] = s;
169 }
170 return Precision.equals(s.getFractionValue(), value, 1);
171 });
172 } catch (MathIllegalStateException e) { // NOPMD - ignore overflows and just take the last valid result
173 }
174 if (lastValid[0] != null) {
175 this.numerator = (int) lastValid[0].getNumerator();
176 this.denominator = (int) lastValid[0].getDenominator();
177 } else {
178 throw new MathIllegalStateException(LocalizedCoreFormats.FAILED_FRACTION_CONVERSION,
179 value, maxIterations);
180 }
181 }
182
183 /**
184 * Create a fraction from an int.
185 * The fraction is num / 1.
186 * @param num the numerator.
187 */
188 public Fraction(int num) {
189 this(num, 1);
190 }
191
192 /**
193 * Create a fraction given the numerator and denominator. The fraction is
194 * reduced to lowest terms.
195 * @param num the numerator.
196 * @param den the denominator.
197 * @throws MathRuntimeException if the denominator is {@code zero}
198 */
199 public Fraction(int num, int den) {
200 if (den == 0) {
201 throw new MathRuntimeException(LocalizedCoreFormats.ZERO_DENOMINATOR_IN_FRACTION,
202 num, den);
203 }
204 if (den < 0) {
205 if (num == Integer.MIN_VALUE ||
206 den == Integer.MIN_VALUE) {
207 throw new MathRuntimeException(LocalizedCoreFormats.OVERFLOW_IN_FRACTION,
208 num, den);
209 }
210 num = -num;
211 den = -den;
212 }
213 // reduce numerator and denominator by greatest common denominator.
214 final int d = ArithmeticUtils.gcd(num, den);
215 if (d > 1) {
216 num /= d;
217 den /= d;
218 }
219
220 // move sign to numerator.
221 if (den < 0) {
222 num = -num;
223 den = -den;
224 }
225 this.numerator = num;
226 this.denominator = den;
227 }
228
229 /**
230 * A test to determine if a series of fractions has converged.
231 */
232 @FunctionalInterface
233 public interface ConvergenceTest {
234 /**
235 * Evaluates if the fraction formed by {@code numerator/denominator} satisfies
236 * this convergence test.
237 *
238 * @param numerator the numerator
239 * @param denominator the denominator
240 * @return if this convergence test is satisfied
241 */
242 boolean test(int numerator, int denominator); // NOPMD - this is not a Junit test, PMD false positive here
243 }
244
245 /** Generate a {@link Stream stream} of convergents from a real number.
246 * @param value value to approximate
247 * @param maxConvergents maximum number of convergents.
248 * @return stream of {@link Fraction} convergents approximating {@code value}
249 * @since 2.1
250 */
251 public static Stream<Fraction> convergents(final double value, final int maxConvergents) {
252 if (FastMath.abs(value) > Integer.MAX_VALUE) {
253 throw new MathIllegalStateException(LocalizedCoreFormats.FRACTION_CONVERSION_OVERFLOW, value, value, 1l);
254 }
255 return ConvergentsIterator.convergents(value, maxConvergents).map(STEP_TO_FRACTION);
256 }
257
258 /**
259 * Returns the last element of the series of convergent-steps to approximate the
260 * given value.
261 * <p>
262 * The series terminates either at the first step that satisfies the given
263 * {@code convergenceTest} or after at most {@code maxConvergents} elements. The
264 * returned Pair consists of that terminal {@link Fraction} and a
265 * {@link Boolean} that indicates if it satisfies the given convergence tests.
266 * If the returned pair's value is {@code false} the element at position
267 * {@code maxConvergents} was examined but failed to satisfy the
268 * {@code convergenceTest}. A caller can then decide to accept the result
269 * nevertheless or to discard it. This method is usually faster than
270 * {@link #convergents(double, int)} if only the terminal element is of
271 * interest.
272 *
273 * @param value value to approximate
274 * @param maxConvergents maximum number of convergents to examine
275 * @param convergenceTest the test if the series has converged at a step
276 * @return the pair of last element of the series of convergents and a boolean
277 * indicating if that element satisfies the specified convergent test
278 */
279 public static Pair<Fraction, Boolean> convergent(double value, int maxConvergents, ConvergenceTest convergenceTest) {
280 Pair<ConvergenceStep, Boolean> converged = convergent(value, maxConvergents, s -> {
281 customAssertNoIntegerOverflow(s, value);
282 return convergenceTest.test((int) s.getNumerator(), (int) s.getDenominator());
283 });
284 return Pair.create(STEP_TO_FRACTION.apply(converged.getKey()), converged.getValue());
285 }
286
287 /** Create a convergent-steps to approximate the given value.
288 * @param value value to approximate
289 * @param maxConvergents maximum number of convergents to examine
290 * @param convergenceTests the test if the series has converged at a step
291 * @return the pair of last element of the series of convergents and a boolean
292 * indicating if that element satisfies the specified convergent test
293 */
294 private static Pair<ConvergenceStep, Boolean> convergent(double value, int maxConvergents,
295 Predicate<ConvergenceStep> convergenceTests) {
296 if (FastMath.abs(value) > Integer.MAX_VALUE) {
297 throw new MathIllegalStateException(LocalizedCoreFormats.FRACTION_CONVERSION_OVERFLOW, value, value, 1l);
298 }
299 return ConvergentsIterator.convergent(value, maxConvergents, s -> {
300 customAssertNoIntegerOverflow(s, value);
301 return convergenceTests.test(s);
302 });
303 }
304
305 /** Check no overflow occurred.
306 * @param s convergent
307 * @param value corresponding value
308 */
309 private static void customAssertNoIntegerOverflow(ConvergenceStep s, double value) {
310 if (s.getNumerator() > Integer.MAX_VALUE || s.getDenominator() > Integer.MAX_VALUE) {
311 throw new MathIllegalStateException(LocalizedCoreFormats.FRACTION_CONVERSION_OVERFLOW, value,
312 s.getNumerator(), s.getDenominator());
313 }
314 }
315
316 /** {@inheritDoc} */
317 @Override
318 public double getReal() {
319 return doubleValue();
320 }
321
322 /** Check if a fraction is an integer.
323 * @return true of fraction is an integer
324 */
325 public boolean isInteger() {
326 return denominator == 1;
327 }
328
329 /** Returns the signum function of this fraction.
330 * <p>
331 * The return value is -1 if the specified value is negative;
332 * 0 if the specified value is zero; and 1 if the specified value is positive.
333 * </p>
334 * @return the signum function of this fraction
335 * @since 1.7
336 */
337 public int signum() {
338 return Integer.signum(numerator);
339 }
340
341 /**
342 * Returns the absolute value of this fraction.
343 * @return the absolute value.
344 */
345 public Fraction abs() {
346 Fraction ret;
347 if (numerator >= 0) {
348 ret = this;
349 } else {
350 ret = negate();
351 }
352 return ret;
353 }
354
355 /**
356 * Compares this object to another based on size.
357 * @param object the object to compare to
358 * @return -1 if this is less than {@code object}, +1 if this is greater
359 * than {@code object}, 0 if they are equal.
360 */
361 @Override
362 public int compareTo(Fraction object) {
363 long nOd = ((long) numerator) * object.denominator;
364 long dOn = ((long) denominator) * object.numerator;
365 return Long.compare(nOd, dOn);
366 }
367
368 /**
369 * Gets the fraction as a {@code double}. This calculates the fraction as
370 * the numerator divided by denominator.
371 * @return the fraction as a {@code double}
372 */
373 @Override
374 public double doubleValue() {
375 return ((double) numerator) / denominator;
376 }
377
378 /**
379 * Test for the equality of two fractions. If the lowest term
380 * numerator and denominators are the same for both fractions, the two
381 * fractions are considered to be equal.
382 * @param other fraction to test for equality to this fraction
383 * @return true if two fractions are equal, false if object is
384 * {@code null}, not an instance of {@link Fraction}, or not equal
385 * to this fraction instance.
386 */
387 @Override
388 public boolean equals(Object other) {
389 if (this == other) {
390 return true;
391 }
392 if (other instanceof Fraction) {
393 // since fractions are always in lowest terms, numerators and
394 // denominators can be compared directly for equality.
395 Fraction rhs = (Fraction)other;
396 return (numerator == rhs.numerator) &&
397 (denominator == rhs.denominator);
398 }
399 return false;
400 }
401
402 /**
403 * Gets the fraction as a {@code float}. This calculates the fraction as
404 * the numerator divided by denominator.
405 * @return the fraction as a {@code float}
406 */
407 @Override
408 public float floatValue() {
409 return (float)doubleValue();
410 }
411
412 /**
413 * Rational number greatest common divisor.
414 *
415 * @param s fraction.
416 * @return gcd(this, s).
417 * @since 3.1
418 */
419 public Fraction gcd(Fraction s) {
420 if (s.isZero()) {
421 return this;
422 }
423 if (this.isZero()) {
424 return s;
425 }
426 int p = ArithmeticUtils.gcd(numerator, s.numerator);
427 int q = ArithmeticUtils.lcm(denominator, s.denominator);
428 return new Fraction(p, q);
429 }
430
431 /**
432 * Rational number least common multiple.
433 *
434 * @param s fraction.
435 * @return lcm(this, s).
436 * @since 3.1
437 */
438 public Fraction lcm(Fraction s) {
439 if (s.isZero()) {
440 return ZERO;
441 }
442 if (this.isZero()) {
443 return ZERO;
444 }
445 return new Fraction(ArithmeticUtils.lcm(numerator, s.numerator),
446 ArithmeticUtils.gcd(denominator, s.denominator));
447 }
448
449 /**
450 * Access the denominator.
451 * @return the denominator.
452 */
453 public int getDenominator() {
454 return denominator;
455 }
456
457 /**
458 * Access the numerator.
459 * @return the numerator.
460 */
461 public int getNumerator() {
462 return numerator;
463 }
464
465 /**
466 * Gets a hashCode for the fraction.
467 * @return a hash code value for this object
468 */
469 @Override
470 public int hashCode() {
471 return 37 * (37 * 17 + numerator) + denominator;
472 }
473
474 /**
475 * Gets the fraction as an {@code int}. This returns the whole number part
476 * of the fraction.
477 * @return the whole number fraction part
478 */
479 @Override
480 public int intValue() {
481 return (int)doubleValue();
482 }
483
484 /**
485 * Gets the fraction as a {@code long}. This returns the whole number part
486 * of the fraction.
487 * @return the whole number fraction part
488 */
489 @Override
490 public long longValue() {
491 return (long)doubleValue();
492 }
493
494 /**
495 * Return the additive inverse of this fraction.
496 * @return the negation of this fraction.
497 */
498 @Override
499 public Fraction negate() {
500 if (numerator==Integer.MIN_VALUE) {
501 throw new MathRuntimeException(LocalizedCoreFormats.OVERFLOW_IN_FRACTION, numerator, denominator);
502 }
503 return new Fraction(-numerator, denominator);
504 }
505
506 /**
507 * Return the multiplicative inverse of this fraction.
508 * @return the reciprocal fraction
509 */
510 @Override
511 public Fraction reciprocal() {
512 return new Fraction(denominator, numerator);
513 }
514
515 /**
516 * Adds the value of this fraction to another, returning the result in reduced form.
517 * The algorithm follows Knuth, 4.5.1.
518 *
519 * @param fraction the fraction to add, must not be {@code null}
520 * @return a {@code Fraction} instance with the resulting values
521 * @throws org.hipparchus.exception.NullArgumentException if the fraction is {@code null}
522 * @throws MathRuntimeException if the resulting numerator or denominator exceeds
523 * {@code Integer.MAX_VALUE}
524 */
525 @Override
526 public Fraction add(Fraction fraction) {
527 return addSub(fraction, true /* add */);
528 }
529
530 /**
531 * Add an integer to the fraction.
532 * @param i the {@code integer} to add.
533 * @return this + i
534 */
535 public Fraction add(final int i) {
536 return new Fraction(numerator + i * denominator, denominator);
537 }
538
539 /**
540 * Subtracts the value of another fraction from the value of this one,
541 * returning the result in reduced form.
542 *
543 * @param fraction the fraction to subtract, must not be {@code null}
544 * @return a {@code Fraction} instance with the resulting values
545 * @throws org.hipparchus.exception.NullArgumentException if the fraction is {@code null}
546 * @throws MathRuntimeException if the resulting numerator or denominator
547 * cannot be represented in an {@code int}.
548 */
549 @Override
550 public Fraction subtract(Fraction fraction) {
551 return addSub(fraction, false /* subtract */);
552 }
553
554 /**
555 * Subtract an integer from the fraction.
556 * @param i the {@code integer} to subtract.
557 * @return this - i
558 */
559 public Fraction subtract(final int i) {
560 return new Fraction(numerator - i * denominator, denominator);
561 }
562
563 /**
564 * Implement add and subtract using algorithm described in Knuth 4.5.1.
565 *
566 * @param fraction the fraction to subtract, must not be {@code null}
567 * @param isAdd true to add, false to subtract
568 * @return a {@code Fraction} instance with the resulting values
569 * @throws org.hipparchus.exception.NullArgumentException if the fraction is {@code null}
570 * @throws MathRuntimeException if the resulting numerator or denominator
571 * cannot be represented in an {@code int}.
572 */
573 private Fraction addSub(Fraction fraction, boolean isAdd) {
574 MathUtils.checkNotNull(fraction, LocalizedCoreFormats.FRACTION);
575
576 // zero is identity for addition.
577 if (numerator == 0) {
578 return isAdd ? fraction : fraction.negate();
579 }
580 if (fraction.numerator == 0) {
581 return this;
582 }
583 // if denominators are randomly distributed, d1 will be 1 about 61%
584 // of the time.
585 int d1 = ArithmeticUtils.gcd(denominator, fraction.denominator);
586 if (d1==1) {
587 // result is ( (u*v' +/- u'v) / u'v')
588 int uvp = ArithmeticUtils.mulAndCheck(numerator, fraction.denominator);
589 int upv = ArithmeticUtils.mulAndCheck(fraction.numerator, denominator);
590 return new Fraction
591 (isAdd ? ArithmeticUtils.addAndCheck(uvp, upv) :
592 ArithmeticUtils.subAndCheck(uvp, upv),
593 ArithmeticUtils.mulAndCheck(denominator, fraction.denominator));
594 }
595 // the quantity 't' requires 65 bits of precision; see knuth 4.5.1
596 // exercise 7. we're going to use a BigInteger.
597 // t = u(v'/d1) +/- v(u'/d1)
598 BigInteger uvp = BigInteger.valueOf(numerator)
599 .multiply(BigInteger.valueOf(fraction.denominator / d1));
600 BigInteger upv = BigInteger.valueOf(fraction.numerator)
601 .multiply(BigInteger.valueOf(denominator / d1));
602 BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv);
603 // but d2 doesn't need extra precision because
604 // d2 = gcd(t,d1) = gcd(t mod d1, d1)
605 int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
606 int d2 = (tmodd1==0)?d1:ArithmeticUtils.gcd(tmodd1, d1);
607
608 // result is (t/d2) / (u'/d1)(v'/d2)
609 BigInteger w = t.divide(BigInteger.valueOf(d2));
610 if (w.bitLength() > 31) {
611 throw new MathRuntimeException(LocalizedCoreFormats.NUMERATOR_OVERFLOW_AFTER_MULTIPLY,
612 w);
613 }
614 return new Fraction (w.intValue(),
615 ArithmeticUtils.mulAndCheck(denominator/d1,
616 fraction.denominator/d2));
617 }
618
619 /**
620 * Multiplies the value of this fraction by another, returning the
621 * result in reduced form.
622 *
623 * @param fraction the fraction to multiply by, must not be {@code null}
624 * @return a {@code Fraction} instance with the resulting values
625 * @throws org.hipparchus.exception.NullArgumentException if the fraction is {@code null}
626 * @throws MathRuntimeException if the resulting numerator or denominator exceeds
627 * {@code Integer.MAX_VALUE}
628 */
629 @Override
630 public Fraction multiply(Fraction fraction) {
631 MathUtils.checkNotNull(fraction, LocalizedCoreFormats.FRACTION);
632 if (numerator == 0 || fraction.numerator == 0) {
633 return ZERO;
634 }
635 // knuth 4.5.1
636 // make sure we don't overflow unless the result *must* overflow.
637 int d1 = ArithmeticUtils.gcd(numerator, fraction.denominator);
638 int d2 = ArithmeticUtils.gcd(fraction.numerator, denominator);
639 return getReducedFraction
640 (ArithmeticUtils.mulAndCheck(numerator/d1, fraction.numerator/d2),
641 ArithmeticUtils.mulAndCheck(denominator/d2, fraction.denominator/d1));
642 }
643
644 /**
645 * Multiply the fraction by an integer.
646 * @param i the {@code integer} to multiply by.
647 * @return this * i
648 */
649 @Override
650 public Fraction multiply(final int i) {
651 return multiply(new Fraction(i));
652 }
653
654 /**
655 * Divide the value of this fraction by another.
656 *
657 * @param fraction the fraction to divide by, must not be {@code null}
658 * @return a {@code Fraction} instance with the resulting values
659 * @throws IllegalArgumentException if the fraction is {@code null}
660 * @throws MathRuntimeException if the fraction to divide by is zero
661 * @throws MathRuntimeException if the resulting numerator or denominator exceeds
662 * {@code Integer.MAX_VALUE}
663 */
664 @Override
665 public Fraction divide(Fraction fraction) {
666 MathUtils.checkNotNull(fraction, LocalizedCoreFormats.FRACTION);
667 if (fraction.numerator == 0) {
668 throw new MathRuntimeException(LocalizedCoreFormats.ZERO_FRACTION_TO_DIVIDE_BY,
669 fraction.numerator, fraction.denominator);
670 }
671 return multiply(fraction.reciprocal());
672 }
673
674 /**
675 * Divide the fraction by an integer.
676 * @param i the {@code integer} to divide by.
677 * @return this * i
678 */
679 public Fraction divide(final int i) {
680 return divide(new Fraction(i));
681 }
682
683 /**
684 * Gets the fraction percentage as a {@code double}. This calculates the
685 * fraction as the numerator divided by denominator multiplied by 100.
686 *
687 * @return the fraction percentage as a {@code double}.
688 */
689 public double percentageValue() {
690 return 100 * doubleValue();
691 }
692
693 /**
694 * Creates a {@code Fraction} instance with the 2 parts
695 * of a fraction Y/Z.
696 * <p>
697 * Any negative signs are resolved to be on the numerator.
698 *
699 * @param numerator the numerator, for example the three in 'three sevenths'
700 * @param denominator the denominator, for example the seven in 'three sevenths'
701 * @return a new fraction instance, with the numerator and denominator reduced
702 * @throws MathRuntimeException if the denominator is {@code zero}
703 */
704 public static Fraction getReducedFraction(int numerator, int denominator) {
705 if (denominator == 0) {
706 throw new MathRuntimeException(LocalizedCoreFormats.ZERO_DENOMINATOR_IN_FRACTION,
707 numerator, denominator);
708 }
709 if (numerator==0) {
710 return ZERO; // normalize zero.
711 }
712 // allow 2^k/-2^31 as a valid fraction (where k>0)
713 if (denominator==Integer.MIN_VALUE && (numerator&1)==0) {
714 numerator/=2; denominator/=2;
715 }
716 if (denominator < 0) {
717 if (numerator==Integer.MIN_VALUE ||
718 denominator==Integer.MIN_VALUE) {
719 throw new MathRuntimeException(LocalizedCoreFormats.OVERFLOW_IN_FRACTION,
720 numerator, denominator);
721 }
722 numerator = -numerator;
723 denominator = -denominator;
724 }
725 // simplify fraction.
726 int gcd = ArithmeticUtils.gcd(numerator, denominator);
727 numerator /= gcd;
728 denominator /= gcd;
729 return new Fraction(numerator, denominator);
730 }
731
732 /**
733 * Returns the {@code String} representing this fraction, ie
734 * "num / dem" or just "num" if the denominator is one.
735 *
736 * @return a string representation of the fraction.
737 * @see java.lang.Object#toString()
738 */
739 @Override
740 public String toString() {
741 if (denominator == 1) {
742 return Integer.toString(numerator);
743 } else if (numerator == 0) {
744 return "0";
745 } else {
746 return numerator + " / " + denominator;
747 }
748 }
749
750 /** {@inheritDoc} */
751 @Override
752 public FractionField getField() {
753 return FractionField.getInstance();
754 }
755
756 }