blob: 16fa581775c40bc5555564d9813c7da3d044c750 [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;
Hans Boehmf74f2b52017-08-23 11:04:28 -070022import java.util.Objects;
Annie Chin56bcbf12016-09-23 17:04:22 -070023import java.util.Random;
Hans Boehm682ff5e2015-03-09 14:40:25 -070024
Hans Boehmf599db72015-08-10 16:19:24 -070025/**
26 * Rational numbers that may turn to null if they get too big.
27 * For many operations, if the length of the nuumerator plus the length of the denominator exceeds
28 * a maximum size, we simply return null, and rely on our caller do something else.
29 * We currently never return null for a pure integer or for a BoundedRational that has just been
30 * constructed.
31 *
32 * We also implement a number of irrational functions. These return a non-null result only when
33 * the result is known to be rational.
34 */
Hans Boehm682ff5e2015-03-09 14:40:25 -070035public class BoundedRational {
Hans Boehmf599db72015-08-10 16:19:24 -070036 // TODO: Consider returning null for integers. With some care, large factorials might become
37 // much faster.
Hans Boehm682ff5e2015-03-09 14:40:25 -070038 // TODO: Maybe eventually make this extend Number?
Hans Boehmf599db72015-08-10 16:19:24 -070039
Hans Boehm995e5eb2016-02-08 11:03:01 -080040 private static final int MAX_SIZE = 10000; // total, in bits
Hans Boehm682ff5e2015-03-09 14:40:25 -070041
42 private final BigInteger mNum;
43 private final BigInteger mDen;
44
45 public BoundedRational(BigInteger n, BigInteger d) {
46 mNum = n;
47 mDen = d;
48 }
49
50 public BoundedRational(BigInteger n) {
51 mNum = n;
52 mDen = BigInteger.ONE;
53 }
54
55 public BoundedRational(long n, long d) {
56 mNum = BigInteger.valueOf(n);
57 mDen = BigInteger.valueOf(d);
58 }
59
60 public BoundedRational(long n) {
61 mNum = BigInteger.valueOf(n);
62 mDen = BigInteger.valueOf(1);
63 }
64
Hans Boehmf599db72015-08-10 16:19:24 -070065 /**
Hans Boehmf74f2b52017-08-23 11:04:28 -070066 * Produce BoundedRational equal to the given double.
67 */
68 public static BoundedRational valueOf(double x) {
69 final long l = Math.round(x);
70 if ((double) l == x && Math.abs(l) <= 1000) {
71 return valueOf(l);
72 }
73 final long allBits = Double.doubleToRawLongBits(Math.abs(x));
74 long mantissa = (allBits & ((1L << 52) - 1));
75 final int biased_exp = (int)(allBits >>> 52);
76 if ((biased_exp & 0x7ff) == 0x7ff) {
77 throw new ArithmeticException("Infinity or NaN not convertible to BoundedRational");
78 }
79 final long sign = x < 0.0 ? -1 : 1;
80 int exp = biased_exp - 1075; // 1023 + 52; we treat mantissa as integer.
81 if (biased_exp == 0) {
82 exp += 1; // Denormal exponent is 1 greater.
83 } else {
84 mantissa += (1L << 52); // Implied leading one.
85 }
86 BigInteger num = BigInteger.valueOf(sign * mantissa);
87 BigInteger den = BigInteger.ONE;
88 if (exp >= 0) {
89 num = num.shiftLeft(exp);
90 } else {
91 den = den.shiftLeft(-exp);
92 }
93 return new BoundedRational(num, den);
94 }
95
96 /**
97 * Produce BoundedRational equal to the given long.
98 */
99 public static BoundedRational valueOf(long x) {
100 if (x >= -2 && x <= 10) {
101 switch((int) x) {
102 case -2:
103 return MINUS_TWO;
104 case -1:
105 return MINUS_ONE;
106 case 0:
107 return ZERO;
108 case 1:
109 return ONE;
110 case 2:
111 return TWO;
112 case 10:
113 return TEN;
114 }
115 }
116 return new BoundedRational(x);
117 }
118
119 /**
Hans Boehmf599db72015-08-10 16:19:24 -0700120 * Convert to String reflecting raw representation.
121 * Debug or log messages only, not pretty.
122 */
Hans Boehm75ca21c2015-03-11 18:43:24 -0700123 public String toString() {
124 return mNum.toString() + "/" + mDen.toString();
125 }
126
Hans Boehmf599db72015-08-10 16:19:24 -0700127 /**
128 * Convert to readable String.
Hans Boehm65a99a42016-02-03 18:16:07 -0800129 * Intended for output to user. More expensive, less useful for debugging than
Hans Boehmf599db72015-08-10 16:19:24 -0700130 * toString(). Not internationalized.
131 */
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700132 public String toNiceString() {
Hans Boehm65a99a42016-02-03 18:16:07 -0800133 final BoundedRational nicer = reduce().positiveDen();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700134 String result = nicer.mNum.toString();
135 if (!nicer.mDen.equals(BigInteger.ONE)) {
136 result += "/" + nicer.mDen;
137 }
138 return result;
139 }
140
Hans Boehm682ff5e2015-03-09 14:40:25 -0700141 public static String toString(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700142 if (r == null) {
143 return "not a small rational";
144 }
Hans Boehm75ca21c2015-03-11 18:43:24 -0700145 return r.toString();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700146 }
147
Hans Boehmf599db72015-08-10 16:19:24 -0700148 /**
Hans Boehm65a99a42016-02-03 18:16:07 -0800149 * Returns a truncated (rounded towards 0) representation of the result.
150 * Includes n digits to the right of the decimal point.
151 * @param n result precision, >= 0
152 */
Hans Boehm995e5eb2016-02-08 11:03:01 -0800153 public String toStringTruncated(int n) {
Hans Boehm65a99a42016-02-03 18:16:07 -0800154 String digits = mNum.abs().multiply(BigInteger.TEN.pow(n)).divide(mDen.abs()).toString();
155 int len = digits.length();
156 if (len < n + 1) {
Hans Boehm24c91ed2016-06-30 18:53:44 -0700157 digits = StringUtils.repeat('0', n + 1 - len) + digits;
Hans Boehm65a99a42016-02-03 18:16:07 -0800158 len = n + 1;
159 }
160 return (signum() < 0 ? "-" : "") + digits.substring(0, len - n) + "."
161 + digits.substring(len - n);
162 }
163
164 /**
Hans Boehmf599db72015-08-10 16:19:24 -0700165 * Return a double approximation.
Hans Boehmf74f2b52017-08-23 11:04:28 -0700166 * The result is correctly rounded to nearest, with ties rounded away from zero.
167 * TODO: Should round ties to even.
Hans Boehmf599db72015-08-10 16:19:24 -0700168 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700169 public double doubleValue() {
Hans Boehmf74f2b52017-08-23 11:04:28 -0700170 final int sign = signum();
171 if (sign < 0) {
172 return -BoundedRational.negate(this).doubleValue();
173 }
174 // We get the mantissa by dividing the numerator by denominator, after
175 // suitably prescaling them so that the integral part of the result contains
176 // enough bits. We do the prescaling to avoid any precision loss, so the division result
177 // is correctly truncated towards zero.
178 final int apprExp = mNum.bitLength() - mDen.bitLength();
179 if (apprExp < -1100 || sign == 0) {
180 // Bail fast for clearly zero result.
181 return 0.0;
182 }
183 final int neededPrec = apprExp - 80;
184 final BigInteger dividend = neededPrec < 0 ? mNum.shiftLeft(-neededPrec) : mNum;
185 final BigInteger divisor = neededPrec > 0 ? mDen.shiftLeft(neededPrec) : mDen;
186 final BigInteger quotient = dividend.divide(divisor);
187 final int qLength = quotient.bitLength();
188 int extraBits = qLength - 53;
189 int exponent = neededPrec + qLength; // Exponent assuming leading binary point.
190 if (exponent >= -1021) {
191 // Binary point is actually to right of leading bit.
192 --exponent;
193 } else {
194 // We're in the gradual underflow range. Drop more bits.
195 extraBits += (-1022 - exponent) + 1;
196 exponent = -1023;
197 }
198 final BigInteger bigMantissa =
199 quotient.add(BigInteger.ONE.shiftLeft(extraBits - 1)).shiftRight(extraBits);
200 if (exponent > 1024) {
201 return Double.POSITIVE_INFINITY;
202 }
203 if (exponent > -1023 && bigMantissa.bitLength() != 53
204 || exponent <= -1023 && bigMantissa.bitLength() >= 53) {
205 throw new AssertionError("doubleValue internal error");
206 }
207 final long mantissa = bigMantissa.longValue();
208 final long bits = (mantissa & ((1l << 52) - 1)) | (((long) exponent + 1023) << 52);
209 return Double.longBitsToDouble(bits);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700210 }
211
Hans Boehm995e5eb2016-02-08 11:03:01 -0800212 public CR crValue() {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700213 return CR.valueOf(mNum).divide(CR.valueOf(mDen));
214 }
215
Hans Boehm995e5eb2016-02-08 11:03:01 -0800216 public int intValue() {
217 BoundedRational reduced = reduce();
218 if (!reduced.mDen.equals(BigInteger.ONE)) {
219 throw new ArithmeticException("intValue of non-int");
220 }
221 return reduced.mNum.intValue();
222 }
223
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700224 // Approximate number of bits to left of binary point.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800225 // Negative indicates leading zeroes to the right of binary point.
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700226 public int wholeNumberBits() {
Hans Boehm995e5eb2016-02-08 11:03:01 -0800227 if (mNum.signum() == 0) {
228 return Integer.MIN_VALUE;
229 } else {
230 return mNum.bitLength() - mDen.bitLength();
231 }
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700232 }
233
Hans Boehmf74f2b52017-08-23 11:04:28 -0700234 /**
235 * Is this number too big for us to continue with rational arithmetic?
236 * We return fals for integers on the assumption that we have no better fallback.
237 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700238 private boolean tooBig() {
Hans Boehmf599db72015-08-10 16:19:24 -0700239 if (mDen.equals(BigInteger.ONE)) {
240 return false;
241 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700242 return (mNum.bitLength() + mDen.bitLength() > MAX_SIZE);
243 }
244
Hans Boehmf599db72015-08-10 16:19:24 -0700245 /**
246 * Return an equivalent fraction with a positive denominator.
247 */
Hans Boehm9e855e82015-04-22 18:03:28 -0700248 private BoundedRational positiveDen() {
Hans Boehmf599db72015-08-10 16:19:24 -0700249 if (mDen.signum() > 0) {
250 return this;
251 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700252 return new BoundedRational(mNum.negate(), mDen.negate());
253 }
254
Hans Boehmf599db72015-08-10 16:19:24 -0700255 /**
256 * Return an equivalent fraction in lowest terms.
257 * Denominator sign may remain negative.
258 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700259 private BoundedRational reduce() {
Hans Boehmf599db72015-08-10 16:19:24 -0700260 if (mDen.equals(BigInteger.ONE)) {
261 return this; // Optimization only
262 }
263 final BigInteger divisor = mNum.gcd(mDen);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700264 return new BoundedRational(mNum.divide(divisor), mDen.divide(divisor));
265 }
266
Hans Boehm995e5eb2016-02-08 11:03:01 -0800267 static Random sReduceRng = new Random();
268
Hans Boehmf599db72015-08-10 16:19:24 -0700269 /**
Hans Boehm2ca183c2016-08-23 17:28:19 -0700270 * Return a possibly reduced version of r that's not tooBig().
Hans Boehmf599db72015-08-10 16:19:24 -0700271 * Return null if none exists.
272 */
Hans Boehm2ca183c2016-08-23 17:28:19 -0700273 private static BoundedRational maybeReduce(BoundedRational r) {
274 if (r == null) return null;
Hans Boehm995e5eb2016-02-08 11:03:01 -0800275 // Reduce randomly, with 1/16 probability, or if the result is too big.
Hans Boehm2ca183c2016-08-23 17:28:19 -0700276 if (!r.tooBig() && (sReduceRng.nextInt() & 0xf) != 0) {
277 return r;
Hans Boehmf599db72015-08-10 16:19:24 -0700278 }
Hans Boehm2ca183c2016-08-23 17:28:19 -0700279 BoundedRational result = r.positiveDen();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700280 result = result.reduce();
Hans Boehmf599db72015-08-10 16:19:24 -0700281 if (!result.tooBig()) {
Hans Boehm2ca183c2016-08-23 17:28:19 -0700282 return result;
Hans Boehmf599db72015-08-10 16:19:24 -0700283 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700284 return null;
285 }
286
287 public int compareTo(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700288 // Compare by multiplying both sides by denominators, invert result if denominator product
289 // was negative.
290 return mNum.multiply(r.mDen).compareTo(r.mNum.multiply(mDen)) * mDen.signum()
291 * r.mDen.signum();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700292 }
293
294 public int signum() {
Hans Boehm75ca21c2015-03-11 18:43:24 -0700295 return mNum.signum() * mDen.signum();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700296 }
297
Hans Boehmf74f2b52017-08-23 11:04:28 -0700298 @Override
299 public int hashCode() {
300 // Note that this may be too expensive to be useful.
301 BoundedRational reduced = reduce().positiveDen();
302 return Objects.hash(reduced.mNum, reduced.mDen);
303 }
304
305 @Override
306 public boolean equals(Object r) {
307 return r != null && r instanceof BoundedRational && compareTo((BoundedRational) r) == 0;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700308 }
309
Hans Boehmf599db72015-08-10 16:19:24 -0700310 // We use static methods for arithmetic, so that we can easily handle the null case. We try
311 // to catch domain errors whenever possible, sometimes even when one of the arguments is null,
312 // but not relevant.
Hans Boehm682ff5e2015-03-09 14:40:25 -0700313
Hans Boehmf599db72015-08-10 16:19:24 -0700314 /**
315 * Returns equivalent BigInteger result if it exists, null if not.
316 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700317 public static BigInteger asBigInteger(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700318 if (r == null) {
319 return null;
320 }
321 final BigInteger[] quotAndRem = r.mNum.divideAndRemainder(r.mDen);
322 if (quotAndRem[1].signum() == 0) {
323 return quotAndRem[0];
324 } else {
325 return null;
326 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700327 }
328 public static BoundedRational add(BoundedRational r1, BoundedRational r2) {
Hans Boehmf599db72015-08-10 16:19:24 -0700329 if (r1 == null || r2 == null) {
330 return null;
331 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700332 final BigInteger den = r1.mDen.multiply(r2.mDen);
Hans Boehmf599db72015-08-10 16:19:24 -0700333 final BigInteger num = r1.mNum.multiply(r2.mDen).add(r2.mNum.multiply(r1.mDen));
Hans Boehm2ca183c2016-08-23 17:28:19 -0700334 return maybeReduce(new BoundedRational(num,den));
Hans Boehm682ff5e2015-03-09 14:40:25 -0700335 }
336
Hans Boehm995e5eb2016-02-08 11:03:01 -0800337 /**
338 * Return the argument, but with the opposite sign.
339 * Returns null only for a null argument.
340 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700341 public static BoundedRational negate(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700342 if (r == null) {
343 return null;
344 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700345 return new BoundedRational(r.mNum.negate(), r.mDen);
346 }
347
Annie Chin56bcbf12016-09-23 17:04:22 -0700348 public static BoundedRational subtract(BoundedRational r1, BoundedRational r2) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700349 return add(r1, negate(r2));
350 }
351
Hans Boehm995e5eb2016-02-08 11:03:01 -0800352 /**
353 * Return product of r1 and r2 without reducing the result.
354 */
355 private static BoundedRational rawMultiply(BoundedRational r1, BoundedRational r2) {
Hans Boehmf599db72015-08-10 16:19:24 -0700356 // It's tempting but marginally unsound to reduce 0 * null to 0. The null could represent
357 // an infinite value, for which we failed to throw an exception because it was too big.
358 if (r1 == null || r2 == null) {
359 return null;
360 }
Hans Boehm995e5eb2016-02-08 11:03:01 -0800361 // Optimize the case of our special ONE constant, since that's cheap and somewhat frequent.
362 if (r1 == ONE) {
363 return r2;
364 }
365 if (r2 == ONE) {
366 return r1;
367 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700368 final BigInteger num = r1.mNum.multiply(r2.mNum);
369 final BigInteger den = r1.mDen.multiply(r2.mDen);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800370 return new BoundedRational(num,den);
371 }
372
Annie Chin56bcbf12016-09-23 17:04:22 -0700373 public static BoundedRational multiply(BoundedRational r1, BoundedRational r2) {
Hans Boehm2ca183c2016-08-23 17:28:19 -0700374 return maybeReduce(rawMultiply(r1, r2));
Hans Boehm682ff5e2015-03-09 14:40:25 -0700375 }
376
Hans Boehmfbcef702015-04-27 18:07:47 -0700377 public static class ZeroDivisionException extends ArithmeticException {
378 public ZeroDivisionException() {
379 super("Division by zero");
380 }
381 }
382
Hans Boehmf599db72015-08-10 16:19:24 -0700383 /**
Hans Boehm995e5eb2016-02-08 11:03:01 -0800384 * Return the reciprocal of r (or null if the argument was null).
Hans Boehmf599db72015-08-10 16:19:24 -0700385 */
Annie Chin56bcbf12016-09-23 17:04:22 -0700386 public static BoundedRational inverse(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700387 if (r == null) {
388 return null;
389 }
390 if (r.mNum.signum() == 0) {
Hans Boehmfbcef702015-04-27 18:07:47 -0700391 throw new ZeroDivisionException();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700392 }
393 return new BoundedRational(r.mDen, r.mNum);
394 }
395
Annie Chin56bcbf12016-09-23 17:04:22 -0700396 public static BoundedRational divide(BoundedRational r1, BoundedRational r2) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700397 return multiply(r1, inverse(r2));
398 }
399
Annie Chin56bcbf12016-09-23 17:04:22 -0700400 public static BoundedRational sqrt(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700401 // Return non-null if numerator and denominator are small perfect squares.
402 if (r == null) {
403 return null;
404 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700405 r = r.positiveDen().reduce();
Hans Boehmf599db72015-08-10 16:19:24 -0700406 if (r.mNum.signum() < 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700407 throw new ArithmeticException("sqrt(negative)");
408 }
Hans Boehmf599db72015-08-10 16:19:24 -0700409 final BigInteger num_sqrt = BigInteger.valueOf(Math.round(Math.sqrt(r.mNum.doubleValue())));
410 if (!num_sqrt.multiply(num_sqrt).equals(r.mNum)) {
411 return null;
412 }
413 final BigInteger den_sqrt = BigInteger.valueOf(Math.round(Math.sqrt(r.mDen.doubleValue())));
414 if (!den_sqrt.multiply(den_sqrt).equals(r.mDen)) {
415 return null;
416 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700417 return new BoundedRational(num_sqrt, den_sqrt);
418 }
419
420 public final static BoundedRational ZERO = new BoundedRational(0);
421 public final static BoundedRational HALF = new BoundedRational(1,2);
422 public final static BoundedRational MINUS_HALF = new BoundedRational(-1,2);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800423 public final static BoundedRational THIRD = new BoundedRational(1,3);
424 public final static BoundedRational QUARTER = new BoundedRational(1,4);
425 public final static BoundedRational SIXTH = new BoundedRational(1,6);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700426 public final static BoundedRational ONE = new BoundedRational(1);
427 public final static BoundedRational MINUS_ONE = new BoundedRational(-1);
428 public final static BoundedRational TWO = new BoundedRational(2);
429 public final static BoundedRational MINUS_TWO = new BoundedRational(-2);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800430 public final static BoundedRational TEN = new BoundedRational(10);
431 public final static BoundedRational TWELVE = new BoundedRational(12);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700432 public final static BoundedRational THIRTY = new BoundedRational(30);
433 public final static BoundedRational MINUS_THIRTY = new BoundedRational(-30);
434 public final static BoundedRational FORTY_FIVE = new BoundedRational(45);
Hans Boehmf599db72015-08-10 16:19:24 -0700435 public final static BoundedRational MINUS_FORTY_FIVE = new BoundedRational(-45);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700436 public final static BoundedRational NINETY = new BoundedRational(90);
437 public final static BoundedRational MINUS_NINETY = new BoundedRational(-90);
438
Hans Boehm682ff5e2015-03-09 14:40:25 -0700439 private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
Hans Boehmf74f2b52017-08-23 11:04:28 -0700440 private static final BigInteger BIG_MINUS_ONE = BigInteger.valueOf(-1);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700441
Hans Boehmf599db72015-08-10 16:19:24 -0700442 /**
Hans Boehm995e5eb2016-02-08 11:03:01 -0800443 * Compute integral power of this, assuming this has been reduced and exp is >= 0.
444 */
445 private BoundedRational rawPow(BigInteger exp) {
446 if (exp.equals(BigInteger.ONE)) {
447 return this;
448 }
449 if (exp.and(BigInteger.ONE).intValue() == 1) {
450 return rawMultiply(rawPow(exp.subtract(BigInteger.ONE)), this);
451 }
452 if (exp.signum() == 0) {
453 return ONE;
454 }
455 BoundedRational tmp = rawPow(exp.shiftRight(1));
456 if (Thread.interrupted()) {
457 throw new CR.AbortedException();
458 }
Hans Boehmf74f2b52017-08-23 11:04:28 -0700459 BoundedRational result = rawMultiply(tmp, tmp);
460 if (result == null || result.tooBig()) {
461 return null;
462 }
463 return result;
Hans Boehm995e5eb2016-02-08 11:03:01 -0800464 }
465
466 /**
Hans Boehmf599db72015-08-10 16:19:24 -0700467 * Compute an integral power of this.
468 */
Hans Boehm995e5eb2016-02-08 11:03:01 -0800469 public BoundedRational pow(BigInteger exp) {
Hans Boehmf74f2b52017-08-23 11:04:28 -0700470 int expSign = exp.signum();
471 if (expSign == 0) {
472 // Questionable if base has undefined or zero value.
473 // java.lang.Math.pow() returns 1 anyway, so we do the same.
474 return BoundedRational.ONE;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700475 }
Hans Boehmf599db72015-08-10 16:19:24 -0700476 if (exp.equals(BigInteger.ONE)) {
477 return this;
478 }
Hans Boehm995e5eb2016-02-08 11:03:01 -0800479 // Reducing once at the beginning means there's no point in reducing later.
Hans Boehmf74f2b52017-08-23 11:04:28 -0700480 BoundedRational reduced = reduce().positiveDen();
481 // First handle cases in which huge exponents could give compact results.
482 if (reduced.mDen.equals(BigInteger.ONE)) {
483 if (reduced.mNum.equals(BigInteger.ZERO)) {
484 return ZERO;
485 }
486 if (reduced.mNum.equals(BigInteger.ONE)) {
487 return ONE;
488 }
489 if (reduced.mNum.equals(BIG_MINUS_ONE)) {
490 if (exp.testBit(0)) {
491 return MINUS_ONE;
492 } else {
493 return ONE;
494 }
495 }
496 }
497 if (exp.bitLength() > 1000) {
498 // Stack overflow is likely; a useful rational result is not.
499 return null;
500 }
501 if (expSign < 0) {
502 return inverse(reduced).rawPow(exp.negate());
503 } else {
504 return reduced.rawPow(exp);
505 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700506 }
507
508 public static BoundedRational pow(BoundedRational base, BoundedRational exp) {
Hans Boehmf599db72015-08-10 16:19:24 -0700509 if (exp == null) {
510 return null;
511 }
Hans Boehmf599db72015-08-10 16:19:24 -0700512 if (base == null) {
513 return null;
514 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700515 exp = exp.reduce().positiveDen();
Hans Boehmf599db72015-08-10 16:19:24 -0700516 if (!exp.mDen.equals(BigInteger.ONE)) {
517 return null;
518 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700519 return base.pow(exp.mNum);
520 }
521
Hans Boehm682ff5e2015-03-09 14:40:25 -0700522
523 private static final BigInteger BIG_FIVE = BigInteger.valueOf(5);
524
Hans Boehmf599db72015-08-10 16:19:24 -0700525 /**
526 * Return the number of decimal digits to the right of the decimal point required to represent
527 * the argument exactly.
528 * Return Integer.MAX_VALUE if that's not possible. Never returns a value less than zero, even
529 * if r is a power of ten.
530 */
Annie Chin56bcbf12016-09-23 17:04:22 -0700531 public static int digitsRequired(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700532 if (r == null) {
533 return Integer.MAX_VALUE;
534 }
535 int powersOfTwo = 0; // Max power of 2 that divides denominator
536 int powersOfFive = 0; // Max power of 5 that divides denominator
Hans Boehm682ff5e2015-03-09 14:40:25 -0700537 // Try the easy case first to speed things up.
Hans Boehmf599db72015-08-10 16:19:24 -0700538 if (r.mDen.equals(BigInteger.ONE)) {
539 return 0;
540 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700541 r = r.reduce();
542 BigInteger den = r.mDen;
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700543 if (den.bitLength() > MAX_SIZE) {
544 return Integer.MAX_VALUE;
545 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700546 while (!den.testBit(0)) {
Hans Boehmf599db72015-08-10 16:19:24 -0700547 ++powersOfTwo;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700548 den = den.shiftRight(1);
549 }
Hans Boehmf599db72015-08-10 16:19:24 -0700550 while (den.mod(BIG_FIVE).signum() == 0) {
551 ++powersOfFive;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700552 den = den.divide(BIG_FIVE);
553 }
Hans Boehmf599db72015-08-10 16:19:24 -0700554 // If the denominator has a factor of other than 2 or 5 (the divisors of 10), the decimal
555 // expansion does not terminate. Multiplying the fraction by any number of powers of 10
556 // will not cancel the demoniator. (Recall the fraction was in lowest terms to start
557 // with.) Otherwise the powers of 10 we need to cancel the denominator is the larger of
558 // powersOfTwo and powersOfFive.
Hans Boehmcd740592015-06-13 21:12:23 -0700559 if (!den.equals(BigInteger.ONE) && !den.equals(BIG_MINUS_ONE)) {
560 return Integer.MAX_VALUE;
561 }
Hans Boehmf599db72015-08-10 16:19:24 -0700562 return Math.max(powersOfTwo, powersOfFive);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700563 }
564}