summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2015-06-02 22:22:58 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-06-02 22:22:58 +0000
commit606ffc7915f03f9f1c534f1220911f74917bc44d (patch)
tree5c9924a81521246b97c6039ee73d72562cb6c293
parent1326e667443a55c847fb0359fb3c7808958774bc (diff)
parent694406b52e3bafa9da4396de836e6214e3a9d799 (diff)
downloadandroid_packages_apps_ExactCalculator-606ffc7915f03f9f1c534f1220911f74917bc44d.tar.gz
android_packages_apps_ExactCalculator-606ffc7915f03f9f1c534f1220911f74917bc44d.tar.bz2
android_packages_apps_ExactCalculator-606ffc7915f03f9f1c534f1220911f74917bc44d.zip
am 694406b5: Merge "Implement percent and new inverse functions" into mnc-dev
* commit '694406b52e3bafa9da4396de836e6214e3a9d799': Implement percent and new inverse functions
-rw-r--r--proguard.flags2
-rw-r--r--res/layout/pad_advanced.xml27
-rw-r--r--res/layout/pad_advanced_tablet_port.xml27
-rw-r--r--res/values/strings.xml33
-rw-r--r--src/com/android/calculator2/BoundedRational.java19
-rw-r--r--src/com/android/calculator2/Calculator.java10
-rw-r--r--src/com/android/calculator2/CalculatorExpr.java56
-rw-r--r--src/com/android/calculator2/Evaluator.java24
-rw-r--r--src/com/android/calculator2/KeyMaps.java7
-rw-r--r--tests/README.txt2
-rw-r--r--tests/src/com/android/calculator2/BRTest.java7
11 files changed, 182 insertions, 32 deletions
diff --git a/proguard.flags b/proguard.flags
index 1185cca..0fa387a 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -54,3 +54,5 @@
# Some small BoundedRational methods like equals() are not used by the
# calculator, but crucial for testing.
-keepclassmembers class com.android.calculator2.BoundedRational { *; }
+# Need CR comparison operators for testing.
+-keepclassmembers class com.hp.creals.CR { *; }
diff --git a/res/layout/pad_advanced.xml b/res/layout/pad_advanced.xml
index bd2f30a..0d6168a 100644
--- a/res/layout/pad_advanced.xml
+++ b/res/layout/pad_advanced.xml
@@ -107,6 +107,15 @@
android:text="@string/fun_ln" />
<Button
+ android:id="@+id/fun_exp"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="0"
+ android:contentDescription="@string/desc_fun_exp"
+ android:text="@string/fun_exp"
+ android:visibility="gone" />
+
+ <Button
android:id="@+id/fun_log"
style="@style/PadButtonStyle.Advanced"
android:layout_row="2"
@@ -115,6 +124,15 @@
android:text="@string/fun_log" />
<Button
+ android:id="@+id/fun_10pow"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_fun_10pow"
+ android:text="@string/fun_10pow"
+ android:visibility="gone" />
+
+ <Button
android:id="@+id/op_fact"
style="@style/PadButtonStyle.Advanced"
android:layout_row="2"
@@ -170,4 +188,13 @@
android:contentDescription="@string/desc_op_sqrt"
android:text="@string/op_sqrt" />
+ <Button
+ android:id="@+id/op_sqr"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="4"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_op_sqr"
+ android:text="@string/op_sqr"
+ android:visibility="gone" />
+
</GridLayout>
diff --git a/res/layout/pad_advanced_tablet_port.xml b/res/layout/pad_advanced_tablet_port.xml
index 00b0a70..bccc44c 100644
--- a/res/layout/pad_advanced_tablet_port.xml
+++ b/res/layout/pad_advanced_tablet_port.xml
@@ -107,6 +107,15 @@
android:text="@string/fun_ln" />
<Button
+ android:id="@+id/fun_exp"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_fun_exp"
+ android:text="@string/fun_exp"
+ android:visibility="gone" />
+
+ <Button
android:id="@+id/fun_log"
style="@style/PadButtonStyle.Advanced"
android:layout_row="1"
@@ -115,6 +124,15 @@
android:text="@string/fun_log" />
<Button
+ android:id="@+id/fun_10pow"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_fun_10pow"
+ android:text="@string/fun_10pow"
+ android:visibility="gone" />
+
+ <Button
android:id="@+id/op_fact"
style="@style/PadButtonStyle.Advanced"
android:layout_row="1"
@@ -170,4 +188,13 @@
android:contentDescription="@string/desc_op_sqrt"
android:text="@string/op_sqrt" />
+ <Button
+ android:id="@+id/op_sqr"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="4"
+ android:contentDescription="@string/desc_op_sqr"
+ android:text="@string/op_sqr"
+ android:visibility="gone" />
+
</GridLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3dad1c0..f44ad2d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -78,23 +78,37 @@
<!-- Subtraction operator (e.g. "1 - 2"). [CHAR_LIMIT=1] -->
<string name="op_sub" translatable="false">−</string>
- <!-- Abbrev. name of cosine function (e.g. "cos(π)". [CHAR_LIMIT=4] -->
+ <!-- Abbrev. name of cosine function (e.g. "cos(π)"). [CHAR_LIMIT=4] -->
<string name="fun_cos" translatable="false">cos</string>
<!-- Natural logarithm function (e.g. "ln(2)"). [CHAR_LIMIT=4] -->
<string name="fun_ln" translatable="false">ln</string>
<!-- Logarithm function (e.g. "log(10)"). [CHAR_LIMIT=4] -->
<string name="fun_log" translatable="false">log</string>
- <!-- Abbrev. name of sine function (e.g. "sin(π)". [CHAR_LIMIT=4] -->
+ <!-- Abbrev. name of sine function (e.g. "sin(π)"). [CHAR_LIMIT=4] -->
<string name="fun_sin" translatable="false">sin</string>
- <!-- Abbrev. name of tangent function (e.g. "tan(π)". [CHAR_LIMIT=4] -->
+ <!-- Abbrev. name of tangent function (e.g. "tan(π)"). [CHAR_LIMIT=4] -->
<string name="fun_tan" translatable="false">tan</string>
- <!-- Abbrev. name of cosine function (e.g. "arccos(π)". [CHAR_LIMIT=5] -->
+ <!-- Abbrev. name of cosine function (e.g. "arccos(π)"). [CHAR_LIMIT=5] -->
<string name="fun_arccos" translatable="false">cos\u207B\u00B9</string>
- <!-- Abbrev. name of sine function (e.g. "arcsin(π)". [CHAR_LIMIT=5] -->
+ <!-- Abbrev. name of sine function (e.g. "arcsin(π)"). [CHAR_LIMIT=5] -->
<string name="fun_arcsin" translatable="false">sin\u207B\u00B9</string>
- <!-- Abbrev. name of tangent function (e.g. "arctan(π)". [CHAR_LIMIT=5] -->
+ <!-- Abbrev. name of tangent function (e.g. "arctan(π)"). [CHAR_LIMIT=5] -->
<string name="fun_arctan" translatable="false">tan\u207B\u00B9</string>
+ <!-- Abbrev. name of base 10 exponential function (e.g. "10^6"). [CHAR_LIMIT=5] -->
+ <string name="fun_10pow" translatable="false">10\u02E3</string>
+ <!-- Abbrev. name of exponential function (e.g. "e^6"). [CHAR_LIMIT=5] -->
+ <string name="fun_exp" translatable="false">e\u02E3</string>
+ <!-- Abbrev. name of suffix square function on key (e.g. "17^2"). [CHAR_LIMIT=5] -->
+ <string name="op_sqr" translatable="false">x\u00B2</string>
+ <!--
+ Abbrev. name of suffix square function in formula.
+ "^2" does not work, since it blends into a later constant.
+ -->
+ <string name="squared" translatable="false">²</string>
+ <!-- Abbrev. name of exponential function in formula. -->
+ <string name="exponential" translatable = "false">exp</string>
+
<!-- Abbrev. name of degree mode [CHAR_LIMIT=4] -->
<string name="mode_deg">deg</string>
<!-- Abbrev. name of radian mode. [CHAR_LIMIT=4] -->
@@ -139,6 +153,13 @@
<!-- Content description for 'arctan' button. [CHAR_LIMIT=NONE] -->
<string name="desc_fun_arctan">inverse tangent</string>
+ <!-- Content description for 10^ button. [CHAR_LIMIT=NONE] -->
+ <string name="desc_fun_10pow">ten to the power of</string>
+ <!-- Content description for e^ button. [CHAR_LIMIT=NONE] -->
+ <string name="desc_fun_exp">exponential function</string>
+ <!-- Content description for ^2 button. [CHAR_LIMIT=NONE] -->
+ <string name="desc_op_sqr">squared</string>
+
<!-- Content description for '+' button. [CHAR_LIMIT=NONE] -->
<string name="desc_op_add">plus</string>
<!-- Content description for '÷' button. [CHAR_LIMIT=NONE] -->
diff --git a/src/com/android/calculator2/BoundedRational.java b/src/com/android/calculator2/BoundedRational.java
index ee2ee92..a6dd6d9 100644
--- a/src/com/android/calculator2/BoundedRational.java
+++ b/src/com/android/calculator2/BoundedRational.java
@@ -229,6 +229,14 @@ public class BoundedRational {
return null;
}
+ private static BoundedRational map0to1(BoundedRational r) {
+ if (r == null) return null;
+ if (r.mNum.equals(BigInteger.ZERO)) {
+ return ONE;
+ }
+ return null;
+ }
+
private static BoundedRational map1to0(BoundedRational r) {
if (r == null) return null;
if (r.mNum.equals(r.mDen)) {
@@ -345,12 +353,7 @@ public class BoundedRational {
}
public static BoundedRational cos(BoundedRational r) {
- // Maps 0 to 1, null otherwise
- if (r == null) return null;
- if (r.mNum.equals(BigInteger.ZERO)) {
- return ONE;
- }
- return null;
+ return map0to1(r);
}
public static BoundedRational degreeCos(BoundedRational r) {
@@ -403,6 +406,10 @@ public class BoundedRational {
return map1to0(r);
}
+ public static BoundedRational exp(BoundedRational r) {
+ return map0to1(r);
+ }
+
// Return the base 10 log of n, if n is a power of 10, -1 otherwise.
// n must be positive.
private static long b10Log(BigInteger n) {
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index 0970174..b2cf477 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -227,12 +227,18 @@ public class Calculator extends Activity
mInvertibleButtons = new View[] {
findViewById(R.id.fun_sin),
findViewById(R.id.fun_cos),
- findViewById(R.id.fun_tan)
+ findViewById(R.id.fun_tan),
+ findViewById(R.id.fun_ln),
+ findViewById(R.id.fun_log),
+ findViewById(R.id.op_sqrt)
};
mInverseButtons = new View[] {
findViewById(R.id.fun_arcsin),
findViewById(R.id.fun_arccos),
- findViewById(R.id.fun_arctan)
+ findViewById(R.id.fun_arctan),
+ findViewById(R.id.fun_exp),
+ findViewById(R.id.fun_10pow),
+ findViewById(R.id.op_sqr)
};
mEvaluator = new Evaluator(this, mResultText);
diff --git a/src/com/android/calculator2/CalculatorExpr.java b/src/com/android/calculator2/CalculatorExpr.java
index e5be4b9..6771b52 100644
--- a/src/com/android/calculator2/CalculatorExpr.java
+++ b/src/com/android/calculator2/CalculatorExpr.java
@@ -582,7 +582,7 @@ class CalculatorExpr {
case R.id.const_pi:
return new EvalRet(i+1, CR.PI, null);
case R.id.const_e:
- return new EvalRet(i+1, CR.valueOf(1).exp(), null);
+ return new EvalRet(i+1, REAL_E, null);
case R.id.op_sqrt:
// Seems to have highest precedence.
// Does not add implicit paren.
@@ -635,6 +635,12 @@ class CalculatorExpr {
ratVal = BoundedRational.ln(argVal.mRatVal);
if (ratVal != null) break;
return new EvalRet(argVal.mPos, argVal.mVal.ln(), null);
+ case R.id.fun_exp:
+ argVal = evalExpr(i+1, ec);
+ if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
+ ratVal = BoundedRational.exp(argVal.mRatVal);
+ if (ratVal != null) break;
+ return new EvalRet(argVal.mPos, argVal.mVal.exp(), null);
case R.id.fun_log:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
@@ -702,35 +708,59 @@ class CalculatorExpr {
// Test for integer-ness to 100 bits past binary point.
private static final BigInteger MASK =
BigInteger.ONE.shiftLeft(-TEST_PREC).subtract(BigInteger.ONE);
+ private static final CR REAL_E = CR.valueOf(1).exp();
+ private static final CR REAL_ONE_HUNDREDTH = CR.valueOf(100).inverse();
+ private static final BoundedRational RATIONAL_ONE_HUNDREDTH =
+ new BoundedRational(1,100);
private static boolean isApprInt(CR x) {
BigInteger appr = x.get_appr(TEST_PREC);
return appr.and(MASK).signum() == 0;
}
- private EvalRet evalFactorial(int i, EvalContext ec) throws SyntaxException {
+ private EvalRet evalSuffix(int i, EvalContext ec) throws SyntaxException {
EvalRet tmp = evalUnary(i, ec);
int cpos = tmp.mPos;
CR cval = tmp.mVal;
BoundedRational ratVal = tmp.mRatVal;
- while (isOperator(cpos, R.id.op_fact, ec)) {
- if (ratVal == null) {
- // Assume it was an integer, but we
- // didn't figure it out.
- // KitKat may have used the Gamma function.
- if (!isApprInt(cval)) {
- throw new ArithmeticException("factorial(non-integer)");
+ boolean isFact;
+ boolean isSquared = false;
+ while ((isFact = isOperator(cpos, R.id.op_fact, ec)) ||
+ (isSquared = isOperator(cpos, R.id.op_sqr, ec)) ||
+ isOperator(cpos, R.id.op_pct, ec)) {
+ if (isFact) {
+ if (ratVal == null) {
+ // Assume it was an integer, but we
+ // didn't figure it out.
+ // KitKat may have used the Gamma function.
+ if (!isApprInt(cval)) {
+ throw new ArithmeticException("factorial(non-integer)");
+ }
+ ratVal = new BoundedRational(cval.BigIntegerValue());
+ }
+ ratVal = BoundedRational.fact(ratVal);
+ cval = ratVal.CRValue();
+ } else if (isSquared) {
+ ratVal = BoundedRational.multiply(ratVal, ratVal);
+ if (ratVal == null) {
+ cval = cval.multiply(cval);
+ } else {
+ cval = ratVal.CRValue();
+ }
+ } else /* percent */ {
+ ratVal = BoundedRational.multiply(ratVal, RATIONAL_ONE_HUNDREDTH);
+ if (ratVal == null) {
+ cval = cval.multiply(REAL_ONE_HUNDREDTH);
+ } else {
+ cval = ratVal.CRValue();
}
- ratVal = new BoundedRational(cval.BigIntegerValue());
}
- ratVal = BoundedRational.fact(ratVal);
++cpos;
}
- if (ratVal != null) cval = ratVal.CRValue();
return new EvalRet(cpos, cval, ratVal);
}
private EvalRet evalFactor(int i, EvalContext ec) throws SyntaxException {
- final EvalRet result1 = evalFactorial(i, ec);
+ final EvalRet result1 = evalSuffix(i, ec);
int cpos = result1.mPos; // current position
CR cval = result1.mVal; // value so far
BoundedRational ratVal = result1.mRatVal; // int value so far
diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java
index 4ddaf15..9b9e830 100644
--- a/src/com/android/calculator2/Evaluator.java
+++ b/src/com/android/calculator2/Evaluator.java
@@ -776,10 +776,15 @@ class Evaluator {
// syntax issues, and the expression is unchanged.
// Return true otherwise.
boolean append(int id) {
- mChangedValue = (KeyMaps.digVal(id) != KeyMaps.NOT_DIGIT
- || KeyMaps.isSuffix(id)
- || id == R.id.const_pi || id == R.id.const_e);
- return mExpr.add(id);
+ if (id == R.id.fun_10pow) {
+ add10pow(); // Handled as macro expansion.
+ return true;
+ } else {
+ mChangedValue = (KeyMaps.digVal(id) != KeyMaps.NOT_DIGIT
+ || KeyMaps.isSuffix(id)
+ || id == R.id.const_pi || id == R.id.const_e);
+ return mExpr.add(id);
+ }
}
void delete() {
@@ -865,6 +870,17 @@ class Evaluator {
mExpr.append(mSaved);
}
+ // Add the power of 10 operator to the expression. This is treated
+ // essentially as a macro expansion.
+ private void add10pow() {
+ CalculatorExpr ten = new CalculatorExpr();
+ ten.add(R.id.digit_1);
+ ten.add(R.id.digit_0);
+ mChangedValue = true; // For consistency. Reevaluation is probably not useful.
+ mExpr.append(ten);
+ mExpr.add(R.id.op_pow);
+ }
+
// Retrieve the main expression being edited.
// It is the callee's reponsibility to call cancelAll to cancel
// ongoing concurrent computations before modifying the result.
diff --git a/src/com/android/calculator2/KeyMaps.java b/src/com/android/calculator2/KeyMaps.java
index e3b84e7..5385424 100644
--- a/src/com/android/calculator2/KeyMaps.java
+++ b/src/com/android/calculator2/KeyMaps.java
@@ -67,6 +67,9 @@ public class KeyMaps {
return context.getString(R.string.fun_ln) + context.getString(R.string.lparen);
case R.id.fun_log:
return context.getString(R.string.fun_log) + context.getString(R.string.lparen);
+ case R.id.fun_exp:
+ // Button label doesn't work.
+ return context.getString(R.string.exponential) + context.getString(R.string.lparen);
case R.id.lparen:
return context.getString(R.string.lparen);
case R.id.rparen:
@@ -79,6 +82,9 @@ public class KeyMaps {
return context.getString(R.string.op_div);
case R.id.op_add:
return context.getString(R.string.op_add);
+ case R.id.op_sqr:
+ // Button label doesn't work.
+ return context.getString(R.string.squared);
case R.id.op_sub:
return context.getString(R.string.op_sub);
case R.id.dec_point:
@@ -132,6 +138,7 @@ public class KeyMaps {
switch (id) {
case R.id.op_fact:
case R.id.op_pct:
+ case R.id.op_sqr:
return true;
default:
return false;
diff --git a/tests/README.txt b/tests/README.txt
index 3069de8..b1cd968 100644
--- a/tests/README.txt
+++ b/tests/README.txt
@@ -2,7 +2,7 @@ Run on Android with
1) Build the tests.
2) Install the calculator with
-adb install <tree root>/out/target/product/generic/data/app/ExactCalculatorTests/ExactCalculator.apk
+adb install <tree root>/out/target/product/generic/data/app/ExactCalculator/ExactCalculator.apk
3) adb install <tree root>/out/target/product/generic/data/app/ExactCalculatorTests/ExactCalculatorTests.apk
4) adb shell am instrument -w com.android.exactcalculator.tests/android.test.InstrumentationTestRunner
diff --git a/tests/src/com/android/calculator2/BRTest.java b/tests/src/com/android/calculator2/BRTest.java
index 1cd56a5..29a5ae9 100644
--- a/tests/src/com/android/calculator2/BRTest.java
+++ b/tests/src/com/android/calculator2/BRTest.java
@@ -92,6 +92,13 @@ public class BRTest extends TestCase {
} catch (ArithmeticException ignored) {
check((long_x - 90) % 180 == 0, "exception on defined tan: " + x);
}
+ if (x.compareTo(BoundedRational.THIRTY) <= 0
+ && x.compareTo(BoundedRational.MINUS_THIRTY) >= 0) {
+ checkWeakEq(BoundedRational.exp(x), xAsCR.exp(), "exp:" + x);
+ checkWeakEq(BoundedRational.pow(BR_15, x),
+ CR.valueOf(15).ln().multiply(xAsCR).exp(),
+ "pow(15,x):" + x);
+ }
if (x.compareTo(BoundedRational.ONE) <= 0
&& x.compareTo(BoundedRational.MINUS_ONE) >= 0) {
checkWeakEq(BoundedRational.asin(x), ASIN.execute(xAsCR),