summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2015-05-31 12:19:05 -0700
committerHans Boehm <hboehm@google.com>2015-06-02 11:51:09 -0700
commit4db31b490443e4454d98a5ae2bc44b87149accfe (patch)
tree9843640804505c092ea1f883928b5871f7b56591 /src
parente125edff83b7f73cadaa7a0a007cccec6bd059b2 (diff)
downloadandroid_packages_apps_ExactCalculator-4db31b490443e4454d98a5ae2bc44b87149accfe.tar.gz
android_packages_apps_ExactCalculator-4db31b490443e4454d98a5ae2bc44b87149accfe.tar.bz2
android_packages_apps_ExactCalculator-4db31b490443e4454d98a5ae2bc44b87149accfe.zip
Implement percent and new inverse functions
Bug: 21493470 Add x^2 10^x and e^x functions, to make the recently added INV key work as expected. Implement % functionality. 10^x is essentially just macro expansions for now. % and x^2 need trivial evaluator support to provide reasonable display syntax. We decided to add evaluator support for exp() as well. Add corresponding exp() support to BoundedRational and its tests. Tiny incidental changes for problems uncovered in the process: Fix bug in tests/README.txt Evaluate the constant e only once. Add one more power test along with the exp() test. Fix proguard.flags so BRTest runs again. Change-Id: I26cfcaf6d99aeec11387297cc5586e2ddcab6add
Diffstat (limited to 'src')
-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
5 files changed, 91 insertions, 25 deletions
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;