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

Commit c023b734 authored by Hans Boehm's avatar Hans Boehm
Browse files

Fix timeout, cancellation and instant display logic

Bug: 20668802
Bug: 20484451
Bug: 20734909
Bug: 20738335

Change the timeout implementation to not rely on removing scheduled
timeouts when an evaluation completes.  This seems simper, more clearly
correct and, unlike its predessessor seems to work reliably.
Correctly display "cancelled" message, but only when cancelled for a
reason other than timeout.

Change SyntaxError to not be a Java "Error", since we routinely recover
from it.

Better support evaluation of partial expressions by removing trailing
operators.  Make some attempt to avoid redundant evaluations, since
they're not as cheap as they used to be.  Change the display logic to
avoid frequent clearing of the display, which resulted in
visible flashing.

Note that the evaluation logic can now stop evaluating before the end
of an expression.

Substantially lengthen timeout used when computing an unrequested
result.  Inverse trig functions can be slow.  See b/20729963.

Make large factorial computations interruptable, as I mistakenly
thought they were.

Ignore "=" on an empty expression.

I'm not positive this fixes the b/20484451, but I haven't been able to
reproduce it with this patch.

I have no idea whether it addresses b/20738335, but it cleans up code
in that vicinity, so it would be interesting to know whether that is
still reproducible.

Change-Id: I67af5afc00e19a6156e1547ce4e2060c70ca367f
parent fbcef700
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ package com.android.calculator2;


import java.math.BigInteger;
import java.math.BigInteger;
import com.hp.creals.CR;
import com.hp.creals.CR;
import com.hp.creals.AbortedError;


public class BoundedRational {
public class BoundedRational {
    // TODO: Maybe eventually make this extend Number?
    // TODO: Maybe eventually make this extend Number?
@@ -441,7 +442,13 @@ public class BoundedRational {
    private static BigInteger genFactorial(long n, long step) {
    private static BigInteger genFactorial(long n, long step) {
        if (n > 4 * step) {
        if (n > 4 * step) {
            BigInteger prod1 = genFactorial(n, 2 * step);
            BigInteger prod1 = genFactorial(n, 2 * step);
            if (Thread.interrupted()) {
                throw new AbortedError();
            }
            BigInteger prod2 = genFactorial(n - step, 2 * step);
            BigInteger prod2 = genFactorial(n - step, 2 * step);
            if (Thread.interrupted()) {
                throw new AbortedError();
            }
            return prod1.multiply(prod2);
            return prod1.multiply(prod2);
        } else {
        } else {
            BigInteger res = BigInteger.valueOf(n);
            BigInteger res = BigInteger.valueOf(n);
+14 −7
Original line number Original line Diff line number Diff line
@@ -379,8 +379,11 @@ public class Calculator extends Activity
        // TODO: Could do this more incrementally.
        // TODO: Could do this more incrementally.
        redisplayFormula();
        redisplayFormula();
        setState(CalculatorState.INPUT);
        setState(CalculatorState.INPUT);
        mResult.clear();
        if (mEvaluator.getExpr().hasInterestingOps()) {
            mEvaluator.evaluateAndShowResult();
            mEvaluator.evaluateAndShowResult();
        } else {
            mResult.clear();
        }
    }
    }


    public void onButtonClick(View view) {
    public void onButtonClick(View view) {
@@ -396,7 +399,6 @@ public class Calculator extends Activity
            return;
            return;
        }
        }



        final int id = view.getId();
        final int id = view.getId();
        switch (id) {
        switch (id) {
            case R.id.eq:
            case R.id.eq:
@@ -419,7 +421,9 @@ public class Calculator extends Activity
                mEvaluator.setDegreeMode(mode);
                mEvaluator.setDegreeMode(mode);
                setState(CalculatorState.INPUT);
                setState(CalculatorState.INPUT);
                mResult.clear();
                mResult.clear();
                if (mEvaluator.getExpr().hasInterestingOps()) {
                    mEvaluator.evaluateAndShowResult();
                    mEvaluator.evaluateAndShowResult();
                }
                break;
                break;
            default:
            default:
                addKeyToExpr(id);
                addKeyToExpr(id);
@@ -472,6 +476,7 @@ public class Calculator extends Activity
        // We should be in EVALUATE state.
        // We should be in EVALUATE state.
        // Display is still in input state.
        // Display is still in input state.
        setState(CalculatorState.INPUT);
        setState(CalculatorState.INPUT);
        mResult.clear();
    }
    }


    // Reevaluation completed; ask result to redisplay current value.
    // Reevaluation completed; ask result to redisplay current value.
@@ -507,7 +512,7 @@ public class Calculator extends Activity
    }
    }


    private void onEquals() {
    private void onEquals() {
        if (mCurrentState == CalculatorState.INPUT) {
        if (mCurrentState == CalculatorState.INPUT && !mEvaluator.getExpr().isEmpty()) {
            setState(CalculatorState.EVALUATE);
            setState(CalculatorState.EVALUATE);
            mEvaluator.requireResult();
            mEvaluator.requireResult();
        }
        }
@@ -526,10 +531,10 @@ public class Calculator extends Activity
            if (len > 0) {
            if (len > 0) {
                mUnprocessedChars = mUnprocessedChars.substring(0, len-1);
                mUnprocessedChars = mUnprocessedChars.substring(0, len-1);
            } else {
            } else {
                mEvaluator.getExpr().delete();
                mEvaluator.delete();
            }
            }
        } else {
        } else {
            mEvaluator.getExpr().delete();
            mEvaluator.delete();
        }
        }
        redisplayAfterFormulaChange();
        redisplayAfterFormulaChange();
    }
    }
@@ -619,6 +624,8 @@ public class Calculator extends Activity
        } else if (mCurrentState == CalculatorState.INIT) {
        } else if (mCurrentState == CalculatorState.INIT) {
            setState(CalculatorState.ERROR);
            setState(CalculatorState.ERROR);
            mResult.displayError(errorResourceId);
            mResult.displayError(errorResourceId);
        } else {
            mResult.clear();
        }
        }
    }
    }


+99 −48
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ import com.hp.creals.PrecisionOverflowError;
import com.hp.creals.AbortedError;
import com.hp.creals.AbortedError;


import android.content.Context;
import android.content.Context;
import android.util.Log;


import java.math.BigInteger;
import java.math.BigInteger;
import java.io.DataInput;
import java.io.DataInput;
@@ -273,7 +274,7 @@ class CalculatorExpr {
            PreEval prev = inMap.get().get(index);
            PreEval prev = inMap.get().get(index);
            if (prev == null) {
            if (prev == null) {
                mExpr = new CalculatorExpr(in);
                mExpr = new CalculatorExpr(in);
                mContext = new EvalContext(in);
                mContext = new EvalContext(in, mExpr.mExpr.size());
                // Recompute other fields
                // Recompute other fields
                // We currently do this in the UI thread, but we
                // We currently do this in the UI thread, but we
                // only create PreEval expressions that were
                // only create PreEval expressions that were
@@ -282,7 +283,14 @@ class CalculatorExpr {
                // constructive real, which involves substantial
                // constructive real, which involves substantial
                // work only in fairly contrived circumstances.
                // work only in fairly contrived circumstances.
                // TODO: Deal better with slow evaluations.
                // TODO: Deal better with slow evaluations.
                EvalRet res = mExpr.evalExpr(0,mContext);
                EvalRet res = null;
                try {
                    res = mExpr.evalExpr(0, mContext);
                } catch (SyntaxException e) {
                    // Should be impossible, since we only write out
                    // expressions that can be evaluated.
                    Log.e("Calculator", "Unexpected syntax exception" + e);
                }
                mValue = res.mVal;
                mValue = res.mVal;
                mRatValue = res.mRatVal;
                mRatValue = res.mRatVal;
                mShortRep = in.readUTF();
                mShortRep = in.readUTF();
@@ -358,7 +366,7 @@ class CalculatorExpr {
        boolean binary = KeyMaps.isBinary(id);
        boolean binary = KeyMaps.isBinary(id);
        if (s == 0 && binary && id != R.id.op_sub) return false;
        if (s == 0 && binary && id != R.id.op_sub) return false;
        if (binary && hasTrailingBinary()
        if (binary && hasTrailingBinary()
            && (id != R.id.op_sub || isOperator(s-1, R.id.op_sub))) {
            && (id != R.id.op_sub || isOperatorUnchecked(s-1, R.id.op_sub))) {
            return false;
            return false;
        }
        }
        boolean isConstPiece = (d != KeyMaps.NOT_DIGIT || id == R.id.dec_point);
        boolean isConstPiece = (d != KeyMaps.NOT_DIGIT || id == R.id.dec_point);
@@ -471,7 +479,7 @@ class CalculatorExpr {
        Token t = new PreEval(val, ratVal,
        Token t = new PreEval(val, ratVal,
                              new CalculatorExpr(
                              new CalculatorExpr(
                                        (ArrayList<Token>)mExpr.clone()),
                                        (ArrayList<Token>)mExpr.clone()),
                              new EvalContext(dm), sr);
                              new EvalContext(dm, mExpr.size()), sr);
        result.mExpr.add(t);
        result.mExpr.add(t);
        return result;
        return result;
    }
    }
@@ -493,15 +501,17 @@ class CalculatorExpr {


    // And take a context argument:
    // And take a context argument:
    private static class EvalContext {
    private static class EvalContext {
        // Memory register contents are not included here,
        public final int mPrefixLength; // Length of prefix to evaluate.
        // since we now make that an explicit part of the expression
                            // Not explicitly saved.
        public final boolean mDegreeMode;
        // If we add any other kinds of evaluation modes, they go here.
        // If we add any other kinds of evaluation modes, they go here.
        boolean mDegreeMode;
        EvalContext(boolean degreeMode, int len) {
        EvalContext(boolean degreeMode) {
            mDegreeMode = degreeMode;
            mDegreeMode = degreeMode;
            mPrefixLength = len;
        }
        }
        EvalContext(DataInput in) throws IOException {
        EvalContext(DataInput in, int len) throws IOException {
            mDegreeMode = in.readBoolean();
            mDegreeMode = in.readBoolean();
            mPrefixLength = len;
        }
        }
        void write(DataOutput out) throws IOException {
        void write(DataOutput out) throws IOException {
            out.writeBoolean(mDegreeMode);
            out.writeBoolean(mDegreeMode);
@@ -532,18 +542,22 @@ class CalculatorExpr {
    // in the event of a syntax error.  We expect that to be caught in
    // in the event of a syntax error.  We expect that to be caught in
    // eval below.
    // eval below.


    private boolean isOperator(int i, int op) {
    private boolean isOperatorUnchecked(int i, int op) {
        if (i >= mExpr.size()) return false;
        Token t = mExpr.get(i);
        Token t = mExpr.get(i);
        if (!(t instanceof Operator)) return false;
        if (!(t instanceof Operator)) return false;
        return ((Operator)(t)).mId == op;
        return ((Operator)(t)).mId == op;
    }
    }


    static class SyntaxError extends Error {
    private boolean isOperator(int i, int op, EvalContext ec) {
        public SyntaxError() {
        if (i >= ec.mPrefixLength) return false;
        return isOperatorUnchecked(i, op);
    }

    static class SyntaxException extends Exception {
        public SyntaxException() {
            super();
            super();
        }
        }
        public SyntaxError(String s) {
        public SyntaxException(String s) {
            super(s);
            super(s);
        }
        }
    }
    }
@@ -553,8 +567,7 @@ class CalculatorExpr {
    // They return both the expression value (as constructive real and,
    // They return both the expression value (as constructive real and,
    // if applicable, as BigInteger) and the position of the next token
    // if applicable, as BigInteger) and the position of the next token
    // that was not used as part of the evaluation.
    // that was not used as part of the evaluation.
    private EvalRet evalUnary(int i, EvalContext ec)
    private EvalRet evalUnary(int i, EvalContext ec) throws SyntaxException {
                    throws ArithmeticException {
        Token t = mExpr.get(i);
        Token t = mExpr.get(i);
        CR value;
        CR value;
        if (t instanceof Constant) {
        if (t instanceof Constant) {
@@ -577,7 +590,7 @@ class CalculatorExpr {
            // Seems to have highest precedence.
            // Seems to have highest precedence.
            // Does not add implicit paren.
            // Does not add implicit paren.
            // Does seem to accept a leading minus.
            // Does seem to accept a leading minus.
            if (isOperator(i+1, R.id.op_sub)) {
            if (isOperator(i+1, R.id.op_sub, ec)) {
                argVal = evalUnary(i+2, ec);
                argVal = evalUnary(i+2, ec);
                ratVal = BoundedRational.sqrt(
                ratVal = BoundedRational.sqrt(
                                BoundedRational.negate(argVal.mRatVal));
                                BoundedRational.negate(argVal.mRatVal));
@@ -592,11 +605,11 @@ class CalculatorExpr {
            }
            }
        case R.id.lparen:
        case R.id.lparen:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            return new EvalRet(argVal.mPos, argVal.mVal, argVal.mRatVal);
            return new EvalRet(argVal.mPos, argVal.mVal, argVal.mRatVal);
        case R.id.fun_sin:
        case R.id.fun_sin:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = ec.mDegreeMode ? BoundedRational.degreeSin(argVal.mRatVal)
            ratVal = ec.mDegreeMode ? BoundedRational.degreeSin(argVal.mRatVal)
                                     : BoundedRational.sin(argVal.mRatVal);
                                     : BoundedRational.sin(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
@@ -604,7 +617,7 @@ class CalculatorExpr {
                    toRadians(argVal.mVal,ec).sin(), null);
                    toRadians(argVal.mVal,ec).sin(), null);
        case R.id.fun_cos:
        case R.id.fun_cos:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = ec.mDegreeMode ? BoundedRational.degreeCos(argVal.mRatVal)
            ratVal = ec.mDegreeMode ? BoundedRational.degreeCos(argVal.mRatVal)
                                     : BoundedRational.cos(argVal.mRatVal);
                                     : BoundedRational.cos(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
@@ -612,7 +625,7 @@ class CalculatorExpr {
                    toRadians(argVal.mVal,ec).cos(), null);
                    toRadians(argVal.mVal,ec).cos(), null);
        case R.id.fun_tan:
        case R.id.fun_tan:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = ec.mDegreeMode ? BoundedRational.degreeTan(argVal.mRatVal)
            ratVal = ec.mDegreeMode ? BoundedRational.degreeTan(argVal.mRatVal)
                                     : BoundedRational.tan(argVal.mRatVal);
                                     : BoundedRational.tan(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
@@ -621,13 +634,13 @@ class CalculatorExpr {
                    argCR.sin().divide(argCR.cos()), null);
                    argCR.sin().divide(argCR.cos()), null);
        case R.id.fun_ln:
        case R.id.fun_ln:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = BoundedRational.ln(argVal.mRatVal);
            ratVal = BoundedRational.ln(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
            return new EvalRet(argVal.mPos, argVal.mVal.ln(), null);
            return new EvalRet(argVal.mPos, argVal.mVal.ln(), null);
        case R.id.fun_log:
        case R.id.fun_log:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = BoundedRational.log(argVal.mRatVal);
            ratVal = BoundedRational.log(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
            return new EvalRet(argVal.mPos,
            return new EvalRet(argVal.mPos,
@@ -635,7 +648,7 @@ class CalculatorExpr {
                               null);
                               null);
        case R.id.fun_arcsin:
        case R.id.fun_arcsin:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = ec.mDegreeMode ? BoundedRational.degreeAsin(argVal.mRatVal)
            ratVal = ec.mDegreeMode ? BoundedRational.degreeAsin(argVal.mRatVal)
                                     : BoundedRational.asin(argVal.mRatVal);
                                     : BoundedRational.asin(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
@@ -645,7 +658,7 @@ class CalculatorExpr {
                               null);
                               null);
        case R.id.fun_arccos:
        case R.id.fun_arccos:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = ec.mDegreeMode ? BoundedRational.degreeAcos(argVal.mRatVal)
            ratVal = ec.mDegreeMode ? BoundedRational.degreeAcos(argVal.mRatVal)
                                     : BoundedRational.acos(argVal.mRatVal);
                                     : BoundedRational.acos(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
@@ -655,7 +668,7 @@ class CalculatorExpr {
                               null);
                               null);
        case R.id.fun_arctan:
        case R.id.fun_arctan:
            argVal = evalExpr(i+1, ec);
            argVal = evalExpr(i+1, ec);
            if (isOperator(argVal.mPos, R.id.rparen)) argVal.mPos++;
            if (isOperator(argVal.mPos, R.id.rparen, ec)) argVal.mPos++;
            ratVal = ec.mDegreeMode ? BoundedRational.degreeAtan(argVal.mRatVal)
            ratVal = ec.mDegreeMode ? BoundedRational.degreeAtan(argVal.mRatVal)
                                     : BoundedRational.atan(argVal.mRatVal);
                                     : BoundedRational.atan(argVal.mRatVal);
            if (ratVal != null) break;
            if (ratVal != null) break;
@@ -664,7 +677,7 @@ class CalculatorExpr {
                                   .atanFunction.execute(argVal.mVal),ec),
                                   .atanFunction.execute(argVal.mVal),ec),
                               null);
                               null);
        default:
        default:
            throw new SyntaxError("Unrecognized token in expression");
            throw new SyntaxException("Unrecognized token in expression");
        }
        }
        // We have a rational value.
        // We have a rational value.
        return new EvalRet(argVal.mPos, ratVal.CRValue(), ratVal);
        return new EvalRet(argVal.mPos, ratVal.CRValue(), ratVal);
@@ -697,12 +710,12 @@ class CalculatorExpr {
        return appr.and(MASK).signum() == 0;
        return appr.and(MASK).signum() == 0;
    }
    }


    private EvalRet evalFactorial(int i, EvalContext ec) {
    private EvalRet evalFactorial(int i, EvalContext ec) throws SyntaxException {
        EvalRet tmp = evalUnary(i, ec);
        EvalRet tmp = evalUnary(i, ec);
        int cpos = tmp.mPos;
        int cpos = tmp.mPos;
        CR cval = tmp.mVal;
        CR cval = tmp.mVal;
        BoundedRational ratVal = tmp.mRatVal;
        BoundedRational ratVal = tmp.mRatVal;
        while (isOperator(cpos, R.id.op_fact)) {
        while (isOperator(cpos, R.id.op_fact, ec)) {
            if (ratVal == null) {
            if (ratVal == null) {
                // Assume it was an integer, but we
                // Assume it was an integer, but we
                // didn't figure it out.
                // didn't figure it out.
@@ -719,13 +732,12 @@ class CalculatorExpr {
        return new EvalRet(cpos, cval, ratVal);
        return new EvalRet(cpos, cval, ratVal);
    }
    }


    private EvalRet evalFactor(int i, EvalContext ec)
    private EvalRet evalFactor(int i, EvalContext ec) throws SyntaxException {
                    throws ArithmeticException {
        final EvalRet result1 = evalFactorial(i, ec);
        final EvalRet result1 = evalFactorial(i, ec);
        int cpos = result1.mPos;  // current position
        int cpos = result1.mPos;  // current position
        CR cval = result1.mVal;   // value so far
        CR cval = result1.mVal;   // value so far
        BoundedRational ratVal = result1.mRatVal;  // int value so far
        BoundedRational ratVal = result1.mRatVal;  // int value so far
        if (isOperator(cpos, R.id.op_pow)) {
        if (isOperator(cpos, R.id.op_pow, ec)) {
            final EvalRet exp = evalSignedFactor(cpos+1, ec);
            final EvalRet exp = evalSignedFactor(cpos+1, ec);
            cpos = exp.mPos;
            cpos = exp.mPos;
            // Try completely rational evaluation first.
            // Try completely rational evaluation first.
@@ -748,9 +760,8 @@ class CalculatorExpr {
        return new EvalRet(cpos, cval, ratVal);
        return new EvalRet(cpos, cval, ratVal);
    }
    }


    private EvalRet evalSignedFactor(int i, EvalContext ec)
    private EvalRet evalSignedFactor(int i, EvalContext ec) throws SyntaxException {
                    throws ArithmeticException {
        final boolean negative = isOperator(i, R.id.op_sub, ec);
        final boolean negative = isOperator(i, R.id.op_sub);
        int cpos = negative ? i + 1 : i;
        int cpos = negative ? i + 1 : i;
        EvalRet tmp = evalFactor(cpos, ec);
        EvalRet tmp = evalFactor(cpos, ec);
        cpos = tmp.mPos;
        cpos = tmp.mPos;
@@ -775,16 +786,15 @@ class CalculatorExpr {
        }
        }
    }
    }


    private EvalRet evalTerm(int i, EvalContext ec)
    private EvalRet evalTerm(int i, EvalContext ec) throws SyntaxException {
                    throws ArithmeticException {
        EvalRet tmp = evalSignedFactor(i, ec);
        EvalRet tmp = evalSignedFactor(i, ec);
        boolean is_mul = false;
        boolean is_mul = false;
        boolean is_div = false;
        boolean is_div = false;
        int cpos = tmp.mPos;   // Current position in expression.
        int cpos = tmp.mPos;   // Current position in expression.
        CR cval = tmp.mVal;    // Current value.
        CR cval = tmp.mVal;    // Current value.
        BoundedRational ratVal = tmp.mRatVal; // Current rational value.
        BoundedRational ratVal = tmp.mRatVal; // Current rational value.
        while ((is_mul = isOperator(cpos, R.id.op_mul))
        while ((is_mul = isOperator(cpos, R.id.op_mul, ec))
               || (is_div = isOperator(cpos, R.id.op_div))
               || (is_div = isOperator(cpos, R.id.op_div, ec))
               || canStartFactor(cpos)) {
               || canStartFactor(cpos)) {
            if (is_mul || is_div) ++cpos;
            if (is_mul || is_div) ++cpos;
            tmp = evalSignedFactor(cpos, ec);
            tmp = evalSignedFactor(cpos, ec);
@@ -809,14 +819,14 @@ class CalculatorExpr {
        return new EvalRet(cpos, cval, ratVal);
        return new EvalRet(cpos, cval, ratVal);
    }
    }


    private EvalRet evalExpr(int i, EvalContext ec) throws ArithmeticException {
    private EvalRet evalExpr(int i, EvalContext ec) throws SyntaxException {
        EvalRet tmp = evalTerm(i, ec);
        EvalRet tmp = evalTerm(i, ec);
        boolean is_plus;
        boolean is_plus;
        int cpos = tmp.mPos;
        int cpos = tmp.mPos;
        CR cval = tmp.mVal;
        CR cval = tmp.mVal;
        BoundedRational ratVal = tmp.mRatVal;
        BoundedRational ratVal = tmp.mRatVal;
        while ((is_plus = isOperator(cpos, R.id.op_add))
        while ((is_plus = isOperator(cpos, R.id.op_add, ec))
               || isOperator(cpos, R.id.op_sub)) {
               || isOperator(cpos, R.id.op_sub, ec)) {
            tmp = evalTerm(cpos+1, ec);
            tmp = evalTerm(cpos+1, ec);
            if (is_plus) {
            if (is_plus) {
                ratVal = BoundedRational.add(ratVal, tmp.mRatVal);
                ratVal = BoundedRational.add(ratVal, tmp.mRatVal);
@@ -848,22 +858,63 @@ class CalculatorExpr {
        final BoundedRational mRatVal;
        final BoundedRational mRatVal;
    }
    }


    // Return the starting position of the sequence of trailing operators
    // that cannot be meaningfully evaluated.
    private int trailingOpsStart() {
        int result = mExpr.size();
        while (result > 0) {
            Token last = mExpr.get(result - 1);
            if (!(last instanceof Operator)) break;
            Operator o = (Operator)last;
            if (KeyMaps.isSuffix(o.mId) || o.mId == R.id.const_pi
                                        || o.mId == R.id.const_e) {
                break;
            }
            --result;
        }
        return result;
    }

    public boolean hasTrailingOperators() {
        return trailingOpsStart() != mExpr.size();
    }

    // Is the current expression worth evaluating?
    public boolean hasInterestingOps() {
        int last = trailingOpsStart();
        int first = 0;
        if (last > first && isOperatorUnchecked(first, R.id.op_sub)) {
            // Leading minus is not by itself interesting.
            first++;
        }
        for (int i = first; i < last; ++i) {
            Token t1 = mExpr.get(i);
            if (!(t1 instanceof Constant)) return true;
            // We consider preevaluated expressions "interesting",
            // since the evaluation will usually result in more precision
            // than the "short representation".
        }
        return false;
    }

    // Evaluate the entire expression, returning null in the event
    // Evaluate the entire expression, returning null in the event
    // of an error.
    // of an error.
    // Not called from the UI thread, but should not be called
    // Not called from the UI thread, but should not be called
    // concurrently with modifications to the expression.
    // concurrently with modifications to the expression.
    EvalResult eval(boolean degreeMode) throws SyntaxError,
    EvalResult eval(boolean degreeMode, boolean required) throws SyntaxException
                        ArithmeticException, PrecisionOverflowError
                        // And unchecked exceptions thrown by CR
                        // and BoundedRational.
    {
    {
        try {
        try {
            EvalContext ec = new EvalContext(degreeMode);
            int prefixLen = required ? mExpr.size() : trailingOpsStart();
            EvalContext ec = new EvalContext(degreeMode, prefixLen);
            EvalRet res = evalExpr(0, ec);
            EvalRet res = evalExpr(0, ec);
            if (res.mPos != mExpr.size()) {
            if (res.mPos != prefixLen) {
                throw new SyntaxError("Failed to parse full expression");
                throw new SyntaxException("Failed to parse full expression");
            }
            }
            return new EvalResult(res.mVal, res.mRatVal);
            return new EvalResult(res.mVal, res.mRatVal);
        } catch (IndexOutOfBoundsException e) {
        } catch (IndexOutOfBoundsException e) {
            throw new SyntaxError("Unexpected expression end");
            throw new SyntaxException("Unexpected expression end");
        }
        }
    }
    }


+105 −57

File changed.

Preview size limit exceeded, changes collapsed.