blob: f3452a2e2ed4b2d5c4a88f9bf17c939a77800a07 [file] [log] [blame]
Hans Boehm682ff5e2015-03-09 14:40:25 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Hans Boehm682ff5e2015-03-09 14:40:25 -070017package com.android.calculator2;
18
Annie Chin56bcbf12016-09-23 17:04:22 -070019import com.hp.creals.CR;
Hans Boehm682ff5e2015-03-09 14:40:25 -070020
21import java.math.BigInteger;
Annie Chin56bcbf12016-09-23 17:04:22 -070022import java.util.Random;
Hans Boehm682ff5e2015-03-09 14:40:25 -070023
Hans Boehmf599db72015-08-10 16:19:24 -070024/**
25 * Rational numbers that may turn to null if they get too big.
26 * For many operations, if the length of the nuumerator plus the length of the denominator exceeds
27 * a maximum size, we simply return null, and rely on our caller do something else.
28 * We currently never return null for a pure integer or for a BoundedRational that has just been
29 * constructed.
30 *
31 * We also implement a number of irrational functions. These return a non-null result only when
32 * the result is known to be rational.
33 */
Hans Boehm682ff5e2015-03-09 14:40:25 -070034public class BoundedRational {
Hans Boehmf599db72015-08-10 16:19:24 -070035 // TODO: Consider returning null for integers. With some care, large factorials might become
36 // much faster.
Hans Boehm682ff5e2015-03-09 14:40:25 -070037 // TODO: Maybe eventually make this extend Number?
Hans Boehmf599db72015-08-10 16:19:24 -070038
Hans Boehm995e5eb2016-02-08 11:03:01 -080039 private static final int MAX_SIZE = 10000; // total, in bits
Hans Boehm682ff5e2015-03-09 14:40:25 -070040
41 private final BigInteger mNum;
42 private final BigInteger mDen;
43
44 public BoundedRational(BigInteger n, BigInteger d) {
45 mNum = n;
46 mDen = d;
47 }
48
49 public BoundedRational(BigInteger n) {
50 mNum = n;
51 mDen = BigInteger.ONE;
52 }
53
54 public BoundedRational(long n, long d) {
55 mNum = BigInteger.valueOf(n);
56 mDen = BigInteger.valueOf(d);
57 }
58
59 public BoundedRational(long n) {
60 mNum = BigInteger.valueOf(n);
61 mDen = BigInteger.valueOf(1);
62 }
63
Hans Boehmf599db72015-08-10 16:19:24 -070064 /**
65 * Convert to String reflecting raw representation.
66 * Debug or log messages only, not pretty.
67 */
Hans Boehm75ca21c2015-03-11 18:43:24 -070068 public String toString() {
69 return mNum.toString() + "/" + mDen.toString();
70 }
71
Hans Boehmf599db72015-08-10 16:19:24 -070072 /**
73 * Convert to readable String.
Hans Boehm65a99a42016-02-03 18:16:07 -080074 * Intended for output to user. More expensive, less useful for debugging than
Hans Boehmf599db72015-08-10 16:19:24 -070075 * toString(). Not internationalized.
76 */
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070077 public String toNiceString() {
Hans Boehm65a99a42016-02-03 18:16:07 -080078 final BoundedRational nicer = reduce().positiveDen();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070079 String result = nicer.mNum.toString();
80 if (!nicer.mDen.equals(BigInteger.ONE)) {
81 result += "/" + nicer.mDen;
82 }
83 return result;
84 }
85
Hans Boehm682ff5e2015-03-09 14:40:25 -070086 public static String toString(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -070087 if (r == null) {
88 return "not a small rational";
89 }
Hans Boehm75ca21c2015-03-11 18:43:24 -070090 return r.toString();
Hans Boehm682ff5e2015-03-09 14:40:25 -070091 }
92
Hans Boehmf599db72015-08-10 16:19:24 -070093 /**
Hans Boehm65a99a42016-02-03 18:16:07 -080094 * Returns a truncated (rounded towards 0) representation of the result.
95 * Includes n digits to the right of the decimal point.
96 * @param n result precision, >= 0
97 */
Hans Boehm995e5eb2016-02-08 11:03:01 -080098 public String toStringTruncated(int n) {
Hans Boehm65a99a42016-02-03 18:16:07 -080099 String digits = mNum.abs().multiply(BigInteger.TEN.pow(n)).divide(mDen.abs()).toString();
100 int len = digits.length();
101 if (len < n + 1) {
Hans Boehm24c91ed2016-06-30 18:53:44 -0700102 digits = StringUtils.repeat('0', n + 1 - len) + digits;
Hans Boehm65a99a42016-02-03 18:16:07 -0800103 len = n + 1;
104 }
105 return (signum() < 0 ? "-" : "") + digits.substring(0, len - n) + "."
106 + digits.substring(len - n);
107 }
108
109 /**
Hans Boehmf599db72015-08-10 16:19:24 -0700110 * Return a double approximation.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800111 * The result is correctly rounded if numerator and denominator are
112 * exactly representable as a double.
113 * TODO: This should always be correctly rounded.
Hans Boehmf599db72015-08-10 16:19:24 -0700114 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700115 public double doubleValue() {
116 return mNum.doubleValue() / mDen.doubleValue();
117 }
118
Hans Boehm995e5eb2016-02-08 11:03:01 -0800119 public CR crValue() {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700120 return CR.valueOf(mNum).divide(CR.valueOf(mDen));
121 }
122
Hans Boehm995e5eb2016-02-08 11:03:01 -0800123 public int intValue() {
124 BoundedRational reduced = reduce();
125 if (!reduced.mDen.equals(BigInteger.ONE)) {
126 throw new ArithmeticException("intValue of non-int");
127 }
128 return reduced.mNum.intValue();
129 }
130
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700131 // Approximate number of bits to left of binary point.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800132 // Negative indicates leading zeroes to the right of binary point.
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700133 public int wholeNumberBits() {
Hans Boehm995e5eb2016-02-08 11:03:01 -0800134 if (mNum.signum() == 0) {
135 return Integer.MIN_VALUE;
136 } else {
137 return mNum.bitLength() - mDen.bitLength();
138 }
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700139 }
140
Hans Boehm682ff5e2015-03-09 14:40:25 -0700141 private boolean tooBig() {
Hans Boehmf599db72015-08-10 16:19:24 -0700142 if (mDen.equals(BigInteger.ONE)) {
143 return false;
144 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700145 return (mNum.bitLength() + mDen.bitLength() > MAX_SIZE);
146 }
147
Hans Boehmf599db72015-08-10 16:19:24 -0700148 /**
149 * Return an equivalent fraction with a positive denominator.
150 */
Hans Boehm9e855e82015-04-22 18:03:28 -0700151 private BoundedRational positiveDen() {
Hans Boehmf599db72015-08-10 16:19:24 -0700152 if (mDen.signum() > 0) {
153 return this;
154 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700155 return new BoundedRational(mNum.negate(), mDen.negate());
156 }
157
Hans Boehmf599db72015-08-10 16:19:24 -0700158 /**
159 * Return an equivalent fraction in lowest terms.
160 * Denominator sign may remain negative.
161 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700162 private BoundedRational reduce() {
Hans Boehmf599db72015-08-10 16:19:24 -0700163 if (mDen.equals(BigInteger.ONE)) {
164 return this; // Optimization only
165 }
166 final BigInteger divisor = mNum.gcd(mDen);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700167 return new BoundedRational(mNum.divide(divisor), mDen.divide(divisor));
168 }
169
Hans Boehm995e5eb2016-02-08 11:03:01 -0800170 static Random sReduceRng = new Random();
171
Hans Boehmf599db72015-08-10 16:19:24 -0700172 /**
Hans Boehm2ca183c2016-08-23 17:28:19 -0700173 * Return a possibly reduced version of r that's not tooBig().
Hans Boehmf599db72015-08-10 16:19:24 -0700174 * Return null if none exists.
175 */
Hans Boehm2ca183c2016-08-23 17:28:19 -0700176 private static BoundedRational maybeReduce(BoundedRational r) {
177 if (r == null) return null;
Hans Boehm995e5eb2016-02-08 11:03:01 -0800178 // Reduce randomly, with 1/16 probability, or if the result is too big.
Hans Boehm2ca183c2016-08-23 17:28:19 -0700179 if (!r.tooBig() && (sReduceRng.nextInt() & 0xf) != 0) {
180 return r;
Hans Boehmf599db72015-08-10 16:19:24 -0700181 }
Hans Boehm2ca183c2016-08-23 17:28:19 -0700182 BoundedRational result = r.positiveDen();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700183 result = result.reduce();
Hans Boehmf599db72015-08-10 16:19:24 -0700184 if (!result.tooBig()) {
Hans Boehm2ca183c2016-08-23 17:28:19 -0700185 return result;
Hans Boehmf599db72015-08-10 16:19:24 -0700186 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700187 return null;
188 }
189
190 public int compareTo(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700191 // Compare by multiplying both sides by denominators, invert result if denominator product
192 // was negative.
193 return mNum.multiply(r.mDen).compareTo(r.mNum.multiply(mDen)) * mDen.signum()
194 * r.mDen.signum();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700195 }
196
197 public int signum() {
Hans Boehm75ca21c2015-03-11 18:43:24 -0700198 return mNum.signum() * mDen.signum();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700199 }
200
201 public boolean equals(BoundedRational r) {
202 return compareTo(r) == 0;
203 }
204
Hans Boehmf599db72015-08-10 16:19:24 -0700205 // We use static methods for arithmetic, so that we can easily handle the null case. We try
206 // to catch domain errors whenever possible, sometimes even when one of the arguments is null,
207 // but not relevant.
Hans Boehm682ff5e2015-03-09 14:40:25 -0700208
Hans Boehmf599db72015-08-10 16:19:24 -0700209 /**
210 * Returns equivalent BigInteger result if it exists, null if not.
211 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700212 public static BigInteger asBigInteger(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700213 if (r == null) {
214 return null;
215 }
216 final BigInteger[] quotAndRem = r.mNum.divideAndRemainder(r.mDen);
217 if (quotAndRem[1].signum() == 0) {
218 return quotAndRem[0];
219 } else {
220 return null;
221 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700222 }
223 public static BoundedRational add(BoundedRational r1, BoundedRational r2) {
Hans Boehmf599db72015-08-10 16:19:24 -0700224 if (r1 == null || r2 == null) {
225 return null;
226 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700227 final BigInteger den = r1.mDen.multiply(r2.mDen);
Hans Boehmf599db72015-08-10 16:19:24 -0700228 final BigInteger num = r1.mNum.multiply(r2.mDen).add(r2.mNum.multiply(r1.mDen));
Hans Boehm2ca183c2016-08-23 17:28:19 -0700229 return maybeReduce(new BoundedRational(num,den));
Hans Boehm682ff5e2015-03-09 14:40:25 -0700230 }
231
Hans Boehm995e5eb2016-02-08 11:03:01 -0800232 /**
233 * Return the argument, but with the opposite sign.
234 * Returns null only for a null argument.
235 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700236 public static BoundedRational negate(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700237 if (r == null) {
238 return null;
239 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700240 return new BoundedRational(r.mNum.negate(), r.mDen);
241 }
242
Annie Chin56bcbf12016-09-23 17:04:22 -0700243 public static BoundedRational subtract(BoundedRational r1, BoundedRational r2) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700244 return add(r1, negate(r2));
245 }
246
Hans Boehm995e5eb2016-02-08 11:03:01 -0800247 /**
248 * Return product of r1 and r2 without reducing the result.
249 */
250 private static BoundedRational rawMultiply(BoundedRational r1, BoundedRational r2) {
Hans Boehmf599db72015-08-10 16:19:24 -0700251 // It's tempting but marginally unsound to reduce 0 * null to 0. The null could represent
252 // an infinite value, for which we failed to throw an exception because it was too big.
253 if (r1 == null || r2 == null) {
254 return null;
255 }
Hans Boehm995e5eb2016-02-08 11:03:01 -0800256 // Optimize the case of our special ONE constant, since that's cheap and somewhat frequent.
257 if (r1 == ONE) {
258 return r2;
259 }
260 if (r2 == ONE) {
261 return r1;
262 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700263 final BigInteger num = r1.mNum.multiply(r2.mNum);
264 final BigInteger den = r1.mDen.multiply(r2.mDen);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800265 return new BoundedRational(num,den);
266 }
267
Annie Chin56bcbf12016-09-23 17:04:22 -0700268 public static BoundedRational multiply(BoundedRational r1, BoundedRational r2) {
Hans Boehm2ca183c2016-08-23 17:28:19 -0700269 return maybeReduce(rawMultiply(r1, r2));
Hans Boehm682ff5e2015-03-09 14:40:25 -0700270 }
271
Hans Boehmfbcef702015-04-27 18:07:47 -0700272 public static class ZeroDivisionException extends ArithmeticException {
273 public ZeroDivisionException() {
274 super("Division by zero");
275 }
276 }
277
Hans Boehmf599db72015-08-10 16:19:24 -0700278 /**
Hans Boehm995e5eb2016-02-08 11:03:01 -0800279 * Return the reciprocal of r (or null if the argument was null).
Hans Boehmf599db72015-08-10 16:19:24 -0700280 */
Annie Chin56bcbf12016-09-23 17:04:22 -0700281 public static BoundedRational inverse(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700282 if (r == null) {
283 return null;
284 }
285 if (r.mNum.signum() == 0) {
Hans Boehmfbcef702015-04-27 18:07:47 -0700286 throw new ZeroDivisionException();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700287 }
288 return new BoundedRational(r.mDen, r.mNum);
289 }
290
Annie Chin56bcbf12016-09-23 17:04:22 -0700291 public static BoundedRational divide(BoundedRational r1, BoundedRational r2) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700292 return multiply(r1, inverse(r2));
293 }
294
Annie Chin56bcbf12016-09-23 17:04:22 -0700295 public static BoundedRational sqrt(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700296 // Return non-null if numerator and denominator are small perfect squares.
297 if (r == null) {
298 return null;
299 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700300 r = r.positiveDen().reduce();
Hans Boehmf599db72015-08-10 16:19:24 -0700301 if (r.mNum.signum() < 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700302 throw new ArithmeticException("sqrt(negative)");
303 }
Hans Boehmf599db72015-08-10 16:19:24 -0700304 final BigInteger num_sqrt = BigInteger.valueOf(Math.round(Math.sqrt(r.mNum.doubleValue())));
305 if (!num_sqrt.multiply(num_sqrt).equals(r.mNum)) {
306 return null;
307 }
308 final BigInteger den_sqrt = BigInteger.valueOf(Math.round(Math.sqrt(r.mDen.doubleValue())));
309 if (!den_sqrt.multiply(den_sqrt).equals(r.mDen)) {
310 return null;
311 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700312 return new BoundedRational(num_sqrt, den_sqrt);
313 }
314
315 public final static BoundedRational ZERO = new BoundedRational(0);
316 public final static BoundedRational HALF = new BoundedRational(1,2);
317 public final static BoundedRational MINUS_HALF = new BoundedRational(-1,2);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800318 public final static BoundedRational THIRD = new BoundedRational(1,3);
319 public final static BoundedRational QUARTER = new BoundedRational(1,4);
320 public final static BoundedRational SIXTH = new BoundedRational(1,6);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700321 public final static BoundedRational ONE = new BoundedRational(1);
322 public final static BoundedRational MINUS_ONE = new BoundedRational(-1);
323 public final static BoundedRational TWO = new BoundedRational(2);
324 public final static BoundedRational MINUS_TWO = new BoundedRational(-2);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800325 public final static BoundedRational TEN = new BoundedRational(10);
326 public final static BoundedRational TWELVE = new BoundedRational(12);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700327 public final static BoundedRational THIRTY = new BoundedRational(30);
328 public final static BoundedRational MINUS_THIRTY = new BoundedRational(-30);
329 public final static BoundedRational FORTY_FIVE = new BoundedRational(45);
Hans Boehmf599db72015-08-10 16:19:24 -0700330 public final static BoundedRational MINUS_FORTY_FIVE = new BoundedRational(-45);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700331 public final static BoundedRational NINETY = new BoundedRational(90);
332 public final static BoundedRational MINUS_NINETY = new BoundedRational(-90);
333
Hans Boehm682ff5e2015-03-09 14:40:25 -0700334 private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
335
Hans Boehmf599db72015-08-10 16:19:24 -0700336 /**
Hans Boehm995e5eb2016-02-08 11:03:01 -0800337 * Compute integral power of this, assuming this has been reduced and exp is >= 0.
338 */
339 private BoundedRational rawPow(BigInteger exp) {
340 if (exp.equals(BigInteger.ONE)) {
341 return this;
342 }
343 if (exp.and(BigInteger.ONE).intValue() == 1) {
344 return rawMultiply(rawPow(exp.subtract(BigInteger.ONE)), this);
345 }
346 if (exp.signum() == 0) {
347 return ONE;
348 }
349 BoundedRational tmp = rawPow(exp.shiftRight(1));
350 if (Thread.interrupted()) {
351 throw new CR.AbortedException();
352 }
353 return rawMultiply(tmp, tmp);
354 }
355
356 /**
Hans Boehmf599db72015-08-10 16:19:24 -0700357 * Compute an integral power of this.
358 */
Hans Boehm995e5eb2016-02-08 11:03:01 -0800359 public BoundedRational pow(BigInteger exp) {
Hans Boehmf599db72015-08-10 16:19:24 -0700360 if (exp.signum() < 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700361 return inverse(pow(exp.negate()));
362 }
Hans Boehmf599db72015-08-10 16:19:24 -0700363 if (exp.equals(BigInteger.ONE)) {
364 return this;
365 }
Hans Boehm995e5eb2016-02-08 11:03:01 -0800366 // Reducing once at the beginning means there's no point in reducing later.
367 return reduce().rawPow(exp);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700368 }
369
370 public static BoundedRational pow(BoundedRational base, BoundedRational exp) {
Hans Boehmf599db72015-08-10 16:19:24 -0700371 if (exp == null) {
372 return null;
373 }
374 if (exp.mNum.signum() == 0) {
375 // Questionable if base has undefined value. Java.lang.Math.pow() returns 1 anyway,
376 // so we do the same.
Hans Boehm682ff5e2015-03-09 14:40:25 -0700377 return new BoundedRational(1);
378 }
Hans Boehmf599db72015-08-10 16:19:24 -0700379 if (base == null) {
380 return null;
381 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700382 exp = exp.reduce().positiveDen();
Hans Boehmf599db72015-08-10 16:19:24 -0700383 if (!exp.mDen.equals(BigInteger.ONE)) {
384 return null;
385 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700386 return base.pow(exp.mNum);
387 }
388
Hans Boehm682ff5e2015-03-09 14:40:25 -0700389
390 private static final BigInteger BIG_FIVE = BigInteger.valueOf(5);
Hans Boehmcd740592015-06-13 21:12:23 -0700391 private static final BigInteger BIG_MINUS_ONE = BigInteger.valueOf(-1);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700392
Hans Boehmf599db72015-08-10 16:19:24 -0700393 /**
394 * Return the number of decimal digits to the right of the decimal point required to represent
395 * the argument exactly.
396 * Return Integer.MAX_VALUE if that's not possible. Never returns a value less than zero, even
397 * if r is a power of ten.
398 */
Annie Chin56bcbf12016-09-23 17:04:22 -0700399 public static int digitsRequired(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700400 if (r == null) {
401 return Integer.MAX_VALUE;
402 }
403 int powersOfTwo = 0; // Max power of 2 that divides denominator
404 int powersOfFive = 0; // Max power of 5 that divides denominator
Hans Boehm682ff5e2015-03-09 14:40:25 -0700405 // Try the easy case first to speed things up.
Hans Boehmf599db72015-08-10 16:19:24 -0700406 if (r.mDen.equals(BigInteger.ONE)) {
407 return 0;
408 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700409 r = r.reduce();
410 BigInteger den = r.mDen;
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700411 if (den.bitLength() > MAX_SIZE) {
412 return Integer.MAX_VALUE;
413 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700414 while (!den.testBit(0)) {
Hans Boehmf599db72015-08-10 16:19:24 -0700415 ++powersOfTwo;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700416 den = den.shiftRight(1);
417 }
Hans Boehmf599db72015-08-10 16:19:24 -0700418 while (den.mod(BIG_FIVE).signum() == 0) {
419 ++powersOfFive;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700420 den = den.divide(BIG_FIVE);
421 }
Hans Boehmf599db72015-08-10 16:19:24 -0700422 // If the denominator has a factor of other than 2 or 5 (the divisors of 10), the decimal
423 // expansion does not terminate. Multiplying the fraction by any number of powers of 10
424 // will not cancel the demoniator. (Recall the fraction was in lowest terms to start
425 // with.) Otherwise the powers of 10 we need to cancel the denominator is the larger of
426 // powersOfTwo and powersOfFive.
Hans Boehmcd740592015-06-13 21:12:23 -0700427 if (!den.equals(BigInteger.ONE) && !den.equals(BIG_MINUS_ONE)) {
428 return Integer.MAX_VALUE;
429 }
Hans Boehmf599db72015-08-10 16:19:24 -0700430 return Math.max(powersOfTwo, powersOfFive);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700431 }
432}