Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba914452 authored by Hans Boehm's avatar Hans Boehm Committed by Android Git Automerger
Browse files

am 0b9806f6: Support pasting of scientific notation numbers

* commit '0b9806f6':
  Support pasting of scientific notation numbers
parents c615be93 0b9806f6
Loading
Loading
Loading
Loading
+25 −1
Original line number Diff line number Diff line
@@ -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) {
+63 −22
Original line number Diff line number Diff line
@@ -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;
            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;
@@ -409,6 +440,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.
     */
@@ -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);
+3 −3
Original line number Diff line number Diff line
@@ -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
+53 −2
Original line number Diff line number Diff line
@@ -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;
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -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;
        }
    }