blob: 383e59dee67e05ff4d8bfeca049378b330c83658 [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
Hans Boehm682ff5e2015-03-09 14:40:25 -070019
20import java.math.BigInteger;
21import com.hp.creals.CR;
22
Hans Boehmf599db72015-08-10 16:19:24 -070023/**
24 * Rational numbers that may turn to null if they get too big.
25 * For many operations, if the length of the nuumerator plus the length of the denominator exceeds
26 * a maximum size, we simply return null, and rely on our caller do something else.
27 * We currently never return null for a pure integer or for a BoundedRational that has just been
28 * constructed.
29 *
30 * We also implement a number of irrational functions. These return a non-null result only when
31 * the result is known to be rational.
32 */
Hans Boehm682ff5e2015-03-09 14:40:25 -070033public class BoundedRational {
Hans Boehmf599db72015-08-10 16:19:24 -070034 // TODO: Consider returning null for integers. With some care, large factorials might become
35 // much faster.
Hans Boehm682ff5e2015-03-09 14:40:25 -070036 // TODO: Maybe eventually make this extend Number?
Hans Boehmf599db72015-08-10 16:19:24 -070037
Hans Boehm65a99a42016-02-03 18:16:07 -080038 private static final int MAX_SIZE = 2000; // total, in bits
Hans Boehm682ff5e2015-03-09 14:40:25 -070039
40 private final BigInteger mNum;
41 private final BigInteger mDen;
42
43 public BoundedRational(BigInteger n, BigInteger d) {
44 mNum = n;
45 mDen = d;
46 }
47
48 public BoundedRational(BigInteger n) {
49 mNum = n;
50 mDen = BigInteger.ONE;
51 }
52
53 public BoundedRational(long n, long d) {
54 mNum = BigInteger.valueOf(n);
55 mDen = BigInteger.valueOf(d);
56 }
57
58 public BoundedRational(long n) {
59 mNum = BigInteger.valueOf(n);
60 mDen = BigInteger.valueOf(1);
61 }
62
Hans Boehmf599db72015-08-10 16:19:24 -070063 /**
64 * Convert to String reflecting raw representation.
65 * Debug or log messages only, not pretty.
66 */
Hans Boehm75ca21c2015-03-11 18:43:24 -070067 public String toString() {
68 return mNum.toString() + "/" + mDen.toString();
69 }
70
Hans Boehmf599db72015-08-10 16:19:24 -070071 /**
72 * Convert to readable String.
Hans Boehm65a99a42016-02-03 18:16:07 -080073 * Intended for output to user. More expensive, less useful for debugging than
Hans Boehmf599db72015-08-10 16:19:24 -070074 * toString(). Not internationalized.
75 */
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070076 public String toNiceString() {
Hans Boehm65a99a42016-02-03 18:16:07 -080077 final BoundedRational nicer = reduce().positiveDen();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070078 String result = nicer.mNum.toString();
79 if (!nicer.mDen.equals(BigInteger.ONE)) {
80 result += "/" + nicer.mDen;
81 }
82 return result;
83 }
84
Hans Boehm682ff5e2015-03-09 14:40:25 -070085 public static String toString(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -070086 if (r == null) {
87 return "not a small rational";
88 }
Hans Boehm75ca21c2015-03-11 18:43:24 -070089 return r.toString();
Hans Boehm682ff5e2015-03-09 14:40:25 -070090 }
91
Hans Boehmf599db72015-08-10 16:19:24 -070092 /**
Hans Boehm65a99a42016-02-03 18:16:07 -080093 * Return a string with n copies of c.
94 */
95 private static String repeat(char c, int n) {
96 final StringBuilder result = new StringBuilder();
97 for (int i = 0; i < n; ++i) {
98 result.append(c);
99 }
100 return result.toString();
101 }
102
103 /*
104 * Returns a truncated (rounded towards 0) representation of the result.
105 * Includes n digits to the right of the decimal point.
106 * @param n result precision, >= 0
107 */
108 public String toString(int n) {
109 String digits = mNum.abs().multiply(BigInteger.TEN.pow(n)).divide(mDen.abs()).toString();
110 int len = digits.length();
111 if (len < n + 1) {
112 digits = repeat('0', n + 1 - len) + digits;
113 len = n + 1;
114 }
115 return (signum() < 0 ? "-" : "") + digits.substring(0, len - n) + "."
116 + digits.substring(len - n);
117 }
118
119 /**
Hans Boehmf599db72015-08-10 16:19:24 -0700120 * Return a double approximation.
121 * Primarily for debugging.
122 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700123 public double doubleValue() {
124 return mNum.doubleValue() / mDen.doubleValue();
125 }
126
127 public CR CRValue() {
128 return CR.valueOf(mNum).divide(CR.valueOf(mDen));
129 }
130
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700131 // Approximate number of bits to left of binary point.
132 public int wholeNumberBits() {
133 return mNum.bitLength() - mDen.bitLength();
134 }
135
Hans Boehm682ff5e2015-03-09 14:40:25 -0700136 private boolean tooBig() {
Hans Boehmf599db72015-08-10 16:19:24 -0700137 if (mDen.equals(BigInteger.ONE)) {
138 return false;
139 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700140 return (mNum.bitLength() + mDen.bitLength() > MAX_SIZE);
141 }
142
Hans Boehmf599db72015-08-10 16:19:24 -0700143 /**
144 * Return an equivalent fraction with a positive denominator.
145 */
Hans Boehm9e855e82015-04-22 18:03:28 -0700146 private BoundedRational positiveDen() {
Hans Boehmf599db72015-08-10 16:19:24 -0700147 if (mDen.signum() > 0) {
148 return this;
149 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700150 return new BoundedRational(mNum.negate(), mDen.negate());
151 }
152
Hans Boehmf599db72015-08-10 16:19:24 -0700153 /**
154 * Return an equivalent fraction in lowest terms.
155 * Denominator sign may remain negative.
156 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700157 private BoundedRational reduce() {
Hans Boehmf599db72015-08-10 16:19:24 -0700158 if (mDen.equals(BigInteger.ONE)) {
159 return this; // Optimization only
160 }
161 final BigInteger divisor = mNum.gcd(mDen);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700162 return new BoundedRational(mNum.divide(divisor), mDen.divide(divisor));
163 }
164
Hans Boehmf599db72015-08-10 16:19:24 -0700165 /**
166 * Return a possibly reduced version of this that's not tooBig().
167 * Return null if none exists.
168 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700169 private BoundedRational maybeReduce() {
Hans Boehmf599db72015-08-10 16:19:24 -0700170 if (!tooBig()) {
171 return this;
172 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700173 BoundedRational result = positiveDen();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700174 result = result.reduce();
Hans Boehmf599db72015-08-10 16:19:24 -0700175 if (!result.tooBig()) {
176 return this;
177 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700178 return null;
179 }
180
181 public int compareTo(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700182 // Compare by multiplying both sides by denominators, invert result if denominator product
183 // was negative.
184 return mNum.multiply(r.mDen).compareTo(r.mNum.multiply(mDen)) * mDen.signum()
185 * r.mDen.signum();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700186 }
187
188 public int signum() {
Hans Boehm75ca21c2015-03-11 18:43:24 -0700189 return mNum.signum() * mDen.signum();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700190 }
191
192 public boolean equals(BoundedRational r) {
193 return compareTo(r) == 0;
194 }
195
Hans Boehmf599db72015-08-10 16:19:24 -0700196 // We use static methods for arithmetic, so that we can easily handle the null case. We try
197 // to catch domain errors whenever possible, sometimes even when one of the arguments is null,
198 // but not relevant.
Hans Boehm682ff5e2015-03-09 14:40:25 -0700199
Hans Boehmf599db72015-08-10 16:19:24 -0700200 /**
201 * Returns equivalent BigInteger result if it exists, null if not.
202 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700203 public static BigInteger asBigInteger(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700204 if (r == null) {
205 return null;
206 }
207 final BigInteger[] quotAndRem = r.mNum.divideAndRemainder(r.mDen);
208 if (quotAndRem[1].signum() == 0) {
209 return quotAndRem[0];
210 } else {
211 return null;
212 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700213 }
214 public static BoundedRational add(BoundedRational r1, BoundedRational r2) {
Hans Boehmf599db72015-08-10 16:19:24 -0700215 if (r1 == null || r2 == null) {
216 return null;
217 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700218 final BigInteger den = r1.mDen.multiply(r2.mDen);
Hans Boehmf599db72015-08-10 16:19:24 -0700219 final BigInteger num = r1.mNum.multiply(r2.mDen).add(r2.mNum.multiply(r1.mDen));
Hans Boehm682ff5e2015-03-09 14:40:25 -0700220 return new BoundedRational(num,den).maybeReduce();
221 }
222
223 public static BoundedRational negate(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700224 if (r == null) {
225 return null;
226 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700227 return new BoundedRational(r.mNum.negate(), r.mDen);
228 }
229
230 static BoundedRational subtract(BoundedRational r1, BoundedRational r2) {
231 return add(r1, negate(r2));
232 }
233
234 static BoundedRational multiply(BoundedRational r1, BoundedRational r2) {
Hans Boehmf599db72015-08-10 16:19:24 -0700235 // It's tempting but marginally unsound to reduce 0 * null to 0. The null could represent
236 // an infinite value, for which we failed to throw an exception because it was too big.
237 if (r1 == null || r2 == null) {
238 return null;
239 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700240 final BigInteger num = r1.mNum.multiply(r2.mNum);
241 final BigInteger den = r1.mDen.multiply(r2.mDen);
242 return new BoundedRational(num,den).maybeReduce();
243 }
244
Hans Boehmfbcef702015-04-27 18:07:47 -0700245 public static class ZeroDivisionException extends ArithmeticException {
246 public ZeroDivisionException() {
247 super("Division by zero");
248 }
249 }
250
Hans Boehmf599db72015-08-10 16:19:24 -0700251 /**
252 * Return the reciprocal of r (or null).
253 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700254 static BoundedRational inverse(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700255 if (r == null) {
256 return null;
257 }
258 if (r.mNum.signum() == 0) {
Hans Boehmfbcef702015-04-27 18:07:47 -0700259 throw new ZeroDivisionException();
Hans Boehm682ff5e2015-03-09 14:40:25 -0700260 }
261 return new BoundedRational(r.mDen, r.mNum);
262 }
263
264 static BoundedRational divide(BoundedRational r1, BoundedRational r2) {
265 return multiply(r1, inverse(r2));
266 }
267
268 static BoundedRational sqrt(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700269 // Return non-null if numerator and denominator are small perfect squares.
270 if (r == null) {
271 return null;
272 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700273 r = r.positiveDen().reduce();
Hans Boehmf599db72015-08-10 16:19:24 -0700274 if (r.mNum.signum() < 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700275 throw new ArithmeticException("sqrt(negative)");
276 }
Hans Boehmf599db72015-08-10 16:19:24 -0700277 final BigInteger num_sqrt = BigInteger.valueOf(Math.round(Math.sqrt(r.mNum.doubleValue())));
278 if (!num_sqrt.multiply(num_sqrt).equals(r.mNum)) {
279 return null;
280 }
281 final BigInteger den_sqrt = BigInteger.valueOf(Math.round(Math.sqrt(r.mDen.doubleValue())));
282 if (!den_sqrt.multiply(den_sqrt).equals(r.mDen)) {
283 return null;
284 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700285 return new BoundedRational(num_sqrt, den_sqrt);
286 }
287
288 public final static BoundedRational ZERO = new BoundedRational(0);
289 public final static BoundedRational HALF = new BoundedRational(1,2);
290 public final static BoundedRational MINUS_HALF = new BoundedRational(-1,2);
291 public final static BoundedRational ONE = new BoundedRational(1);
292 public final static BoundedRational MINUS_ONE = new BoundedRational(-1);
293 public final static BoundedRational TWO = new BoundedRational(2);
294 public final static BoundedRational MINUS_TWO = new BoundedRational(-2);
295 public final static BoundedRational THIRTY = new BoundedRational(30);
296 public final static BoundedRational MINUS_THIRTY = new BoundedRational(-30);
297 public final static BoundedRational FORTY_FIVE = new BoundedRational(45);
Hans Boehmf599db72015-08-10 16:19:24 -0700298 public final static BoundedRational MINUS_FORTY_FIVE = new BoundedRational(-45);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700299 public final static BoundedRational NINETY = new BoundedRational(90);
300 public final static BoundedRational MINUS_NINETY = new BoundedRational(-90);
301
302 private static BoundedRational map0to0(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700303 if (r == null) {
304 return null;
305 }
306 if (r.mNum.signum() == 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700307 return ZERO;
308 }
309 return null;
310 }
311
Hans Boehm4db31b42015-05-31 12:19:05 -0700312 private static BoundedRational map0to1(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700313 if (r == null) {
314 return null;
315 }
316 if (r.mNum.signum() == 0) {
Hans Boehm4db31b42015-05-31 12:19:05 -0700317 return ONE;
318 }
319 return null;
320 }
321
Hans Boehm682ff5e2015-03-09 14:40:25 -0700322 private static BoundedRational map1to0(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700323 if (r == null) {
324 return null;
325 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700326 if (r.mNum.equals(r.mDen)) {
327 return ZERO;
328 }
329 return null;
330 }
331
Hans Boehmf599db72015-08-10 16:19:24 -0700332 // Throw an exception if the argument is definitely out of bounds for asin or acos.
Hans Boehm682ff5e2015-03-09 14:40:25 -0700333 private static void checkAsinDomain(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700334 if (r == null) {
335 return;
336 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700337 if (r.mNum.abs().compareTo(r.mDen.abs()) > 0) {
338 throw new ArithmeticException("inverse trig argument out of range");
339 }
340 }
341
342 public static BoundedRational sin(BoundedRational r) {
343 return map0to0(r);
344 }
345
346 private final static BigInteger BIG360 = BigInteger.valueOf(360);
347
348 public static BoundedRational degreeSin(BoundedRational r) {
349 final BigInteger r_BI = asBigInteger(r);
Hans Boehmf599db72015-08-10 16:19:24 -0700350 if (r_BI == null) {
351 return null;
352 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700353 final int r_int = r_BI.mod(BIG360).intValue();
Hans Boehmf599db72015-08-10 16:19:24 -0700354 if (r_int % 30 != 0) {
355 return null;
356 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700357 switch (r_int / 10) {
358 case 0:
359 return ZERO;
360 case 3: // 30 degrees
361 return HALF;
362 case 9:
363 return ONE;
364 case 15:
365 return HALF;
366 case 18: // 180 degrees
367 return ZERO;
368 case 21:
369 return MINUS_HALF;
370 case 27:
371 return MINUS_ONE;
372 case 33:
373 return MINUS_HALF;
374 default:
375 return null;
376 }
377 }
378
379 public static BoundedRational asin(BoundedRational r) {
380 checkAsinDomain(r);
381 return map0to0(r);
382 }
383
384 public static BoundedRational degreeAsin(BoundedRational r) {
385 checkAsinDomain(r);
386 final BigInteger r2_BI = asBigInteger(multiply(r, TWO));
Hans Boehmf599db72015-08-10 16:19:24 -0700387 if (r2_BI == null) {
388 return null;
389 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700390 final int r2_int = r2_BI.intValue();
Hans Boehmf599db72015-08-10 16:19:24 -0700391 // Somewhat surprisingly, it seems to be the case that the following covers all rational
392 // cases:
Hans Boehm682ff5e2015-03-09 14:40:25 -0700393 switch (r2_int) {
394 case -2: // Corresponding to -1 argument
395 return MINUS_NINETY;
396 case -1: // Corresponding to -1/2 argument
397 return MINUS_THIRTY;
398 case 0:
399 return ZERO;
400 case 1:
401 return THIRTY;
402 case 2:
403 return NINETY;
404 default:
405 throw new AssertionError("Impossible asin arg");
406 }
407 }
408
409 public static BoundedRational tan(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700410 // Unlike the degree case, we cannot check for the singularity, since it occurs at an
411 // irrational argument.
Hans Boehm682ff5e2015-03-09 14:40:25 -0700412 return map0to0(r);
413 }
414
415 public static BoundedRational degreeTan(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700416 final BoundedRational degSin = degreeSin(r);
417 final BoundedRational degCos = degreeCos(r);
418 if (degCos != null && degCos.mNum.signum() == 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700419 throw new ArithmeticException("Tangent undefined");
420 }
Hans Boehmf599db72015-08-10 16:19:24 -0700421 return divide(degSin, degCos);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700422 }
423
424 public static BoundedRational atan(BoundedRational r) {
425 return map0to0(r);
426 }
427
428 public static BoundedRational degreeAtan(BoundedRational r) {
429 final BigInteger r_BI = asBigInteger(r);
Hans Boehmf599db72015-08-10 16:19:24 -0700430 if (r_BI == null) {
431 return null;
432 }
433 if (r_BI.abs().compareTo(BigInteger.ONE) > 0) {
434 return null;
435 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700436 final int r_int = r_BI.intValue();
437 // Again, these seem to be all rational cases:
438 switch (r_int) {
439 case -1:
440 return MINUS_FORTY_FIVE;
441 case 0:
442 return ZERO;
443 case 1:
444 return FORTY_FIVE;
445 default:
446 throw new AssertionError("Impossible atan arg");
447 }
448 }
449
450 public static BoundedRational cos(BoundedRational r) {
Hans Boehm4db31b42015-05-31 12:19:05 -0700451 return map0to1(r);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700452 }
453
454 public static BoundedRational degreeCos(BoundedRational r) {
455 return degreeSin(add(r, NINETY));
456 }
457
458 public static BoundedRational acos(BoundedRational r) {
459 checkAsinDomain(r);
460 return map1to0(r);
461 }
462
463 public static BoundedRational degreeAcos(BoundedRational r) {
464 final BoundedRational asin_r = degreeAsin(r);
465 return subtract(NINETY, asin_r);
466 }
467
468 private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
469
Hans Boehmf599db72015-08-10 16:19:24 -0700470 /**
471 * Compute an integral power of this.
472 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700473 private BoundedRational pow(BigInteger exp) {
Hans Boehmf599db72015-08-10 16:19:24 -0700474 if (exp.signum() < 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700475 return inverse(pow(exp.negate()));
476 }
Hans Boehmf599db72015-08-10 16:19:24 -0700477 if (exp.equals(BigInteger.ONE)) {
478 return this;
479 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700480 if (exp.and(BigInteger.ONE).intValue() == 1) {
481 return multiply(pow(exp.subtract(BigInteger.ONE)), this);
482 }
Hans Boehmf599db72015-08-10 16:19:24 -0700483 if (exp.signum() == 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700484 return ONE;
485 }
486 BoundedRational tmp = pow(exp.shiftRight(1));
Hans Boehmc1ea0912015-06-19 15:05:07 -0700487 if (Thread.interrupted()) {
Hans Boehm19e93c92015-06-19 18:31:28 -0700488 throw new CR.AbortedException();
Hans Boehmc1ea0912015-06-19 15:05:07 -0700489 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700490 return multiply(tmp, tmp);
491 }
492
493 public static BoundedRational pow(BoundedRational base, BoundedRational exp) {
Hans Boehmf599db72015-08-10 16:19:24 -0700494 if (exp == null) {
495 return null;
496 }
497 if (exp.mNum.signum() == 0) {
498 // Questionable if base has undefined value. Java.lang.Math.pow() returns 1 anyway,
499 // so we do the same.
Hans Boehm682ff5e2015-03-09 14:40:25 -0700500 return new BoundedRational(1);
501 }
Hans Boehmf599db72015-08-10 16:19:24 -0700502 if (base == null) {
503 return null;
504 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700505 exp = exp.reduce().positiveDen();
Hans Boehmf599db72015-08-10 16:19:24 -0700506 if (!exp.mDen.equals(BigInteger.ONE)) {
507 return null;
508 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700509 return base.pow(exp.mNum);
510 }
511
512 public static BoundedRational ln(BoundedRational r) {
Hans Boehm9e855e82015-04-22 18:03:28 -0700513 if (r != null && r.signum() <= 0) {
514 throw new ArithmeticException("log(non-positive)");
Hans Boehm682ff5e2015-03-09 14:40:25 -0700515 }
516 return map1to0(r);
517 }
518
Hans Boehm4db31b42015-05-31 12:19:05 -0700519 public static BoundedRational exp(BoundedRational r) {
520 return map0to1(r);
521 }
522
Hans Boehmf599db72015-08-10 16:19:24 -0700523 /**
524 * Return the base 10 log of n, if n is a power of 10, -1 otherwise.
525 * n must be positive.
526 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700527 private static long b10Log(BigInteger n) {
528 // This algorithm is very naive, but we doubt it matters.
529 long count = 0;
Hans Boehmf599db72015-08-10 16:19:24 -0700530 while (n.mod(BigInteger.TEN).signum() == 0) {
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700531 if (Thread.interrupted()) {
532 throw new CR.AbortedException();
533 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700534 n = n.divide(BigInteger.TEN);
535 ++count;
536 }
537 if (n.equals(BigInteger.ONE)) {
538 return count;
539 }
540 return -1;
541 }
542
543 public static BoundedRational log(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700544 if (r == null) {
545 return null;
546 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700547 if (r.signum() <= 0) {
548 throw new ArithmeticException("log(non-positive)");
Hans Boehm682ff5e2015-03-09 14:40:25 -0700549 }
Hans Boehm9e855e82015-04-22 18:03:28 -0700550 r = r.reduce().positiveDen();
Hans Boehmf599db72015-08-10 16:19:24 -0700551 if (r == null) {
552 return null;
553 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700554 if (r.mDen.equals(BigInteger.ONE)) {
555 long log = b10Log(r.mNum);
Hans Boehmf599db72015-08-10 16:19:24 -0700556 if (log != -1) {
557 return new BoundedRational(log);
558 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700559 } else if (r.mNum.equals(BigInteger.ONE)) {
560 long log = b10Log(r.mDen);
Hans Boehmf599db72015-08-10 16:19:24 -0700561 if (log != -1) {
562 return new BoundedRational(-log);
563 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700564 }
565 return null;
566 }
567
Hans Boehmf599db72015-08-10 16:19:24 -0700568 /**
569 * Generalized factorial.
570 * Compute n * (n - step) * (n - 2 * step) * etc. This can be used to compute factorial a bit
571 * faster, especially if BigInteger uses sub-quadratic multiplication.
572 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700573 private static BigInteger genFactorial(long n, long step) {
574 if (n > 4 * step) {
575 BigInteger prod1 = genFactorial(n, 2 * step);
Hans Boehmc023b732015-04-29 11:30:47 -0700576 if (Thread.interrupted()) {
Hans Boehm19e93c92015-06-19 18:31:28 -0700577 throw new CR.AbortedException();
Hans Boehmc023b732015-04-29 11:30:47 -0700578 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700579 BigInteger prod2 = genFactorial(n - step, 2 * step);
Hans Boehmc023b732015-04-29 11:30:47 -0700580 if (Thread.interrupted()) {
Hans Boehm19e93c92015-06-19 18:31:28 -0700581 throw new CR.AbortedException();
Hans Boehmc023b732015-04-29 11:30:47 -0700582 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700583 return prod1.multiply(prod2);
584 } else {
Hans Boehm997783b2015-10-01 16:07:56 -0700585 if (n == 0) {
586 return BigInteger.ONE;
587 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700588 BigInteger res = BigInteger.valueOf(n);
589 for (long i = n - step; i > 1; i -= step) {
590 res = res.multiply(BigInteger.valueOf(i));
591 }
592 return res;
593 }
594 }
595
Hans Boehmf599db72015-08-10 16:19:24 -0700596 /**
597 * Factorial function.
598 * Always produces non-null (or exception) when called on non-null r.
599 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700600 public static BoundedRational fact(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700601 if (r == null) {
602 return null;
603 }
604 final BigInteger rAsInt = asBigInteger(r);
605 if (rAsInt == null) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700606 throw new ArithmeticException("Non-integral factorial argument");
607 }
Hans Boehmf599db72015-08-10 16:19:24 -0700608 if (rAsInt.signum() < 0) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700609 throw new ArithmeticException("Negative factorial argument");
610 }
Hans Boehmf599db72015-08-10 16:19:24 -0700611 if (rAsInt.bitLength() > 30) {
Hans Boehm682ff5e2015-03-09 14:40:25 -0700612 // Will fail. LongValue() may not work. Punt now.
613 throw new ArithmeticException("Factorial argument too big");
614 }
Hans Boehmf599db72015-08-10 16:19:24 -0700615 return new BoundedRational(genFactorial(rAsInt.longValue(), 1));
Hans Boehm682ff5e2015-03-09 14:40:25 -0700616 }
617
618 private static final BigInteger BIG_FIVE = BigInteger.valueOf(5);
Hans Boehmcd740592015-06-13 21:12:23 -0700619 private static final BigInteger BIG_MINUS_ONE = BigInteger.valueOf(-1);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700620
Hans Boehmf599db72015-08-10 16:19:24 -0700621 /**
622 * Return the number of decimal digits to the right of the decimal point required to represent
623 * the argument exactly.
624 * Return Integer.MAX_VALUE if that's not possible. Never returns a value less than zero, even
625 * if r is a power of ten.
626 */
Hans Boehm682ff5e2015-03-09 14:40:25 -0700627 static int digitsRequired(BoundedRational r) {
Hans Boehmf599db72015-08-10 16:19:24 -0700628 if (r == null) {
629 return Integer.MAX_VALUE;
630 }
631 int powersOfTwo = 0; // Max power of 2 that divides denominator
632 int powersOfFive = 0; // Max power of 5 that divides denominator
Hans Boehm682ff5e2015-03-09 14:40:25 -0700633 // Try the easy case first to speed things up.
Hans Boehmf599db72015-08-10 16:19:24 -0700634 if (r.mDen.equals(BigInteger.ONE)) {
635 return 0;
636 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700637 r = r.reduce();
638 BigInteger den = r.mDen;
Hans Boehm82e5a2f2015-07-20 20:08:14 -0700639 if (den.bitLength() > MAX_SIZE) {
640 return Integer.MAX_VALUE;
641 }
Hans Boehm682ff5e2015-03-09 14:40:25 -0700642 while (!den.testBit(0)) {
Hans Boehmf599db72015-08-10 16:19:24 -0700643 ++powersOfTwo;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700644 den = den.shiftRight(1);
645 }
Hans Boehmf599db72015-08-10 16:19:24 -0700646 while (den.mod(BIG_FIVE).signum() == 0) {
647 ++powersOfFive;
Hans Boehm682ff5e2015-03-09 14:40:25 -0700648 den = den.divide(BIG_FIVE);
649 }
Hans Boehmf599db72015-08-10 16:19:24 -0700650 // If the denominator has a factor of other than 2 or 5 (the divisors of 10), the decimal
651 // expansion does not terminate. Multiplying the fraction by any number of powers of 10
652 // will not cancel the demoniator. (Recall the fraction was in lowest terms to start
653 // with.) Otherwise the powers of 10 we need to cancel the denominator is the larger of
654 // powersOfTwo and powersOfFive.
Hans Boehmcd740592015-06-13 21:12:23 -0700655 if (!den.equals(BigInteger.ONE) && !den.equals(BIG_MINUS_ONE)) {
656 return Integer.MAX_VALUE;
657 }
Hans Boehmf599db72015-08-10 16:19:24 -0700658 return Math.max(powersOfTwo, powersOfFive);
Hans Boehm682ff5e2015-03-09 14:40:25 -0700659 }
660}