Home | History | Annotate | Download | only in calculator2
      1 /*
      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 
     17 package com.android.calculator2;
     18 
     19 import com.hp.creals.CR;
     20 
     21 import java.math.BigInteger;
     22 import java.util.Objects;
     23 import java.util.Random;
     24 
     25 /**
     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  */
     35 public class BoundedRational {
     36     // TODO: Consider returning null for integers.  With some care, large factorials might become
     37     // much faster.
     38     // TODO: Maybe eventually make this extend Number?
     39 
     40     private static final int MAX_SIZE = 10000; // total, in bits
     41 
     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 
     65     /**
     66      * 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     /**
    120      * Convert to String reflecting raw representation.
    121      * Debug or log messages only, not pretty.
    122      */
    123     public String toString() {
    124         return mNum.toString() + "/" + mDen.toString();
    125     }
    126 
    127     /**
    128      * Convert to readable String.
    129      * Intended for output to user.  More expensive, less useful for debugging than
    130      * toString().  Not internationalized.
    131      */
    132     public String toNiceString() {
    133         final BoundedRational nicer = reduce().positiveDen();
    134         String result = nicer.mNum.toString();
    135         if (!nicer.mDen.equals(BigInteger.ONE)) {
    136             result += "/" + nicer.mDen;
    137         }
    138         return result;
    139     }
    140 
    141     public static String toString(BoundedRational r) {
    142         if (r == null) {
    143             return "not a small rational";
    144         }
    145         return r.toString();
    146     }
    147 
    148     /**
    149      * 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      */
    153     public String toStringTruncated(int n) {
    154         String digits = mNum.abs().multiply(BigInteger.TEN.pow(n)).divide(mDen.abs()).toString();
    155         int len = digits.length();
    156         if (len < n + 1) {
    157             digits = StringUtils.repeat('0', n + 1 - len) + digits;
    158             len = n + 1;
    159         }
    160         return (signum() < 0 ? "-" : "") + digits.substring(0, len - n) + "."
    161                 + digits.substring(len - n);
    162     }
    163 
    164     /**
    165      * Return a double approximation.
    166      * The result is correctly rounded to nearest, with ties rounded away from zero.
    167      * TODO: Should round ties to even.
    168      */
    169     public double doubleValue() {
    170         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);
    210     }
    211 
    212     public CR crValue() {
    213         return CR.valueOf(mNum).divide(CR.valueOf(mDen));
    214     }
    215 
    216     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 
    224     // Approximate number of bits to left of binary point.
    225     // Negative indicates leading zeroes to the right of binary point.
    226     public int wholeNumberBits() {
    227         if (mNum.signum() == 0) {
    228             return Integer.MIN_VALUE;
    229         } else {
    230             return mNum.bitLength() - mDen.bitLength();
    231         }
    232     }
    233 
    234     /**
    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      */
    238     private boolean tooBig() {
    239         if (mDen.equals(BigInteger.ONE)) {
    240             return false;
    241         }
    242         return (mNum.bitLength() + mDen.bitLength() > MAX_SIZE);
    243     }
    244 
    245     /**
    246      * Return an equivalent fraction with a positive denominator.
    247      */
    248     private BoundedRational positiveDen() {
    249         if (mDen.signum() > 0) {
    250             return this;
    251         }
    252         return new BoundedRational(mNum.negate(), mDen.negate());
    253     }
    254 
    255     /**
    256      * Return an equivalent fraction in lowest terms.
    257      * Denominator sign may remain negative.
    258      */
    259     private BoundedRational reduce() {
    260         if (mDen.equals(BigInteger.ONE)) {
    261             return this;  // Optimization only
    262         }
    263         final BigInteger divisor = mNum.gcd(mDen);
    264         return new BoundedRational(mNum.divide(divisor), mDen.divide(divisor));
    265     }
    266 
    267     static Random sReduceRng = new Random();
    268 
    269     /**
    270      * Return a possibly reduced version of r that's not tooBig().
    271      * Return null if none exists.
    272      */
    273     private static BoundedRational maybeReduce(BoundedRational r) {
    274         if (r == null) return null;
    275         // Reduce randomly, with 1/16 probability, or if the result is too big.
    276         if (!r.tooBig() && (sReduceRng.nextInt() & 0xf) != 0) {
    277             return r;
    278         }
    279         BoundedRational result = r.positiveDen();
    280         result = result.reduce();
    281         if (!result.tooBig()) {
    282             return result;
    283         }
    284         return null;
    285     }
    286 
    287     public int compareTo(BoundedRational r) {
    288         // 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();
    292     }
    293 
    294     public int signum() {
    295         return mNum.signum() * mDen.signum();
    296     }
    297 
    298     @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;
    308     }
    309 
    310     // 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.
    313 
    314     /**
    315      * Returns equivalent BigInteger result if it exists, null if not.
    316      */
    317     public static BigInteger asBigInteger(BoundedRational r) {
    318         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         }
    327     }
    328     public static BoundedRational add(BoundedRational r1, BoundedRational r2) {
    329         if (r1 == null || r2 == null) {
    330             return null;
    331         }
    332         final BigInteger den = r1.mDen.multiply(r2.mDen);
    333         final BigInteger num = r1.mNum.multiply(r2.mDen).add(r2.mNum.multiply(r1.mDen));
    334         return maybeReduce(new BoundedRational(num,den));
    335     }
    336 
    337     /**
    338      * Return the argument, but with the opposite sign.
    339      * Returns null only for a null argument.
    340      */
    341     public static BoundedRational negate(BoundedRational r) {
    342         if (r == null) {
    343             return null;
    344         }
    345         return new BoundedRational(r.mNum.negate(), r.mDen);
    346     }
    347 
    348     public static BoundedRational subtract(BoundedRational r1, BoundedRational r2) {
    349         return add(r1, negate(r2));
    350     }
    351 
    352     /**
    353      * Return product of r1 and r2 without reducing the result.
    354      */
    355     private static BoundedRational rawMultiply(BoundedRational r1, BoundedRational r2) {
    356         // 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         }
    361         // 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         }
    368         final BigInteger num = r1.mNum.multiply(r2.mNum);
    369         final BigInteger den = r1.mDen.multiply(r2.mDen);
    370         return new BoundedRational(num,den);
    371     }
    372 
    373     public static BoundedRational multiply(BoundedRational r1, BoundedRational r2) {
    374         return maybeReduce(rawMultiply(r1, r2));
    375     }
    376 
    377     public static class ZeroDivisionException extends ArithmeticException {
    378         public ZeroDivisionException() {
    379             super("Division by zero");
    380         }
    381     }
    382 
    383     /**
    384      * Return the reciprocal of r (or null if the argument was null).
    385      */
    386     public static BoundedRational inverse(BoundedRational r) {
    387         if (r == null) {
    388             return null;
    389         }
    390         if (r.mNum.signum() == 0) {
    391             throw new ZeroDivisionException();
    392         }
    393         return new BoundedRational(r.mDen, r.mNum);
    394     }
    395 
    396     public static BoundedRational divide(BoundedRational r1, BoundedRational r2) {
    397         return multiply(r1, inverse(r2));
    398     }
    399 
    400     public static BoundedRational sqrt(BoundedRational r) {
    401         // Return non-null if numerator and denominator are small perfect squares.
    402         if (r == null) {
    403             return null;
    404         }
    405         r = r.positiveDen().reduce();
    406         if (r.mNum.signum() < 0) {
    407             throw new ArithmeticException("sqrt(negative)");
    408         }
    409         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         }
    417         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);
    423     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);
    426     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);
    430     public final static BoundedRational TEN = new BoundedRational(10);
    431     public final static BoundedRational TWELVE = new BoundedRational(12);
    432     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);
    435     public final static BoundedRational MINUS_FORTY_FIVE = new BoundedRational(-45);
    436     public final static BoundedRational NINETY = new BoundedRational(90);
    437     public final static BoundedRational MINUS_NINETY = new BoundedRational(-90);
    438 
    439     private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
    440     private static final BigInteger BIG_MINUS_ONE = BigInteger.valueOf(-1);
    441 
    442     /**
    443      * 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         }
    459         BoundedRational result = rawMultiply(tmp, tmp);
    460         if (result == null || result.tooBig()) {
    461             return null;
    462         }
    463         return result;
    464     }
    465 
    466     /**
    467      * Compute an integral power of this.
    468      */
    469     public BoundedRational pow(BigInteger exp) {
    470         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;
    475         }
    476         if (exp.equals(BigInteger.ONE)) {
    477             return this;
    478         }
    479         // Reducing once at the beginning means there's no point in reducing later.
    480         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         }
    506     }
    507 
    508     public static BoundedRational pow(BoundedRational base, BoundedRational exp) {
    509         if (exp == null) {
    510             return null;
    511         }
    512         if (base == null) {
    513             return null;
    514         }
    515         exp = exp.reduce().positiveDen();
    516         if (!exp.mDen.equals(BigInteger.ONE)) {
    517             return null;
    518         }
    519         return base.pow(exp.mNum);
    520     }
    521 
    522 
    523     private static final BigInteger BIG_FIVE = BigInteger.valueOf(5);
    524 
    525     /**
    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      */
    531     public static int digitsRequired(BoundedRational r) {
    532         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
    537         // Try the easy case first to speed things up.
    538         if (r.mDen.equals(BigInteger.ONE)) {
    539             return 0;
    540         }
    541         r = r.reduce();
    542         BigInteger den = r.mDen;
    543         if (den.bitLength() > MAX_SIZE) {
    544             return Integer.MAX_VALUE;
    545         }
    546         while (!den.testBit(0)) {
    547             ++powersOfTwo;
    548             den = den.shiftRight(1);
    549         }
    550         while (den.mod(BIG_FIVE).signum() == 0) {
    551             ++powersOfFive;
    552             den = den.divide(BIG_FIVE);
    553         }
    554         // 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.
    559         if (!den.equals(BigInteger.ONE) && !den.equals(BIG_MINUS_ONE)) {
    560             return Integer.MAX_VALUE;
    561         }
    562         return Math.max(powersOfTwo, powersOfFive);
    563     }
    564 }
    565