Import translations. DO NOT MERGE
am: 3813499c90 -s ours
Change-Id: I1c18d4efc4094dbe05d6ac2cc5ecdeb46f009ed5
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 067ad1a..706bcb4 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -75,7 +75,7 @@
<string name="approximate" msgid="7117143366610670836">"(±1 a l\'últim dígit)"</string>
<string name="menu_leading" msgid="2338520833272667740">"Resposta dígits inicials"</string>
<string name="menu_fraction" msgid="1247477377840252234">"Resposta fracció"</string>
- <string name="menu_licenses" msgid="1890541368064108592">"Llic. prog. lliure"</string>
+ <string name="menu_licenses" msgid="1890541368064108592">"Llic. codi obert"</string>
<string name="menu_history" msgid="9006222701220105452">"Historial"</string>
<string name="menu_clear_history" msgid="2336526604226069572">"Esborra"</string>
<string name="title_history" msgid="7820912156315181539">"Historial"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
deleted file mode 100644
index 5f5aab3..0000000
--- a/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="1756503303878374382">"Calculator"</string>
- <string name="dec_point" msgid="8920102493070918054">"."</string>
- <string name="mode_deg" msgid="1146123354434332479">"deg"</string>
- <string name="mode_rad" msgid="1434228830085760996">"rad"</string>
- <string name="clr" msgid="6730945431543327337">"clr"</string>
- <string name="cleared" msgid="3952521190281880880">"cleared"</string>
- <string name="del" msgid="5878069000864178910">"del"</string>
- <string name="desc_const_e" msgid="1889187337970539507">"Euler\'s number"</string>
- <string name="desc_const_pi" msgid="5430918714009441655">"pi"</string>
- <string name="desc_dec_point" msgid="5725254504360445023">"point"</string>
- <string name="desc_lparen" msgid="8688758170211924916">"left parenthesis"</string>
- <string name="desc_rparen" msgid="7920608385146151731">"right parenthesis"</string>
- <string name="desc_fun_cos" msgid="3787913784504974731">"cosine"</string>
- <string name="desc_fun_ln" msgid="2505119732546227166">"natural logarithm"</string>
- <string name="desc_fun_log" msgid="4202230639814060062">"logarithm"</string>
- <string name="desc_fun_sin" msgid="7425661271929838876">"sine"</string>
- <string name="desc_fun_tan" msgid="3545496562069310036">"tangent"</string>
- <string name="desc_fun_arccos" msgid="7583077865497939123">"inverse cosine"</string>
- <string name="desc_fun_arcsin" msgid="8592309261123302915">"inverse sine"</string>
- <string name="desc_fun_arctan" msgid="6891388393360447455">"inverse tangent"</string>
- <string name="desc_fun_10pow" msgid="3972646467174520812">"ten to the power of"</string>
- <string name="desc_fun_exp" msgid="657106837530588390">"exponential function"</string>
- <string name="desc_op_sqr" msgid="693577095029219627">"squared"</string>
- <string name="desc_op_add" msgid="5539599782598050813">"plus"</string>
- <string name="desc_op_div" msgid="3032399266115257498">"divide"</string>
- <string name="desc_op_fact" msgid="6142780103294179702">"factorial"</string>
- <string name="desc_op_mul" msgid="6882122010553947015">"multiply"</string>
- <string name="desc_op_pct" msgid="2466179646911499434">"percent"</string>
- <string name="desc_op_pow" msgid="6010144442344099030">"power"</string>
- <string name="desc_op_sqrt" msgid="6882241473601721767">"square root"</string>
- <string name="desc_op_sub" msgid="2744940875059679129">"minus"</string>
- <string name="desc_mode_deg" msgid="4129831241246511710">"degree mode"</string>
- <string name="desc_mode_rad" msgid="730135521908627673">"radian mode"</string>
- <string name="desc_switch_deg" msgid="6675211986921592374">"switch to degrees"</string>
- <string name="desc_switch_rad" msgid="2845875847488919914">"switch to radians"</string>
- <string name="desc_eq" msgid="3349320880874699285">"equals"</string>
- <string name="desc_clr" msgid="737502124268890797">"clear"</string>
- <string name="desc_del" msgid="7879515781929311432">"Delete"</string>
- <string name="desc_inv_off" msgid="3672218250519616068">"show inverse functions"</string>
- <string name="desc_inv_on" msgid="2515675590767677178">"hide inverse functions"</string>
- <string name="desc_formula" msgid="8056588859637585007">"No formula"</string>
- <string name="desc_result" msgid="1794073190203126821">"No result"</string>
- <string name="desc_num_pad" msgid="515720457459745571">"Numbers and basic operations"</string>
- <string name="desc_adv_pad" msgid="3794276256462677914">"Advanced operations"</string>
- <string name="error_nan" msgid="5674077944929888710">"Not a number"</string>
- <string name="error_syntax" msgid="4786987111228645602">"Bad expression"</string>
- <string name="error_aborted" msgid="3402238176316342537">"Aborted"</string>
- <string name="error_overflow" msgid="7800547394563434764">"Infinite?"</string>
- <string name="error_zero_divide" msgid="458040988686661801">"Can\'t divide by 0"</string>
- <string name="text_copied_toast" msgid="2104264466812485425">"Text copied"</string>
- <string name="cancelled" msgid="2207323593183640994">"Computation cancelled."</string>
- <string name="timeout" msgid="5461892570729418573">"Value may be infinite or undefined."</string>
- <string name="ok_remove_timeout" msgid="8344529153919268011">"Use longer timeouts"</string>
- <string name="dismiss" msgid="7872443888515066216">"Dismiss"</string>
- <string name="exact" msgid="2597237880041789948">"(exact)"</string>
- <string name="approximate" msgid="7117143366610670836">"(±1 in last digit)"</string>
- <string name="menu_leading" msgid="2338520833272667740">"Answer with leading digits"</string>
- <string name="menu_fraction" msgid="1247477377840252234">"Answer as fraction"</string>
- <string name="menu_licenses" msgid="1890541368064108592">"Open-source licences"</string>
- <string name="menu_history" msgid="9006222701220105452">"History"</string>
- <string name="menu_clear_history" msgid="2336526604226069572">"Clear"</string>
- <string name="title_history" msgid="7820912156315181539">"History"</string>
- <string name="desc_navigate_up" msgid="6208699569165589623">"Navigate up"</string>
- <string name="dialog_timeout" msgid="9069768442342329065">"Timeout"</string>
- <string name="dialog_clear" msgid="7137395395908350702">"Clear history and memory?"</string>
- <string name="title_current_expression" msgid="2442754548537900591">"Current Expression"</string>
- <string name="no_history" msgid="4331262030447527188">"No history"</string>
-</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
deleted file mode 100644
index 2f1b4a8..0000000
--- a/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="1756503303878374382">"Calculator"</string>
- <string name="dec_point" msgid="8920102493070918054">"."</string>
- <string name="mode_deg" msgid="1146123354434332479">"deg"</string>
- <string name="mode_rad" msgid="1434228830085760996">"rad"</string>
- <string name="clr" msgid="6730945431543327337">"clr"</string>
- <string name="cleared" msgid="3952521190281880880">"cleared"</string>
- <string name="del" msgid="5878069000864178910">"del"</string>
- <string name="desc_const_e" msgid="1889187337970539507">"Euler\'s number"</string>
- <string name="desc_const_pi" msgid="5430918714009441655">"pi"</string>
- <string name="desc_dec_point" msgid="5725254504360445023">"point"</string>
- <string name="desc_lparen" msgid="8688758170211924916">"left parenthesis"</string>
- <string name="desc_rparen" msgid="7920608385146151731">"right parenthesis"</string>
- <string name="desc_fun_cos" msgid="3787913784504974731">"cosine"</string>
- <string name="desc_fun_ln" msgid="2505119732546227166">"natural logarithm"</string>
- <string name="desc_fun_log" msgid="4202230639814060062">"logarithm"</string>
- <string name="desc_fun_sin" msgid="7425661271929838876">"sine"</string>
- <string name="desc_fun_tan" msgid="3545496562069310036">"tangent"</string>
- <string name="desc_fun_arccos" msgid="7583077865497939123">"inverse cosine"</string>
- <string name="desc_fun_arcsin" msgid="8592309261123302915">"inverse sine"</string>
- <string name="desc_fun_arctan" msgid="6891388393360447455">"inverse tangent"</string>
- <string name="desc_fun_10pow" msgid="3972646467174520812">"ten to the power of"</string>
- <string name="desc_fun_exp" msgid="657106837530588390">"exponential function"</string>
- <string name="desc_op_sqr" msgid="693577095029219627">"squared"</string>
- <string name="desc_op_add" msgid="5539599782598050813">"plus"</string>
- <string name="desc_op_div" msgid="3032399266115257498">"divide"</string>
- <string name="desc_op_fact" msgid="6142780103294179702">"factorial"</string>
- <string name="desc_op_mul" msgid="6882122010553947015">"multiply"</string>
- <string name="desc_op_pct" msgid="2466179646911499434">"percent"</string>
- <string name="desc_op_pow" msgid="6010144442344099030">"power"</string>
- <string name="desc_op_sqrt" msgid="6882241473601721767">"square root"</string>
- <string name="desc_op_sub" msgid="2744940875059679129">"minus"</string>
- <string name="desc_mode_deg" msgid="4129831241246511710">"degree mode"</string>
- <string name="desc_mode_rad" msgid="730135521908627673">"radian mode"</string>
- <string name="desc_switch_deg" msgid="6675211986921592374">"switch to degrees"</string>
- <string name="desc_switch_rad" msgid="2845875847488919914">"switch to radians"</string>
- <string name="desc_eq" msgid="3349320880874699285">"equals"</string>
- <string name="desc_clr" msgid="737502124268890797">"clear"</string>
- <string name="desc_del" msgid="7879515781929311432">"delete"</string>
- <string name="desc_inv_off" msgid="3672218250519616068">"show inverse functions"</string>
- <string name="desc_inv_on" msgid="2515675590767677178">"hide inverse functions"</string>
- <string name="desc_formula" msgid="8056588859637585007">"No formula"</string>
- <string name="desc_result" msgid="1794073190203126821">"No result"</string>
- <string name="desc_num_pad" msgid="515720457459745571">"Numbers and basic operations"</string>
- <string name="desc_adv_pad" msgid="3794276256462677914">"Advanced operations"</string>
- <string name="error_nan" msgid="5674077944929888710">"Not a number"</string>
- <string name="error_syntax" msgid="4786987111228645602">"Bad expression"</string>
- <string name="error_aborted" msgid="3402238176316342537">"Aborted"</string>
- <string name="error_overflow" msgid="7800547394563434764">"Infinite?"</string>
- <string name="error_zero_divide" msgid="458040988686661801">"Can\'t divide by 0"</string>
- <string name="text_copied_toast" msgid="2104264466812485425">"Text copied"</string>
- <string name="cancelled" msgid="2207323593183640994">"Computation cancelled."</string>
- <string name="timeout" msgid="5461892570729418573">"Value may be infinite or undefined."</string>
- <string name="ok_remove_timeout" msgid="8344529153919268011">"Use longer timeouts"</string>
- <string name="dismiss" msgid="7872443888515066216">"Dismiss"</string>
- <string name="exact" msgid="2597237880041789948">"(exact)"</string>
- <string name="approximate" msgid="7117143366610670836">"(±1 in last digit)"</string>
- <string name="menu_leading" msgid="2338520833272667740">"Answer with leading digits"</string>
- <string name="menu_fraction" msgid="1247477377840252234">"Answer as fraction"</string>
- <string name="menu_licenses" msgid="1890541368064108592">"Open source licenses"</string>
- <string name="menu_history" msgid="9006222701220105452">"History"</string>
- <string name="menu_clear_history" msgid="2336526604226069572">"Clear"</string>
- <string name="title_history" msgid="7820912156315181539">"History"</string>
- <string name="desc_navigate_up" msgid="6208699569165589623">"Navigate up"</string>
- <string name="dialog_timeout" msgid="9069768442342329065">"Timeout"</string>
- <string name="dialog_clear" msgid="7137395395908350702">"Clear history and memory?"</string>
- <string name="title_current_expression" msgid="2442754548537900591">"Current Expression"</string>
- <string name="no_history" msgid="4331262030447527188">"No History"</string>
-</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 4a11525..445259c 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -76,12 +76,12 @@
<string name="menu_leading" msgid="2338520833272667740">"Jawab dengan digit depan"</string>
<string name="menu_fraction" msgid="1247477377840252234">"Jawab sebagai pecahan"</string>
<string name="menu_licenses" msgid="1890541368064108592">"Lisensi sumber terbuka"</string>
- <string name="menu_history" msgid="9006222701220105452">"Histori"</string>
+ <string name="menu_history" msgid="9006222701220105452">"Riwayat"</string>
<string name="menu_clear_history" msgid="2336526604226069572">"Hapus"</string>
- <string name="title_history" msgid="7820912156315181539">"Histori"</string>
+ <string name="title_history" msgid="7820912156315181539">"Riwayat"</string>
<string name="desc_navigate_up" msgid="6208699569165589623">"Navigasi naik"</string>
<string name="dialog_timeout" msgid="9069768442342329065">"Waktu habis"</string>
- <string name="dialog_clear" msgid="7137395395908350702">"Hapus histori dan memori?"</string>
+ <string name="dialog_clear" msgid="7137395395908350702">"Hapus riwayat dan memori?"</string>
<string name="title_current_expression" msgid="2442754548537900591">"Ekspresi Saat Ini"</string>
- <string name="no_history" msgid="4331262030447527188">"Tidak Ada Histori"</string>
+ <string name="no_history" msgid="4331262030447527188">"Tidak Ada Riwayat"</string>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 276e487..7012533 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="1756503303878374382">"ಕ್ಯಾಲ್ಕ್ಯುಲೇಟರ್"</string>
+ <string name="app_name" msgid="1756503303878374382">"ಕ್ಯಾಲ್ಕುಲೇಟರ್"</string>
<string name="dec_point" msgid="8920102493070918054">"."</string>
<string name="mode_deg" msgid="1146123354434332479">"deg"</string>
<string name="mode_rad" msgid="1434228830085760996">"rad"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 310301d..f316b60 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="1756503303878374382">"Rekenmachine"</string>
+ <string name="app_name" msgid="1756503303878374382">"Calculator"</string>
<string name="dec_point" msgid="8920102493070918054">","</string>
<string name="mode_deg" msgid="1146123354434332479">"deg"</string>
<string name="mode_rad" msgid="1434228830085760996">"rad"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index bb129a6..7a21377 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -70,18 +70,18 @@
<string name="cancelled" msgid="2207323593183640994">"గణన రద్దు చేసారు."</string>
<string name="timeout" msgid="5461892570729418573">"విలువ అనంతం లేదా నిర్వచించబడనిది కావచ్చు."</string>
<string name="ok_remove_timeout" msgid="8344529153919268011">"దీర్ఘకాల సమయ ముగింపులను ఉపయోగించు"</string>
- <string name="dismiss" msgid="7872443888515066216">"విస్మరించు"</string>
+ <string name="dismiss" msgid="7872443888515066216">"తీసివేయి"</string>
<string name="exact" msgid="2597237880041789948">"(సరిగ్గా)"</string>
<string name="approximate" msgid="7117143366610670836">"(చివరి అంకెలో ±1)"</string>
<string name="menu_leading" msgid="2338520833272667740">"ఆధిక్య అంకెల్లో జవాబివ్వు"</string>
<string name="menu_fraction" msgid="1247477377840252234">"ఫ్రాక్షన్లో జవాబివ్వు"</string>
<string name="menu_licenses" msgid="1890541368064108592">"ఓపెన్ సోర్స్ లైసెన్స్లు"</string>
<string name="menu_history" msgid="9006222701220105452">"చరిత్ర"</string>
- <string name="menu_clear_history" msgid="2336526604226069572">"క్లియర్ చేయి"</string>
+ <string name="menu_clear_history" msgid="2336526604226069572">"తీసివేయి"</string>
<string name="title_history" msgid="7820912156315181539">"చరిత్ర"</string>
<string name="desc_navigate_up" msgid="6208699569165589623">"పైకి నావిగేట్ చేయి"</string>
<string name="dialog_timeout" msgid="9069768442342329065">"సమయాభావం జరిగింది"</string>
- <string name="dialog_clear" msgid="7137395395908350702">"చరిత్ర మరియు మెమరీని క్లియర్ చేయాలా?"</string>
+ <string name="dialog_clear" msgid="7137395395908350702">"చరిత్ర మరియు మెమరీని తీసివేయాలా?"</string>
<string name="title_current_expression" msgid="2442754548537900591">"ప్రస్తుత వ్యక్తీకరణ"</string>
<string name="no_history" msgid="4331262030447527188">"చరిత్ర లేదు"</string>
</resources>
diff --git a/src/com/android/calculator2/BoundedRational.java b/src/com/android/calculator2/BoundedRational.java
index f3452a2..16fa581 100644
--- a/src/com/android/calculator2/BoundedRational.java
+++ b/src/com/android/calculator2/BoundedRational.java
@@ -19,6 +19,7 @@
import com.hp.creals.CR;
import java.math.BigInteger;
+import java.util.Objects;
import java.util.Random;
/**
@@ -62,6 +63,60 @@
}
/**
+ * Produce BoundedRational equal to the given double.
+ */
+ public static BoundedRational valueOf(double x) {
+ final long l = Math.round(x);
+ if ((double) l == x && Math.abs(l) <= 1000) {
+ return valueOf(l);
+ }
+ final long allBits = Double.doubleToRawLongBits(Math.abs(x));
+ long mantissa = (allBits & ((1L << 52) - 1));
+ final int biased_exp = (int)(allBits >>> 52);
+ if ((biased_exp & 0x7ff) == 0x7ff) {
+ throw new ArithmeticException("Infinity or NaN not convertible to BoundedRational");
+ }
+ final long sign = x < 0.0 ? -1 : 1;
+ int exp = biased_exp - 1075; // 1023 + 52; we treat mantissa as integer.
+ if (biased_exp == 0) {
+ exp += 1; // Denormal exponent is 1 greater.
+ } else {
+ mantissa += (1L << 52); // Implied leading one.
+ }
+ BigInteger num = BigInteger.valueOf(sign * mantissa);
+ BigInteger den = BigInteger.ONE;
+ if (exp >= 0) {
+ num = num.shiftLeft(exp);
+ } else {
+ den = den.shiftLeft(-exp);
+ }
+ return new BoundedRational(num, den);
+ }
+
+ /**
+ * Produce BoundedRational equal to the given long.
+ */
+ public static BoundedRational valueOf(long x) {
+ if (x >= -2 && x <= 10) {
+ switch((int) x) {
+ case -2:
+ return MINUS_TWO;
+ case -1:
+ return MINUS_ONE;
+ case 0:
+ return ZERO;
+ case 1:
+ return ONE;
+ case 2:
+ return TWO;
+ case 10:
+ return TEN;
+ }
+ }
+ return new BoundedRational(x);
+ }
+
+ /**
* Convert to String reflecting raw representation.
* Debug or log messages only, not pretty.
*/
@@ -108,12 +163,50 @@
/**
* Return a double approximation.
- * The result is correctly rounded if numerator and denominator are
- * exactly representable as a double.
- * TODO: This should always be correctly rounded.
+ * The result is correctly rounded to nearest, with ties rounded away from zero.
+ * TODO: Should round ties to even.
*/
public double doubleValue() {
- return mNum.doubleValue() / mDen.doubleValue();
+ final int sign = signum();
+ if (sign < 0) {
+ return -BoundedRational.negate(this).doubleValue();
+ }
+ // We get the mantissa by dividing the numerator by denominator, after
+ // suitably prescaling them so that the integral part of the result contains
+ // enough bits. We do the prescaling to avoid any precision loss, so the division result
+ // is correctly truncated towards zero.
+ final int apprExp = mNum.bitLength() - mDen.bitLength();
+ if (apprExp < -1100 || sign == 0) {
+ // Bail fast for clearly zero result.
+ return 0.0;
+ }
+ final int neededPrec = apprExp - 80;
+ final BigInteger dividend = neededPrec < 0 ? mNum.shiftLeft(-neededPrec) : mNum;
+ final BigInteger divisor = neededPrec > 0 ? mDen.shiftLeft(neededPrec) : mDen;
+ final BigInteger quotient = dividend.divide(divisor);
+ final int qLength = quotient.bitLength();
+ int extraBits = qLength - 53;
+ int exponent = neededPrec + qLength; // Exponent assuming leading binary point.
+ if (exponent >= -1021) {
+ // Binary point is actually to right of leading bit.
+ --exponent;
+ } else {
+ // We're in the gradual underflow range. Drop more bits.
+ extraBits += (-1022 - exponent) + 1;
+ exponent = -1023;
+ }
+ final BigInteger bigMantissa =
+ quotient.add(BigInteger.ONE.shiftLeft(extraBits - 1)).shiftRight(extraBits);
+ if (exponent > 1024) {
+ return Double.POSITIVE_INFINITY;
+ }
+ if (exponent > -1023 && bigMantissa.bitLength() != 53
+ || exponent <= -1023 && bigMantissa.bitLength() >= 53) {
+ throw new AssertionError("doubleValue internal error");
+ }
+ final long mantissa = bigMantissa.longValue();
+ final long bits = (mantissa & ((1l << 52) - 1)) | (((long) exponent + 1023) << 52);
+ return Double.longBitsToDouble(bits);
}
public CR crValue() {
@@ -138,6 +231,10 @@
}
}
+ /**
+ * Is this number too big for us to continue with rational arithmetic?
+ * We return fals for integers on the assumption that we have no better fallback.
+ */
private boolean tooBig() {
if (mDen.equals(BigInteger.ONE)) {
return false;
@@ -198,8 +295,16 @@
return mNum.signum() * mDen.signum();
}
- public boolean equals(BoundedRational r) {
- return compareTo(r) == 0;
+ @Override
+ public int hashCode() {
+ // Note that this may be too expensive to be useful.
+ BoundedRational reduced = reduce().positiveDen();
+ return Objects.hash(reduced.mNum, reduced.mDen);
+ }
+
+ @Override
+ public boolean equals(Object r) {
+ return r != null && r instanceof BoundedRational && compareTo((BoundedRational) r) == 0;
}
// We use static methods for arithmetic, so that we can easily handle the null case. We try
@@ -332,6 +437,7 @@
public final static BoundedRational MINUS_NINETY = new BoundedRational(-90);
private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
+ private static final BigInteger BIG_MINUS_ONE = BigInteger.valueOf(-1);
/**
* Compute integral power of this, assuming this has been reduced and exp is >= 0.
@@ -350,32 +456,59 @@
if (Thread.interrupted()) {
throw new CR.AbortedException();
}
- return rawMultiply(tmp, tmp);
+ BoundedRational result = rawMultiply(tmp, tmp);
+ if (result == null || result.tooBig()) {
+ return null;
+ }
+ return result;
}
/**
* Compute an integral power of this.
*/
public BoundedRational pow(BigInteger exp) {
- if (exp.signum() < 0) {
- return inverse(pow(exp.negate()));
+ int expSign = exp.signum();
+ if (expSign == 0) {
+ // Questionable if base has undefined or zero value.
+ // java.lang.Math.pow() returns 1 anyway, so we do the same.
+ return BoundedRational.ONE;
}
if (exp.equals(BigInteger.ONE)) {
return this;
}
// Reducing once at the beginning means there's no point in reducing later.
- return reduce().rawPow(exp);
+ BoundedRational reduced = reduce().positiveDen();
+ // First handle cases in which huge exponents could give compact results.
+ if (reduced.mDen.equals(BigInteger.ONE)) {
+ if (reduced.mNum.equals(BigInteger.ZERO)) {
+ return ZERO;
+ }
+ if (reduced.mNum.equals(BigInteger.ONE)) {
+ return ONE;
+ }
+ if (reduced.mNum.equals(BIG_MINUS_ONE)) {
+ if (exp.testBit(0)) {
+ return MINUS_ONE;
+ } else {
+ return ONE;
+ }
+ }
+ }
+ if (exp.bitLength() > 1000) {
+ // Stack overflow is likely; a useful rational result is not.
+ return null;
+ }
+ if (expSign < 0) {
+ return inverse(reduced).rawPow(exp.negate());
+ } else {
+ return reduced.rawPow(exp);
+ }
}
public static BoundedRational pow(BoundedRational base, BoundedRational exp) {
if (exp == null) {
return null;
}
- if (exp.mNum.signum() == 0) {
- // Questionable if base has undefined value. Java.lang.Math.pow() returns 1 anyway,
- // so we do the same.
- return new BoundedRational(1);
- }
if (base == null) {
return null;
}
@@ -388,7 +521,6 @@
private static final BigInteger BIG_FIVE = BigInteger.valueOf(5);
- private static final BigInteger BIG_MINUS_ONE = BigInteger.valueOf(-1);
/**
* Return the number of decimal digits to the right of the decimal point required to represent
diff --git a/src/com/android/calculator2/UnifiedReal.java b/src/com/android/calculator2/UnifiedReal.java
index f6cf50b..f85dd3e 100644
--- a/src/com/android/calculator2/UnifiedReal.java
+++ b/src/com/android/calculator2/UnifiedReal.java
@@ -84,6 +84,23 @@
this(new BoundedRational(n));
}
+ public static UnifiedReal valueOf(double x) {
+ if (x == 0.0 || x == 1.0) {
+ return valueOf((long) x);
+ }
+ return new UnifiedReal(BoundedRational.valueOf(x));
+ }
+
+ public static UnifiedReal valueOf(long x) {
+ if (x == 0) {
+ return UnifiedReal.ZERO;
+ } else if (x == 1) {
+ return UnifiedReal.ONE;
+ } else {
+ return new UnifiedReal(BoundedRational.valueOf(x));
+ }
+ }
+
// Various helpful constants
private final static BigInteger BIG_24 = BigInteger.valueOf(24);
private final static int DEFAULT_COMPARE_TOLERANCE = -1000;
@@ -173,8 +190,8 @@
}
/**
- * Given a constructive real cr, try to determine whether cr is the square root of
- * a small integer. If so, return its square as a BoundedRational. Otherwise return null.
+ * Given a constructive real cr, try to determine whether cr is the logarithm of a small
+ * integer. If so, return exp(cr) as a BoundedRational. Otherwise return null.
* We make this determination by simple table lookup, so spurious null returns are
* entirely possible, or even likely.
*/
@@ -419,7 +436,8 @@
/**
* Return a double approximation.
- * TODO: Result is correctly rounded if known to be rational.
+ * Rational arguments are currently rounded to nearest, with ties away from zero.
+ * TODO: Improve rounding.
*/
public double doubleValue() {
if (mCrFactor == CR_ONE) {
@@ -506,11 +524,27 @@
/**
* Returns true if values are definitely known to be equal, false in all other cases.
+ * This does not satisfy the contract for Object.equals().
*/
public boolean definitelyEquals(UnifiedReal u) {
return isComparable(u) && compareTo(u) == 0;
}
+ @Override
+ public int hashCode() {
+ // Better useless than wrong. Probably.
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object r) {
+ if (r == null || !(r instanceof UnifiedReal)) {
+ return false;
+ }
+ // This is almost certainly a programming error. Don't even try.
+ throw new AssertionError("Can't compare UnifiedReals for exact equality");
+ }
+
/**
* Returns true if values are definitely known not to be equal, false in all other cases.
* Performs no approximate evaluation.
@@ -669,7 +703,14 @@
return multiply(u.inverse());
}
+ /**
+ * Return the square root.
+ * This may fail to return a known rational value, even when the result is rational.
+ */
public UnifiedReal sqrt() {
+ if (definitelyZero()) {
+ return ZERO;
+ }
if (mCrFactor == CR_ONE) {
BoundedRational ratSqrt;
// Check for all arguments of the form <perfect rational square> * small_int,
@@ -866,15 +907,23 @@
private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
+ // The (in abs value) integral exponent for which we attempt to use a recursive
+ // algorithm for evaluating pow(). The recursive algorithm works independent of the sign of the
+ // base, and can produce rational results. But it can become slow for very large exponents.
+ private static final BigInteger RECURSIVE_POW_LIMIT = BigInteger.valueOf(1000);
+ // The corresponding limit when we're using rational arithmetic. This should fail fast
+ // anyway, but we avoid ridiculously deep recursion.
+ private static final BigInteger HARD_RECURSIVE_POW_LIMIT = BigInteger.ONE.shiftLeft(1000);
+
/**
- * Compute an integral power of a constrive real, using the standard recursive algorithm.
+ * Compute an integral power of a constructive real, using the standard recursive algorithm.
* exp is known to be positive.
*/
private static CR recursivePow(CR base, BigInteger exp) {
if (exp.equals(BigInteger.ONE)) {
return base;
}
- if (exp.and(BigInteger.ONE).intValue() == 1) {
+ if (exp.testBit(0)) {
return base.multiply(recursivePow(base, exp.subtract(BigInteger.ONE)));
}
CR tmp = recursivePow(base, exp.shiftRight(1));
@@ -885,28 +934,62 @@
}
/**
+ * Compute an integral power of a constructive real, using the exp function when
+ * we safely can. Use recursivePow when we can't. exp is known to be nozero.
+ */
+ private UnifiedReal expLnPow(BigInteger exp) {
+ int sign = signum(DEFAULT_COMPARE_TOLERANCE);
+ if (sign > 0) {
+ // Safe to take the log. This avoids deep recursion for huge exponents, which
+ // may actually make sense here.
+ return new UnifiedReal(crValue().ln().multiply(CR.valueOf(exp)).exp());
+ } else if (sign < 0) {
+ CR result = crValue().negate().ln().multiply(CR.valueOf(exp)).exp();
+ if (exp.testBit(0) /* odd exponent */) {
+ result = result.negate();
+ }
+ return new UnifiedReal(result);
+ } else {
+ // Base of unknown sign with integer exponent. Use a recursive computation.
+ // (Another possible option would be to use the absolute value of the base, and then
+ // adjust the sign at the end. But that would have to be done in the CR
+ // implementation.)
+ if (exp.signum() < 0) {
+ // This may be very expensive if exp.negate() is large.
+ return new UnifiedReal(recursivePow(crValue(), exp.negate()).inverse());
+ } else {
+ return new UnifiedReal(recursivePow(crValue(), exp));
+ }
+ }
+ }
+
+
+ /**
* Compute an integral power of this.
* This recurses roughly as deeply as the number of bits in the exponent, and can, in
* ridiculous cases, result in a stack overflow.
*/
private UnifiedReal pow(BigInteger exp) {
- if (exp.signum() < 0) {
- return pow(exp.negate()).inverse();
- }
if (exp.equals(BigInteger.ONE)) {
return this;
}
if (exp.signum() == 0) {
- // Questionable if base has undefined value. Java.lang.Math.pow() returns 1 anyway,
- // so we do the same.
+ // Questionable if base has undefined value or is 0.
+ // Java.lang.Math.pow() returns 1 anyway, so we do the same.
return ONE;
}
- if (mCrFactor == CR_ONE) {
+ BigInteger absExp = exp.abs();
+ if (mCrFactor == CR_ONE && absExp.compareTo(HARD_RECURSIVE_POW_LIMIT) <= 0) {
final BoundedRational ratPow = mRatFactor.pow(exp);
+ // We count on this to fail, e.g. for very large exponents, when it would
+ // otherwise be too expensive.
if (ratPow != null) {
- return new UnifiedReal(mRatFactor.pow(exp));
+ return new UnifiedReal(ratPow);
}
}
+ if (absExp.compareTo(RECURSIVE_POW_LIMIT) > 0) {
+ return expLnPow(exp);
+ }
BoundedRational square = getSquare(mCrFactor);
if (square != null) {
final BoundedRational nRatFactor =
@@ -920,19 +1003,15 @@
}
}
}
- if (signum(DEFAULT_COMPARE_TOLERANCE) > 0) {
- // Safe to take the log. This avoids deep recursion for huge exponents, which
- // may actually make sense here.
- return new UnifiedReal(crValue().ln().multiply(CR.valueOf(exp)).exp());
- } else {
- // Possibly negative base with integer exponent. Use a recursive computation.
- // (Another possible option would be to use the absolute value of the base, and then
- // adjust the sign at the end. But that would have to be done in the CR
- // implementation.)
- return new UnifiedReal(recursivePow(crValue(), exp));
- }
+ return expLnPow(exp);
}
+ /**
+ * Return this ^ expon.
+ * This is really only well-defined for a positive base, particularly since
+ * 0^x is not continuous at zero. (0^0 = 1 (as is epsilon^0), but 0^epsilon is 0.
+ * We nonetheless try to do reasonable things at zero, when we recognize that case.
+ */
public UnifiedReal pow(UnifiedReal expon) {
if (mCrFactor == CR_E) {
if (mRatFactor.equals(BoundedRational.ONE)) {
@@ -956,6 +1035,14 @@
}
}
}
+ // If the exponent were known zero, we would have handled it above.
+ if (definitelyZero()) {
+ return ZERO;
+ }
+ int sign = signum(DEFAULT_COMPARE_TOLERANCE);
+ if (sign < 0) {
+ throw new ArithmeticException("Negative base for pow() with non-integer exponent");
+ }
return new UnifiedReal(crValue().ln().multiply(expon.crValue()).exp());
}
@@ -964,7 +1051,7 @@
*/
private static long pow16(int n) {
if (n > 10) {
- throw new AssertionError("Unexpexted pow16 argument");
+ throw new AssertionError("Unexpected pow16 argument");
}
long result = n*n;
result *= result;
@@ -1011,6 +1098,9 @@
}
public UnifiedReal ln() {
+ if (mCrFactor == CR_E) {
+ return new UnifiedReal(mRatFactor, CR_ONE).ln().add(ONE);
+ }
if (isComparable(ZERO)) {
if (signum() <= 0) {
throw new ArithmeticException("log(non-positive)");
@@ -1069,9 +1159,6 @@
}
final BoundedRational crExp = getExp(mCrFactor);
if (crExp != null) {
- if (mRatFactor.signum() < 0) {
- return negate().exp().inverse();
- }
boolean needSqrt = false;
BoundedRational ratExponent = mRatFactor;
BigInteger asBI = BoundedRational.asBigInteger(ratExponent);