1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
43
44 public class Fraction
45 extends Number
46 implements FieldElement<Fraction>, Comparable<Fraction>, Serializable {
47
48
49 public static final Fraction TWO = new Fraction(2, 1);
50
51
52 public static final Fraction ONE = new Fraction(1, 1);
53
54
55 public static final Fraction ZERO = new Fraction(0, 1);
56
57
58 public static final Fraction FOUR_FIFTHS = new Fraction(4, 5);
59
60
61 public static final Fraction ONE_FIFTH = new Fraction(1, 5);
62
63
64 public static final Fraction ONE_HALF = new Fraction(1, 2);
65
66
67 public static final Fraction ONE_QUARTER = new Fraction(1, 4);
68
69
70 public static final Fraction ONE_THIRD = new Fraction(1, 3);
71
72
73 public static final Fraction THREE_FIFTHS = new Fraction(3, 5);
74
75
76 public static final Fraction THREE_QUARTERS = new Fraction(3, 4);
77
78
79 public static final Fraction TWO_FIFTHS = new Fraction(2, 5);
80
81
82 public static final Fraction TWO_QUARTERS = new Fraction(2, 4);
83
84
85 public static final Fraction TWO_THIRDS = new Fraction(2, 3);
86
87
88 public static final Fraction MINUS_ONE = new Fraction(-1, 1);
89
90
91 private static final long serialVersionUID = 3698073679419233275L;
92
93
94 private static final double DEFAULT_EPSILON = 1e-5;
95
96
97 private static final Function<ConvergenceStep, Fraction> STEP_TO_FRACTION =
98 s -> new Fraction((int) s.getNumerator(), (int) s.getDenominator());
99
100
101 private final int denominator;
102
103
104 private final int numerator;
105
106
107
108
109
110
111
112 public Fraction(double value) throws MathIllegalStateException {
113 this(value, DEFAULT_EPSILON, 100);
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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
149
150
151
152
153
154
155
156
157
158
159
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) {
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
185
186
187
188 public Fraction(int num) {
189 this(num, 1);
190 }
191
192
193
194
195
196
197
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
214 final int d = ArithmeticUtils.gcd(num, den);
215 if (d > 1) {
216 num /= d;
217 den /= d;
218 }
219
220
221 if (den < 0) {
222 num = -num;
223 den = -den;
224 }
225 this.numerator = num;
226 this.denominator = den;
227 }
228
229
230
231
232 @FunctionalInterface
233 public interface ConvergenceTest {
234
235
236
237
238
239
240
241
242 boolean test(int numerator, int denominator);
243 }
244
245
246
247
248
249
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279 public static Pair<Fraction, Boolean> convergent(double value, int maxConvergents, ConvergenceTest convergenceTest) {
280 Pair<ConvergenceStep, Boolean> converged = convergent(value, maxConvergents, s -> {
281 assertNoIntegerOverflow(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
288
289
290
291
292
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 assertNoIntegerOverflow(s, value);
301 return convergenceTests.test(s);
302 });
303 }
304
305
306
307
308
309 private static void assertNoIntegerOverflow(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
317 @Override
318 public double getReal() {
319 return doubleValue();
320 }
321
322
323
324
325 public boolean isInteger() {
326 return denominator == 1;
327 }
328
329
330
331
332
333
334
335
336
337 public int signum() {
338 return Integer.signum(numerator);
339 }
340
341
342
343
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
357
358
359
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
370
371
372
373 @Override
374 public double doubleValue() {
375 return (double)numerator / (double)denominator;
376 }
377
378
379
380
381
382
383
384
385
386
387 @Override
388 public boolean equals(Object other) {
389 if (this == other) {
390 return true;
391 }
392 if (other instanceof Fraction) {
393
394
395 Fraction rhs = (Fraction)other;
396 return (numerator == rhs.numerator) &&
397 (denominator == rhs.denominator);
398 }
399 return false;
400 }
401
402
403
404
405
406
407 @Override
408 public float floatValue() {
409 return (float)doubleValue();
410 }
411
412
413
414
415
416
417
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
433
434
435
436
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
451
452
453 public int getDenominator() {
454 return denominator;
455 }
456
457
458
459
460
461 public int getNumerator() {
462 return numerator;
463 }
464
465
466
467
468
469 @Override
470 public int hashCode() {
471 return 37 * (37 * 17 + numerator) + denominator;
472 }
473
474
475
476
477
478
479 @Override
480 public int intValue() {
481 return (int)doubleValue();
482 }
483
484
485
486
487
488
489 @Override
490 public long longValue() {
491 return (long)doubleValue();
492 }
493
494
495
496
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
508
509
510 @Override
511 public Fraction reciprocal() {
512 return new Fraction(denominator, numerator);
513 }
514
515
516
517
518
519
520
521
522
523
524
525 @Override
526 public Fraction add(Fraction fraction) {
527 return addSub(fraction, true );
528 }
529
530
531
532
533
534
535 public Fraction add(final int i) {
536 return new Fraction(numerator + i * denominator, denominator);
537 }
538
539
540
541
542
543
544
545
546
547
548
549 @Override
550 public Fraction subtract(Fraction fraction) {
551 return addSub(fraction, false );
552 }
553
554
555
556
557
558
559 public Fraction subtract(final int i) {
560 return new Fraction(numerator - i * denominator, denominator);
561 }
562
563
564
565
566
567
568
569
570
571
572
573 private Fraction addSub(Fraction fraction, boolean isAdd) {
574 MathUtils.checkNotNull(fraction, LocalizedCoreFormats.FRACTION);
575
576
577 if (numerator == 0) {
578 return isAdd ? fraction : fraction.negate();
579 }
580 if (fraction.numerator == 0) {
581 return this;
582 }
583
584
585 int d1 = ArithmeticUtils.gcd(denominator, fraction.denominator);
586 if (d1==1) {
587
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
596
597
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
604
605 int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
606 int d2 = (tmodd1==0)?d1:ArithmeticUtils.gcd(tmodd1, d1);
607
608
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
621
622
623
624
625
626
627
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
636
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
646
647
648
649 @Override
650 public Fraction multiply(final int i) {
651 return multiply(new Fraction(i));
652 }
653
654
655
656
657
658
659
660
661
662
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
676
677
678
679 public Fraction divide(final int i) {
680 return divide(new Fraction(i));
681 }
682
683
684
685
686
687
688
689 public double percentageValue() {
690 return 100 * doubleValue();
691 }
692
693
694
695
696
697
698
699
700
701
702
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;
711 }
712
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
726 int gcd = ArithmeticUtils.gcd(numerator, denominator);
727 numerator /= gcd;
728 denominator /= gcd;
729 return new Fraction(numerator, denominator);
730 }
731
732
733
734
735
736
737
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
751 @Override
752 public FractionField getField() {
753 return FractionField.getInstance();
754 }
755
756 }