summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2015-07-09 23:01:56 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-07-09 23:01:56 +0000
commite1343214088af4742438c92b0d96c4ba8bbf0e2d (patch)
tree2da686b8ece2644cd8a7b0aa9de6364a8c8889d8
parent36fe131b66fb2f6ad1efef582f717657d62b3eb1 (diff)
parentc22691223c65a112de898d5e6406a5e30cb5639a (diff)
downloadandroid_packages_apps_ExactCalculator-e1343214088af4742438c92b0d96c4ba8bbf0e2d.tar.gz
android_packages_apps_ExactCalculator-e1343214088af4742438c92b0d96c4ba8bbf0e2d.tar.bz2
android_packages_apps_ExactCalculator-e1343214088af4742438c92b0d96c4ba8bbf0e2d.zip
am c2269122: am 8f4a8c73: am 0b9806f6: Support pasting of scientific notation numbers
* commit 'c22691223c65a112de898d5e6406a5e30cb5639a': Support pasting of scientific notation numbers
-rw-r--r--src/com/android/calculator2/Calculator.java26
-rw-r--r--src/com/android/calculator2/CalculatorExpr.java85
-rw-r--r--src/com/android/calculator2/CalculatorResult.java6
-rw-r--r--src/com/android/calculator2/Evaluator.java55
-rw-r--r--src/com/android/calculator2/KeyMaps.java7
5 files changed, 150 insertions, 29 deletions
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index 5cb6685..f1c9835 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -903,7 +903,7 @@ public class Calculator extends Activity
* are added to mUnprocessedChars, which is presumed to immediately precede the newly
* added characters.
* @param moreChars Characters to be added.
- * @param explicit These characters were explicitly typed by the user.
+ * @param explicit These characters were explicitly typed by the user, not pasted.
*/
private void addChars(String moreChars, boolean explicit) {
if (mUnprocessedChars != null) {
@@ -911,9 +911,33 @@ public class Calculator extends Activity
}
int current = 0;
int len = moreChars.length();
+ boolean lastWasDigit = false;
while (current < len) {
char c = moreChars.charAt(current);
int k = KeyMaps.keyForChar(c);
+ if (!explicit) {
+ int expEnd;
+ if (lastWasDigit && current !=
+ (expEnd = Evaluator.exponentEnd(moreChars, current))) {
+ // Process scientific notation with 'E' when pasting, in spite of ambiguity
+ // with base of natural log.
+ // Otherwise the 10^x key is the user's friend.
+ mEvaluator.addExponent(moreChars, current, expEnd);
+ current = expEnd;
+ lastWasDigit = false;
+ continue;
+ } else {
+ boolean isDigit = KeyMaps.digVal(k) != KeyMaps.NOT_DIGIT;
+ if (current == 0 && (isDigit || k == R.id.dec_point)
+ && mEvaluator.getExpr().hasTrailingConstant()) {
+ // Refuse to concatenate pasted content to trailing constant.
+ // This makes pasting of calculator results more consistent, whether or
+ // not the old calculator instance is still around.
+ addKeyToExpr(R.id.op_mul);
+ }
+ lastWasDigit = (isDigit || lastWasDigit && k == R.id.dec_point);
+ }
+ }
if (k != View.NO_ID) {
mCurrentButton = findViewById(k);
if (explicit) {
diff --git a/src/com/android/calculator2/CalculatorExpr.java b/src/com/android/calculator2/CalculatorExpr.java
index 2162bb2..3023b5c 100644
--- a/src/com/android/calculator2/CalculatorExpr.java
+++ b/src/com/android/calculator2/CalculatorExpr.java
@@ -79,19 +79,22 @@ class CalculatorExpr {
// Supports addition and removal of trailing characters; hence mutable.
private static class Constant extends Token implements Cloneable {
private boolean mSawDecimal;
- String mWhole; // part before decimal point
- private String mFraction; // part after decimal point
+ String mWhole; // String preceding decimal point.
+ private String mFraction; // String after decimal point.
+ private int mExponent; // Explicit exponent, only generated through addExponent.
Constant() {
mWhole = "";
mFraction = "";
- mSawDecimal = false;
+ mSawDecimal = false;
+ mExponent = 0;
};
Constant(DataInput in) throws IOException {
mWhole = in.readUTF();
mSawDecimal = in.readBoolean();
mFraction = in.readUTF();
+ mExponent = in.readInt();
}
@Override
@@ -100,19 +103,33 @@ class CalculatorExpr {
out.writeUTF(mWhole);
out.writeBoolean(mSawDecimal);
out.writeUTF(mFraction);
+ out.writeInt(mExponent);
}
// Given a button press, append corresponding digit.
// We assume id is a digit or decimal point.
// Just return false if this was the second (or later) decimal point
// in this constant.
+ // Assumes that this constant does not have an exponent.
boolean add(int id) {
if (id == R.id.dec_point) {
- if (mSawDecimal) return false;
+ if (mSawDecimal || mExponent != 0) return false;
mSawDecimal = true;
return true;
}
int val = KeyMaps.digVal(id);
+ if (mExponent != 0) {
+ if (Math.abs(mExponent) <= 10000) {
+ if (mExponent > 0) {
+ mExponent = 10 * mExponent + val;
+ } else {
+ mExponent = 10 * mExponent - val;
+ }
+ return true;
+ } else { // Too large; refuse
+ return false;
+ }
+ }
if (mSawDecimal) {
mFraction += val;
} else {
@@ -121,10 +138,18 @@ class CalculatorExpr {
return true;
}
+ void addExponent(int exp) {
+ // Note that adding a 0 exponent is a no-op. That's OK.
+ mExponent = exp;
+ }
+
// Undo the last add.
// Assumes the constant is nonempty.
void delete() {
- if (!mFraction.isEmpty()) {
+ if (mExponent != 0) {
+ mExponent /= 10;
+ // Once zero, it can only be added back with addExponent.
+ } else if (!mFraction.isEmpty()) {
mFraction = mFraction.substring(0, mFraction.length() - 1);
} else if (mSawDecimal) {
mSawDecimal = false;
@@ -146,28 +171,24 @@ class CalculatorExpr {
result += '.';
result += mFraction;
}
- return KeyMaps.translateResult(result);
- }
-
- // Eliminates leading decimal, which some of our
- // other packages don't like.
- // Meant for machine consumption:
- // Doesn't internationalize decimal point or digits.
- public String toEasyString() {
- String result = mWhole;
- if (result.isEmpty()) result = "0";
- if (mSawDecimal) {
- result += '.';
- result += mFraction;
+ if (mExponent != 0) {
+ result += "E" + mExponent;
}
- return result;
+ return KeyMaps.translateResult(result);
}
+ // Return non-null BoundedRational representation.
public BoundedRational toRational() {
String whole = mWhole;
if (whole.isEmpty()) whole = "0";
BigInteger num = new BigInteger(whole + mFraction);
BigInteger den = BigInteger.TEN.pow(mFraction.length());
+ if (mExponent > 0) {
+ num = num.multiply(BigInteger.TEN.pow(mExponent));
+ }
+ if (mExponent < 0) {
+ den = den.multiply(BigInteger.TEN.pow(-mExponent));
+ }
return new BoundedRational(num, den);
}
@@ -186,6 +207,7 @@ class CalculatorExpr {
res.mWhole = mWhole;
res.mFraction = mFraction;
res.mSawDecimal = mSawDecimal;
+ res.mExponent = mExponent;
return res;
}
}
@@ -350,6 +372,15 @@ class CalculatorExpr {
}
}
+ boolean hasTrailingConstant() {
+ int s = mExpr.size();
+ if (s == 0) {
+ return false;
+ }
+ Token t = mExpr.get(s-1);
+ return t instanceof Constant;
+ }
+
private boolean hasTrailingBinary() {
int s = mExpr.size();
if (s == 0) return false;
@@ -410,6 +441,15 @@ class CalculatorExpr {
}
/**
+ * Add exponent to the constant at the end of the expression.
+ * Assumes there is a constant at the end of the expression.
+ */
+ void addExponent(int exp) {
+ Token lastTok = mExpr.get(mExpr.size() - 1);
+ ((Constant) lastTok).addExponent(exp);
+ }
+
+ /**
* Remove trailing op_add and op_sub operators.
*/
void removeTrailingAdditiveOperators() {
@@ -595,18 +635,19 @@ class CalculatorExpr {
// that was not used as part of the evaluation.
private EvalRet evalUnary(int i, EvalContext ec) throws SyntaxException {
Token t = mExpr.get(i);
+ BoundedRational ratVal;
CR value;
if (t instanceof Constant) {
Constant c = (Constant)t;
- value = CR.valueOf(c.toEasyString(),10);
- return new EvalRet(i+1, value, c.toRational());
+ ratVal = c.toRational();
+ value = ratVal.CRValue();
+ return new EvalRet(i+1, value, ratVal);
}
if (t instanceof PreEval) {
PreEval p = (PreEval)t;
return new EvalRet(i+1, p.mValue, p.mRatValue);
}
EvalRet argVal;
- BoundedRational ratVal;
switch(((Operator)(t)).mId) {
case R.id.const_pi:
return new EvalRet(i+1, CR.PI, null);
diff --git a/src/com/android/calculator2/CalculatorResult.java b/src/com/android/calculator2/CalculatorResult.java
index 27f5d09..8d3dfee 100644
--- a/src/com/android/calculator2/CalculatorResult.java
+++ b/src/com/android/calculator2/CalculatorResult.java
@@ -349,7 +349,7 @@ public class CalculatorResult extends AlignedTextView {
// We add ellipses and exponents in a way that leaves most digits in the position they
// would have been in had we not done so.
// This minimizes jumps as a result of scrolling. Result is NOT internationalized,
- // uses "e" for exponent.
+ // uses "E" for exponent.
public String formatResult(String in, int precOffset, int maxDigs, boolean truncated,
boolean negative, int lastDisplayedOffset[], boolean forcePrecision) {
final int minusSpace = negative ? 1 : 0;
@@ -415,7 +415,7 @@ public class CalculatorResult extends AlignedTextView {
result = result.substring(0, result.length() - dropDigits);
lastDisplayedOffset[0] -= dropDigits;
}
- result = result + "e" + Integer.toString(exponent);
+ result = result + "E" + Integer.toString(exponent);
} // else don't add zero exponent
}
if (truncated || negative && result.charAt(0) != '-') {
@@ -504,7 +504,7 @@ public class CalculatorResult extends AlignedTextView {
int maxChars = getMaxChars();
int lastDisplayedOffset[] = new int[1];
String result = getFormattedResult(currentCharOffset, maxChars, lastDisplayedOffset, false);
- int expIndex = result.indexOf('e');
+ int expIndex = result.indexOf('E');
result = KeyMaps.translateResult(result);
if (expIndex > 0 && result.indexOf('.') == -1) {
// Gray out exponent if used as position indicator
diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java
index 9c4346e..4bdc56f 100644
--- a/src/com/android/calculator2/Evaluator.java
+++ b/src/com/android/calculator2/Evaluator.java
@@ -613,7 +613,7 @@ class Evaluator {
}
if (totalDigits <= SHORT_TARGET_LENGTH - 3) {
return negativeSign + cache.charAt(msdIndex) + "."
- + cache.substring(msdIndex + 1, lsdIndex + 1) + "e" + exponent;
+ + cache.substring(msdIndex + 1, lsdIndex + 1) + "E" + exponent;
}
}
// We need to abbreviate.
@@ -624,7 +624,7 @@ class Evaluator {
// Need abbreviation + exponent
return negativeSign + cache.charAt(msdIndex) + "."
+ cache.substring(msdIndex + 1, msdIndex + SHORT_TARGET_LENGTH - negative - 4)
- + KeyMaps.ELLIPSIS + "e" + exponent;
+ + KeyMaps.ELLIPSIS + "E" + exponent;
}
// Return the most significant digit position in the given string
@@ -1008,4 +1008,55 @@ class Evaluator {
return mExpr;
}
+ private static final int MAX_EXP_CHARS = 8;
+
+ /**
+ * Return the index of the character after the exponent starting at s[offset].
+ * Return offset if there is no exponent at that position.
+ * Exponents have syntax E[-]digit* .
+ * "E2" and "E-2" are valid. "E+2" and "e2" are not.
+ * We allow any Unicode digits, and either of the commonly used minus characters.
+ */
+ static int exponentEnd(String s, int offset) {
+ int i = offset;
+ int len = s.length();
+ if (i >= len - 1 || s.charAt(i) != 'E') {
+ return offset;
+ }
+ ++i;
+ if (KeyMaps.keyForChar(s.charAt(i)) == R.id.op_sub) {
+ ++i;
+ }
+ if (i == len || i > offset + MAX_EXP_CHARS || !Character.isDigit(s.charAt(i))) {
+ return offset;
+ }
+ ++i;
+ while (i < len && Character.isDigit(s.charAt(i))) {
+ ++i;
+ }
+ return i;
+ }
+
+ /**
+ * Add the exponent represented by s[begin..end) to the constant at the end of current
+ * expression.
+ * The end of the current expression must be a constant.
+ * Exponents have the same syntax as for exponentEnd().
+ */
+ void addExponent(String s, int begin, int end) {
+ int sign = 1;
+ int exp = 0;
+ int i = begin + 1;
+ // We do the decimal conversion ourselves to exactly match exponentEnd() conventions
+ // and handle various kinds of digits on input. Also avoids allocation.
+ if (KeyMaps.keyForChar(s.charAt(i)) == R.id.op_sub) {
+ sign = -1;
+ ++i;
+ }
+ for (; i < end; ++i) {
+ exp = 10 * exp + Character.digit(s.charAt(i), 10);
+ }
+ mExpr.addExponent(sign * exp);
+ mChangedValue = true;
+ }
}
diff --git a/src/com/android/calculator2/KeyMaps.java b/src/com/android/calculator2/KeyMaps.java
index c28e80d..b4bfdf9 100644
--- a/src/com/android/calculator2/KeyMaps.java
+++ b/src/com/android/calculator2/KeyMaps.java
@@ -184,6 +184,8 @@ public class KeyMaps {
public static final String ELLIPSIS = "\u2026";
+ public static final char MINUS_SIGN = '\u2212';
+
/**
* Map key id to digit or NOT_DIGIT
* Pure function.
@@ -309,12 +311,15 @@ public class KeyMaps {
case ',':
return R.id.dec_point;
case '-':
+ case MINUS_SIGN:
return R.id.op_sub;
case '+':
return R.id.op_add;
case '*':
+ case '\u00D7': // MULTIPLICATION SIGN
return R.id.op_mul;
case '/':
+ case '\u00F7': // DIVISION SIGN
return R.id.op_div;
// We no longer localize function names, so they can't start with an 'e' or 'p'.
case 'e':
@@ -337,7 +342,7 @@ public class KeyMaps {
if (c == mDecimalPt) return R.id.dec_point;
if (c == mPiChar) return R.id.const_pi;
// pi is not translated, but it might be typable on a Greek keyboard,
- // so we check ...
+ // or pasted in, so we check ...
return View.NO_ID;
}
}