184614957604253d51296e06c97daced699a0a9deHans Boehm/*
2e4579121c8a9c9df22a35944ad5066d38dc22b37Hans Boehm * Copyright (C) 2015 The Android Open Source Project
384614957604253d51296e06c97daced699a0a9deHans Boehm *
484614957604253d51296e06c97daced699a0a9deHans Boehm * Licensed under the Apache License, Version 2.0 (the "License");
584614957604253d51296e06c97daced699a0a9deHans Boehm * you may not use this file except in compliance with the License.
684614957604253d51296e06c97daced699a0a9deHans Boehm * You may obtain a copy of the License at
784614957604253d51296e06c97daced699a0a9deHans Boehm *
884614957604253d51296e06c97daced699a0a9deHans Boehm *   http://www.apache.org/licenses/LICENSE-2.0
984614957604253d51296e06c97daced699a0a9deHans Boehm *
1084614957604253d51296e06c97daced699a0a9deHans Boehm * Unless required by applicable law or agreed to in writing, software
1184614957604253d51296e06c97daced699a0a9deHans Boehm * distributed under the License is distributed on an "AS IS" BASIS,
1284614957604253d51296e06c97daced699a0a9deHans Boehm * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1384614957604253d51296e06c97daced699a0a9deHans Boehm * See the License for the specific language governing permissions and
1484614957604253d51296e06c97daced699a0a9deHans Boehm * limitations under the License.
1584614957604253d51296e06c97daced699a0a9deHans Boehm */
1684614957604253d51296e06c97daced699a0a9deHans Boehm
1784614957604253d51296e06c97daced699a0a9deHans Boehmpackage com.android.calculator2;
1884614957604253d51296e06c97daced699a0a9deHans Boehm
1984614957604253d51296e06c97daced699a0a9deHans Boehm
2084614957604253d51296e06c97daced699a0a9deHans Boehmimport com.hp.creals.CR;
2184614957604253d51296e06c97daced699a0a9deHans Boehmimport com.hp.creals.UnaryCRFunction;
2284614957604253d51296e06c97daced699a0a9deHans Boehm
2384614957604253d51296e06c97daced699a0a9deHans Boehmimport android.content.Context;
248a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehmimport android.text.SpannableString;
258a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehmimport android.text.SpannableStringBuilder;
268a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehmimport android.text.Spanned;
278a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehmimport android.text.style.TtsSpan;
288a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehmimport android.text.style.TtsSpan.TextBuilder;
29c023b734310f231244f17189788ae9c17c90b9a8Hans Boehmimport android.util.Log;
3084614957604253d51296e06c97daced699a0a9deHans Boehm
314a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport java.math.BigInteger;
324a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport java.io.DataInput;
334a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport java.io.DataOutput;
344a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport java.io.IOException;
354a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport java.util.ArrayList;
364a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport java.util.HashMap;
374a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport java.util.IdentityHashMap;
384a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm
393666e6390cea89085f65ba507f6eac346abbf009Hans Boehm/**
403666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * A mathematical expression represented as a sequence of "tokens".
413666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * Many tokens are represented by button ids for the corresponding operator.
423666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * A token may also represent the result of a previously evaluated expression.
433666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * The add() method adds a token to the end of the expression.  The delete method() removes one.
443666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * Clear() deletes the entire expression contents. Eval() evaluates the expression,
453666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * producing both a constructive real (CR), and possibly a BoundedRational result.
463666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * Expressions are parsed only during evaluation; no explicit parse tree is maintained.
473666e6390cea89085f65ba507f6eac346abbf009Hans Boehm *
483666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * The write() method is used to save the current expression.  Note that CR provides no
493666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * serialization facility.  Thus we save all previously computed values by writing out the
503666e6390cea89085f65ba507f6eac346abbf009Hans Boehm * expression that was used to compute them, and reevaluate on input.
513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm */
5284614957604253d51296e06c97daced699a0a9deHans Boehmclass CalculatorExpr {
5384614957604253d51296e06c97daced699a0a9deHans Boehm    private ArrayList<Token> mExpr;  // The actual representation
5484614957604253d51296e06c97daced699a0a9deHans Boehm                                     // as a list of tokens.  Constant
5584614957604253d51296e06c97daced699a0a9deHans Boehm                                     // tokens are always nonempty.
5684614957604253d51296e06c97daced699a0a9deHans Boehm
5784614957604253d51296e06c97daced699a0a9deHans Boehm    private static enum TokenKind { CONSTANT, OPERATOR, PRE_EVAL };
5884614957604253d51296e06c97daced699a0a9deHans Boehm    private static TokenKind[] tokenKindValues = TokenKind.values();
5984614957604253d51296e06c97daced699a0a9deHans Boehm    private final static BigInteger BIG_MILLION = BigInteger.valueOf(1000000);
6084614957604253d51296e06c97daced699a0a9deHans Boehm    private final static BigInteger BIG_BILLION = BigInteger.valueOf(1000000000);
6184614957604253d51296e06c97daced699a0a9deHans Boehm
6284614957604253d51296e06c97daced699a0a9deHans Boehm    private static abstract class Token {
6384614957604253d51296e06c97daced699a0a9deHans Boehm        abstract TokenKind kind();
648a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm
658a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm        /**
668a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm         * Write kind as Byte followed by data needed by subclass constructor.
678a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm         */
6884614957604253d51296e06c97daced699a0a9deHans Boehm        abstract void write(DataOutput out) throws IOException;
698a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm
708a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm        /**
718a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm         * Return a textual representation of the token.
728a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm         * The result is suitable for either display as part od the formula or TalkBack use.
738a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm         * It may be a SpannableString that includes added TalkBack information.
748a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm         * @param context context used for converting button ids to strings
758a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm         */
768a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm        abstract CharSequence toCharSequence(Context context);
7784614957604253d51296e06c97daced699a0a9deHans Boehm    }
7884614957604253d51296e06c97daced699a0a9deHans Boehm
793666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
803666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Representation of an operator token
813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
8284614957604253d51296e06c97daced699a0a9deHans Boehm    private static class Operator extends Token {
833666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final int id; // We use the button resource id
8484614957604253d51296e06c97daced699a0a9deHans Boehm        Operator(int resId) {
853666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            id = resId;
8684614957604253d51296e06c97daced699a0a9deHans Boehm        }
8784614957604253d51296e06c97daced699a0a9deHans Boehm        Operator(DataInput in) throws IOException {
883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            id = in.readInt();
8984614957604253d51296e06c97daced699a0a9deHans Boehm        }
9084614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
9184614957604253d51296e06c97daced699a0a9deHans Boehm        void write(DataOutput out) throws IOException {
9284614957604253d51296e06c97daced699a0a9deHans Boehm            out.writeByte(TokenKind.OPERATOR.ordinal());
933666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            out.writeInt(id);
9484614957604253d51296e06c97daced699a0a9deHans Boehm        }
9584614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
968a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm        public CharSequence toCharSequence(Context context) {
973666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            String desc = KeyMaps.toDescriptiveString(context, id);
988a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm            if (desc != null) {
993666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                SpannableString result = new SpannableString(KeyMaps.toString(context, id));
1008a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm                Object descSpan = new TtsSpan.TextBuilder(desc).build();
1018a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm                result.setSpan(descSpan, 0, result.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
1028a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm                return result;
1038a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm            } else {
1043666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                return KeyMaps.toString(context, id);
1058a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm            }
10684614957604253d51296e06c97daced699a0a9deHans Boehm        }
10784614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
10884614957604253d51296e06c97daced699a0a9deHans Boehm        TokenKind kind() { return TokenKind.OPERATOR; }
10984614957604253d51296e06c97daced699a0a9deHans Boehm    }
11084614957604253d51296e06c97daced699a0a9deHans Boehm
1113666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
1123666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Representation of a (possibly incomplete) numerical constant.
1133666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Supports addition and removal of trailing characters; hence mutable.
1143666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
11584614957604253d51296e06c97daced699a0a9deHans Boehm    private static class Constant extends Token implements Cloneable {
11684614957604253d51296e06c97daced699a0a9deHans Boehm        private boolean mSawDecimal;
1173666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        private String mWhole;  // String preceding decimal point.
1180b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        private String mFraction; // String after decimal point.
1190b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        private int mExponent;  // Explicit exponent, only generated through addExponent.
12084614957604253d51296e06c97daced699a0a9deHans Boehm
12184614957604253d51296e06c97daced699a0a9deHans Boehm        Constant() {
12284614957604253d51296e06c97daced699a0a9deHans Boehm            mWhole = "";
12384614957604253d51296e06c97daced699a0a9deHans Boehm            mFraction = "";
1240b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            mSawDecimal = false;
1250b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            mExponent = 0;
12684614957604253d51296e06c97daced699a0a9deHans Boehm        };
12784614957604253d51296e06c97daced699a0a9deHans Boehm
12884614957604253d51296e06c97daced699a0a9deHans Boehm        Constant(DataInput in) throws IOException {
12984614957604253d51296e06c97daced699a0a9deHans Boehm            mWhole = in.readUTF();
13084614957604253d51296e06c97daced699a0a9deHans Boehm            mSawDecimal = in.readBoolean();
13184614957604253d51296e06c97daced699a0a9deHans Boehm            mFraction = in.readUTF();
1320b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            mExponent = in.readInt();
13384614957604253d51296e06c97daced699a0a9deHans Boehm        }
13484614957604253d51296e06c97daced699a0a9deHans Boehm
13584614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
13684614957604253d51296e06c97daced699a0a9deHans Boehm        void write(DataOutput out) throws IOException {
13784614957604253d51296e06c97daced699a0a9deHans Boehm            out.writeByte(TokenKind.CONSTANT.ordinal());
13884614957604253d51296e06c97daced699a0a9deHans Boehm            out.writeUTF(mWhole);
13984614957604253d51296e06c97daced699a0a9deHans Boehm            out.writeBoolean(mSawDecimal);
14084614957604253d51296e06c97daced699a0a9deHans Boehm            out.writeUTF(mFraction);
1410b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            out.writeInt(mExponent);
14284614957604253d51296e06c97daced699a0a9deHans Boehm        }
14384614957604253d51296e06c97daced699a0a9deHans Boehm
14484614957604253d51296e06c97daced699a0a9deHans Boehm        // Given a button press, append corresponding digit.
14584614957604253d51296e06c97daced699a0a9deHans Boehm        // We assume id is a digit or decimal point.
14684614957604253d51296e06c97daced699a0a9deHans Boehm        // Just return false if this was the second (or later) decimal point
14784614957604253d51296e06c97daced699a0a9deHans Boehm        // in this constant.
1480b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        // Assumes that this constant does not have an exponent.
1493666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public boolean add(int id) {
15084614957604253d51296e06c97daced699a0a9deHans Boehm            if (id == R.id.dec_point) {
1510b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                if (mSawDecimal || mExponent != 0) return false;
15284614957604253d51296e06c97daced699a0a9deHans Boehm                mSawDecimal = true;
15384614957604253d51296e06c97daced699a0a9deHans Boehm                return true;
15484614957604253d51296e06c97daced699a0a9deHans Boehm            }
15584614957604253d51296e06c97daced699a0a9deHans Boehm            int val = KeyMaps.digVal(id);
1560b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            if (mExponent != 0) {
1570b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                if (Math.abs(mExponent) <= 10000) {
1580b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                    if (mExponent > 0) {
1590b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                        mExponent = 10 * mExponent + val;
1600b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                    } else {
1610b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                        mExponent = 10 * mExponent - val;
1620b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                    }
1630b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                    return true;
1640b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                } else {  // Too large; refuse
1650b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                    return false;
1660b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                }
1670b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            }
16884614957604253d51296e06c97daced699a0a9deHans Boehm            if (mSawDecimal) {
16984614957604253d51296e06c97daced699a0a9deHans Boehm                mFraction += val;
17084614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
17184614957604253d51296e06c97daced699a0a9deHans Boehm                mWhole += val;
17284614957604253d51296e06c97daced699a0a9deHans Boehm            }
17384614957604253d51296e06c97daced699a0a9deHans Boehm            return true;
17484614957604253d51296e06c97daced699a0a9deHans Boehm        }
17584614957604253d51296e06c97daced699a0a9deHans Boehm
1763666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public void addExponent(int exp) {
1770b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            // Note that adding a 0 exponent is a no-op.  That's OK.
1780b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            mExponent = exp;
1790b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        }
1800b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm
1813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        /**
1823666e6390cea89085f65ba507f6eac346abbf009Hans Boehm         * Undo the last add or remove last exponent digit.
1833666e6390cea89085f65ba507f6eac346abbf009Hans Boehm         * Assumes the constant is nonempty.
1843666e6390cea89085f65ba507f6eac346abbf009Hans Boehm         */
1853666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public void delete() {
1860b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            if (mExponent != 0) {
1870b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                mExponent /= 10;
1880b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                // Once zero, it can only be added back with addExponent.
1890b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            } else if (!mFraction.isEmpty()) {
19084614957604253d51296e06c97daced699a0a9deHans Boehm                mFraction = mFraction.substring(0, mFraction.length() - 1);
19184614957604253d51296e06c97daced699a0a9deHans Boehm            } else if (mSawDecimal) {
19284614957604253d51296e06c97daced699a0a9deHans Boehm                mSawDecimal = false;
19384614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
19484614957604253d51296e06c97daced699a0a9deHans Boehm                mWhole = mWhole.substring(0, mWhole.length() - 1);
19584614957604253d51296e06c97daced699a0a9deHans Boehm            }
19684614957604253d51296e06c97daced699a0a9deHans Boehm        }
19784614957604253d51296e06c97daced699a0a9deHans Boehm
1983666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public boolean isEmpty() {
19984614957604253d51296e06c97daced699a0a9deHans Boehm            return (mSawDecimal == false && mWhole.isEmpty());
20084614957604253d51296e06c97daced699a0a9deHans Boehm        }
20184614957604253d51296e06c97daced699a0a9deHans Boehm
2023666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        /**
2033666e6390cea89085f65ba507f6eac346abbf009Hans Boehm         * Produce human-readable string representation of constant, as typed.
2043666e6390cea89085f65ba507f6eac346abbf009Hans Boehm         * Result is internationalized.
2053666e6390cea89085f65ba507f6eac346abbf009Hans Boehm         */
20684614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
20784614957604253d51296e06c97daced699a0a9deHans Boehm        public String toString() {
20884614957604253d51296e06c97daced699a0a9deHans Boehm            String result = mWhole;
20984614957604253d51296e06c97daced699a0a9deHans Boehm            if (mSawDecimal) {
210013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm                result += '.';
2114a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm                result += mFraction;
2124a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm            }
2130b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            if (mExponent != 0) {
2140b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                result += "E" + mExponent;
21584614957604253d51296e06c97daced699a0a9deHans Boehm            }
2160b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            return KeyMaps.translateResult(result);
21784614957604253d51296e06c97daced699a0a9deHans Boehm        }
21884614957604253d51296e06c97daced699a0a9deHans Boehm
2193666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        /**
220fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm         * Return BoundedRational representation of constant, if well-formed.
221fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm         * Result is never null.
2223666e6390cea89085f65ba507f6eac346abbf009Hans Boehm         */
223fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm        public BoundedRational toRational() throws SyntaxException {
2244a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm            String whole = mWhole;
225fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm            if (whole.isEmpty()) {
226fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm                if (mFraction.isEmpty()) {
227fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm                    // Decimal point without digits.
228fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm                    throw new SyntaxException();
229fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm                } else {
230fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm                    whole = "0";
231fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm                }
232fa5203c051fb5139c8fc678cae3997c8a7293c84Hans Boehm            }
2334a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm            BigInteger num = new BigInteger(whole + mFraction);
234682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            BigInteger den = BigInteger.TEN.pow(mFraction.length());
2350b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            if (mExponent > 0) {
2360b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                num = num.multiply(BigInteger.TEN.pow(mExponent));
2370b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            }
2380b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            if (mExponent < 0) {
2390b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm                den = den.multiply(BigInteger.TEN.pow(-mExponent));
2400b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            }
241682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            return new BoundedRational(num, den);
242682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm        }
243682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm
24484614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
2453666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public CharSequence toCharSequence(Context context) {
24684614957604253d51296e06c97daced699a0a9deHans Boehm            return toString();
24784614957604253d51296e06c97daced699a0a9deHans Boehm        }
24884614957604253d51296e06c97daced699a0a9deHans Boehm
24984614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
2503666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public TokenKind kind() {
2513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return TokenKind.CONSTANT;
2523666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        }
25384614957604253d51296e06c97daced699a0a9deHans Boehm
25484614957604253d51296e06c97daced699a0a9deHans Boehm        // Override clone to make it public
25584614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
25684614957604253d51296e06c97daced699a0a9deHans Boehm        public Object clone() {
2573666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            Constant result = new Constant();
2583666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            result.mWhole = mWhole;
2593666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            result.mFraction = mFraction;
2603666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            result.mSawDecimal = mSawDecimal;
2613666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            result.mExponent = mExponent;
2623666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return result;
26384614957604253d51296e06c97daced699a0a9deHans Boehm        }
26484614957604253d51296e06c97daced699a0a9deHans Boehm    }
26584614957604253d51296e06c97daced699a0a9deHans Boehm
2663666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // Hash maps used to detect duplicate subexpressions when we write out CalculatorExprs and
2673666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // read them back in.
26884614957604253d51296e06c97daced699a0a9deHans Boehm    private static final ThreadLocal<IdentityHashMap<CR,Integer>>outMap =
2693666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            new ThreadLocal<IdentityHashMap<CR,Integer>>();
27084614957604253d51296e06c97daced699a0a9deHans Boehm        // Maps expressions to indices on output
27184614957604253d51296e06c97daced699a0a9deHans Boehm    private static final ThreadLocal<HashMap<Integer,PreEval>>inMap =
2723666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            new ThreadLocal<HashMap<Integer,PreEval>>();
27384614957604253d51296e06c97daced699a0a9deHans Boehm        // Maps expressions to indices on output
2743666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    private static final ThreadLocal<Integer> exprIndex = new ThreadLocal<Integer>();
27584614957604253d51296e06c97daced699a0a9deHans Boehm
2763666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
2773666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Prepare for expression output.
2783666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Initializes map that will lbe used to avoid duplicating shared subexpressions.
2793666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * This avoids a potential exponential blow-up in the expression size.
2803666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
2813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public static void initExprOutput() {
28284614957604253d51296e06c97daced699a0a9deHans Boehm        outMap.set(new IdentityHashMap<CR,Integer>());
28384614957604253d51296e06c97daced699a0a9deHans Boehm        exprIndex.set(Integer.valueOf(0));
28484614957604253d51296e06c97daced699a0a9deHans Boehm    }
28584614957604253d51296e06c97daced699a0a9deHans Boehm
2863666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
2873666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Prepare for expression input.
2883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Initializes map that will be used to reconstruct shared subexpressions.
2893666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
2903666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public static void initExprInput() {
29184614957604253d51296e06c97daced699a0a9deHans Boehm        inMap.set(new HashMap<Integer,PreEval>());
29284614957604253d51296e06c97daced699a0a9deHans Boehm    }
29384614957604253d51296e06c97daced699a0a9deHans Boehm
2943666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
2953666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * The "token" class for previously evaluated subexpressions.
2963666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * We treat previously evaluated subexpressions as tokens.  These are inserted when we either
2973666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * continue an expression after evaluating some of it, or copy an expression and paste it back
2983666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * in.
2993666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * The representation includes both CR and possibly BoundedRational values.  In order to
3003666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * support saving and restoring, we also include the underlying expression itself, and the
3013666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * context (currently just degree mode) used to evaluate it.  The short string representation
3023666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * is also stored in order to avoid potentially expensive recomputation in the UI thread.
3033666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
30484614957604253d51296e06c97daced699a0a9deHans Boehm    private static class PreEval extends Token {
3053666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final CR value;
3063666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final BoundedRational ratValue;
30784614957604253d51296e06c97daced699a0a9deHans Boehm        private final CalculatorExpr mExpr;
30884614957604253d51296e06c97daced699a0a9deHans Boehm        private final EvalContext mContext;
30950ed320c1cdd1b3624f73956a80eb2f2c2f5a01dHans Boehm        private final String mShortRep;  // Not internationalized.
310013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm        PreEval(CR val, BoundedRational ratVal, CalculatorExpr expr,
311013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm                EvalContext ec, String shortRep) {
3123666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            value = val;
3133666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratValue = ratVal;
31484614957604253d51296e06c97daced699a0a9deHans Boehm            mExpr = expr;
31584614957604253d51296e06c97daced699a0a9deHans Boehm            mContext = ec;
31684614957604253d51296e06c97daced699a0a9deHans Boehm            mShortRep = shortRep;
31784614957604253d51296e06c97daced699a0a9deHans Boehm        }
31884614957604253d51296e06c97daced699a0a9deHans Boehm        // In writing out PreEvals, we are careful to avoid writing
31984614957604253d51296e06c97daced699a0a9deHans Boehm        // out duplicates.  We assume that two expressions are
3208afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        // duplicates if they have the same CR value.  This avoids a
32184614957604253d51296e06c97daced699a0a9deHans Boehm        // potential exponential blow up in certain off cases and
32284614957604253d51296e06c97daced699a0a9deHans Boehm        // redundant evaluation after reading them back in.
32384614957604253d51296e06c97daced699a0a9deHans Boehm        // The parameter hash map maps expressions we've seen
32484614957604253d51296e06c97daced699a0a9deHans Boehm        // before to their index.
32584614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
3263666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public void write(DataOutput out) throws IOException {
32784614957604253d51296e06c97daced699a0a9deHans Boehm            out.writeByte(TokenKind.PRE_EVAL.ordinal());
3283666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            Integer index = outMap.get().get(value);
32984614957604253d51296e06c97daced699a0a9deHans Boehm            if (index == null) {
33084614957604253d51296e06c97daced699a0a9deHans Boehm                int nextIndex = exprIndex.get() + 1;
33184614957604253d51296e06c97daced699a0a9deHans Boehm                exprIndex.set(nextIndex);
3323666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                outMap.get().put(value, nextIndex);
33384614957604253d51296e06c97daced699a0a9deHans Boehm                out.writeInt(nextIndex);
33484614957604253d51296e06c97daced699a0a9deHans Boehm                mExpr.write(out);
33584614957604253d51296e06c97daced699a0a9deHans Boehm                mContext.write(out);
33684614957604253d51296e06c97daced699a0a9deHans Boehm                out.writeUTF(mShortRep);
33784614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
33884614957604253d51296e06c97daced699a0a9deHans Boehm                // Just write out the index
33984614957604253d51296e06c97daced699a0a9deHans Boehm                out.writeInt(index);
34084614957604253d51296e06c97daced699a0a9deHans Boehm            }
34184614957604253d51296e06c97daced699a0a9deHans Boehm        }
34284614957604253d51296e06c97daced699a0a9deHans Boehm        PreEval(DataInput in) throws IOException {
34384614957604253d51296e06c97daced699a0a9deHans Boehm            int index = in.readInt();
34484614957604253d51296e06c97daced699a0a9deHans Boehm            PreEval prev = inMap.get().get(index);
34584614957604253d51296e06c97daced699a0a9deHans Boehm            if (prev == null) {
34684614957604253d51296e06c97daced699a0a9deHans Boehm                mExpr = new CalculatorExpr(in);
347c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                mContext = new EvalContext(in, mExpr.mExpr.size());
3483666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                // Recompute other fields We currently do this in the UI thread, but we only
3493666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                // create PreEval expressions that were previously successfully evaluated, and
3503666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                // thus don't diverge.  We also only evaluate to a constructive real, which
3513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                // involves substantial work only in fairly contrived circumstances.
35284614957604253d51296e06c97daced699a0a9deHans Boehm                // TODO: Deal better with slow evaluations.
353c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                EvalRet res = null;
354c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                try {
355c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                    res = mExpr.evalExpr(0, mContext);
356c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                } catch (SyntaxException e) {
357c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                    // Should be impossible, since we only write out
358c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                    // expressions that can be evaluated.
359c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                    Log.e("Calculator", "Unexpected syntax exception" + e);
360c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                }
3613666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                value = res.val;
3623666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                ratValue = res.ratVal;
36384614957604253d51296e06c97daced699a0a9deHans Boehm                mShortRep = in.readUTF();
36484614957604253d51296e06c97daced699a0a9deHans Boehm                inMap.get().put(index, this);
36584614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
3663666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                value = prev.value;
3673666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                ratValue = prev.ratValue;
36884614957604253d51296e06c97daced699a0a9deHans Boehm                mExpr = prev.mExpr;
36984614957604253d51296e06c97daced699a0a9deHans Boehm                mContext = prev.mContext;
37084614957604253d51296e06c97daced699a0a9deHans Boehm                mShortRep = prev.mShortRep;
37184614957604253d51296e06c97daced699a0a9deHans Boehm            }
37284614957604253d51296e06c97daced699a0a9deHans Boehm        }
37384614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
3743666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public CharSequence toCharSequence(Context context) {
37550ed320c1cdd1b3624f73956a80eb2f2c2f5a01dHans Boehm            return KeyMaps.translateResult(mShortRep);
37684614957604253d51296e06c97daced699a0a9deHans Boehm        }
37784614957604253d51296e06c97daced699a0a9deHans Boehm        @Override
3783666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public TokenKind kind() {
379187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm            return TokenKind.PRE_EVAL;
380187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm        }
3813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public boolean hasEllipsis() {
382187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm            return mShortRep.lastIndexOf(KeyMaps.ELLIPSIS) != -1;
383187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm        }
38484614957604253d51296e06c97daced699a0a9deHans Boehm    }
38584614957604253d51296e06c97daced699a0a9deHans Boehm
3863666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
3873666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Read token from in.
3883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
3893666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public static Token newToken(DataInput in) throws IOException {
39084614957604253d51296e06c97daced699a0a9deHans Boehm        TokenKind kind = tokenKindValues[in.readByte()];
39184614957604253d51296e06c97daced699a0a9deHans Boehm        switch(kind) {
39284614957604253d51296e06c97daced699a0a9deHans Boehm        case CONSTANT:
39384614957604253d51296e06c97daced699a0a9deHans Boehm            return new Constant(in);
39484614957604253d51296e06c97daced699a0a9deHans Boehm        case OPERATOR:
39584614957604253d51296e06c97daced699a0a9deHans Boehm            return new Operator(in);
39684614957604253d51296e06c97daced699a0a9deHans Boehm        case PRE_EVAL:
39784614957604253d51296e06c97daced699a0a9deHans Boehm            return new PreEval(in);
39884614957604253d51296e06c97daced699a0a9deHans Boehm        default: throw new IOException("Bad save file format");
39984614957604253d51296e06c97daced699a0a9deHans Boehm        }
40084614957604253d51296e06c97daced699a0a9deHans Boehm    }
40184614957604253d51296e06c97daced699a0a9deHans Boehm
40284614957604253d51296e06c97daced699a0a9deHans Boehm    CalculatorExpr() {
40384614957604253d51296e06c97daced699a0a9deHans Boehm        mExpr = new ArrayList<Token>();
40484614957604253d51296e06c97daced699a0a9deHans Boehm    }
40584614957604253d51296e06c97daced699a0a9deHans Boehm
40684614957604253d51296e06c97daced699a0a9deHans Boehm    private CalculatorExpr(ArrayList<Token> expr) {
40784614957604253d51296e06c97daced699a0a9deHans Boehm        mExpr = expr;
40884614957604253d51296e06c97daced699a0a9deHans Boehm    }
40984614957604253d51296e06c97daced699a0a9deHans Boehm
4103666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
4113666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Construct CalculatorExpr, by reading it from in.
4123666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
41384614957604253d51296e06c97daced699a0a9deHans Boehm    CalculatorExpr(DataInput in) throws IOException {
41484614957604253d51296e06c97daced699a0a9deHans Boehm        mExpr = new ArrayList<Token>();
41584614957604253d51296e06c97daced699a0a9deHans Boehm        int size = in.readInt();
41684614957604253d51296e06c97daced699a0a9deHans Boehm        for (int i = 0; i < size; ++i) {
41784614957604253d51296e06c97daced699a0a9deHans Boehm            mExpr.add(newToken(in));
41884614957604253d51296e06c97daced699a0a9deHans Boehm        }
41984614957604253d51296e06c97daced699a0a9deHans Boehm    }
42084614957604253d51296e06c97daced699a0a9deHans Boehm
4213666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
4223666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Write this expression to out.
4233666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
4243666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public void write(DataOutput out) throws IOException {
42584614957604253d51296e06c97daced699a0a9deHans Boehm        int size = mExpr.size();
42684614957604253d51296e06c97daced699a0a9deHans Boehm        out.writeInt(size);
42784614957604253d51296e06c97daced699a0a9deHans Boehm        for (int i = 0; i < size; ++i) {
42884614957604253d51296e06c97daced699a0a9deHans Boehm            mExpr.get(i).write(out);
42984614957604253d51296e06c97daced699a0a9deHans Boehm        }
43084614957604253d51296e06c97daced699a0a9deHans Boehm    }
43184614957604253d51296e06c97daced699a0a9deHans Boehm
4323666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
4333666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Does this expression end with a numeric constant?
4343666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * As opposed to an operator or preevaluated expression.
4353666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
4360b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm    boolean hasTrailingConstant() {
4370b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        int s = mExpr.size();
4380b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        if (s == 0) {
4390b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            return false;
4400b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        }
4410b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        Token t = mExpr.get(s-1);
4420b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        return t instanceof Constant;
4430b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm    }
4440b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm
4453666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
4463666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Does this expression end with a binary operator?
4473666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
44884614957604253d51296e06c97daced699a0a9deHans Boehm    private boolean hasTrailingBinary() {
44984614957604253d51296e06c97daced699a0a9deHans Boehm        int s = mExpr.size();
45084614957604253d51296e06c97daced699a0a9deHans Boehm        if (s == 0) return false;
45184614957604253d51296e06c97daced699a0a9deHans Boehm        Token t = mExpr.get(s-1);
45284614957604253d51296e06c97daced699a0a9deHans Boehm        if (!(t instanceof Operator)) return false;
45384614957604253d51296e06c97daced699a0a9deHans Boehm        Operator o = (Operator)t;
4543666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return (KeyMaps.isBinary(o.id));
45584614957604253d51296e06c97daced699a0a9deHans Boehm    }
45684614957604253d51296e06c97daced699a0a9deHans Boehm
457017de9893efd33e179db10bc71189e150bc3486dHans Boehm    /**
458017de9893efd33e179db10bc71189e150bc3486dHans Boehm     * Append press of button with given id to expression.
459017de9893efd33e179db10bc71189e150bc3486dHans Boehm     * If the insertion would clearly result in a syntax error, either just return false
460017de9893efd33e179db10bc71189e150bc3486dHans Boehm     * and do nothing, or make an adjustment to avoid the problem.  We do the latter only
461017de9893efd33e179db10bc71189e150bc3486dHans Boehm     * for unambiguous consecutive binary operators, in which case we delete the first
462017de9893efd33e179db10bc71189e150bc3486dHans Boehm     * operator.
463017de9893efd33e179db10bc71189e150bc3486dHans Boehm     */
46484614957604253d51296e06c97daced699a0a9deHans Boehm    boolean add(int id) {
46584614957604253d51296e06c97daced699a0a9deHans Boehm        int s = mExpr.size();
4663666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        final int d = KeyMaps.digVal(id);
4673666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        final boolean binary = KeyMaps.isBinary(id);
468017de9893efd33e179db10bc71189e150bc3486dHans Boehm        Token lastTok = s == 0 ? null : mExpr.get(s-1);
4693666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        int lastOp = lastTok instanceof Operator ? ((Operator) lastTok).id : 0;
470017de9893efd33e179db10bc71189e150bc3486dHans Boehm        // Quietly replace a trailing binary operator with another one, unless the second
471017de9893efd33e179db10bc71189e150bc3486dHans Boehm        // operator is minus, in which case we just allow it as a unary minus.
472017de9893efd33e179db10bc71189e150bc3486dHans Boehm        if (binary && !KeyMaps.isPrefix(id)) {
473017de9893efd33e179db10bc71189e150bc3486dHans Boehm            if (s == 0 || lastOp == R.id.lparen || KeyMaps.isFunc(lastOp)
474017de9893efd33e179db10bc71189e150bc3486dHans Boehm                    || KeyMaps.isPrefix(lastOp) && lastOp != R.id.op_sub) {
475017de9893efd33e179db10bc71189e150bc3486dHans Boehm                return false;
476017de9893efd33e179db10bc71189e150bc3486dHans Boehm            }
477017de9893efd33e179db10bc71189e150bc3486dHans Boehm            while (hasTrailingBinary()) {
478017de9893efd33e179db10bc71189e150bc3486dHans Boehm                delete();
479017de9893efd33e179db10bc71189e150bc3486dHans Boehm            }
480017de9893efd33e179db10bc71189e150bc3486dHans Boehm            // s invalid and not used below.
48184614957604253d51296e06c97daced699a0a9deHans Boehm        }
4823666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        final boolean isConstPiece = (d != KeyMaps.NOT_DIGIT || id == R.id.dec_point);
48384614957604253d51296e06c97daced699a0a9deHans Boehm        if (isConstPiece) {
484017de9893efd33e179db10bc71189e150bc3486dHans Boehm            // Since we treat juxtaposition as multiplication, a constant can appear anywhere.
48584614957604253d51296e06c97daced699a0a9deHans Boehm            if (s == 0) {
48684614957604253d51296e06c97daced699a0a9deHans Boehm                mExpr.add(new Constant());
48784614957604253d51296e06c97daced699a0a9deHans Boehm                s++;
48884614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
48984614957604253d51296e06c97daced699a0a9deHans Boehm                Token last = mExpr.get(s-1);
49084614957604253d51296e06c97daced699a0a9deHans Boehm                if(!(last instanceof Constant)) {
491017de9893efd33e179db10bc71189e150bc3486dHans Boehm                    if (last instanceof PreEval) {
492017de9893efd33e179db10bc71189e150bc3486dHans Boehm                        // Add explicit multiplication to avoid confusing display.
493017de9893efd33e179db10bc71189e150bc3486dHans Boehm                        mExpr.add(new Operator(R.id.op_mul));
494017de9893efd33e179db10bc71189e150bc3486dHans Boehm                        s++;
49584614957604253d51296e06c97daced699a0a9deHans Boehm                    }
49684614957604253d51296e06c97daced699a0a9deHans Boehm                    mExpr.add(new Constant());
49784614957604253d51296e06c97daced699a0a9deHans Boehm                    s++;
49884614957604253d51296e06c97daced699a0a9deHans Boehm                }
49984614957604253d51296e06c97daced699a0a9deHans Boehm            }
50084614957604253d51296e06c97daced699a0a9deHans Boehm            return ((Constant)(mExpr.get(s-1))).add(id);
50184614957604253d51296e06c97daced699a0a9deHans Boehm        } else {
50284614957604253d51296e06c97daced699a0a9deHans Boehm            mExpr.add(new Operator(id));
50384614957604253d51296e06c97daced699a0a9deHans Boehm            return true;
50484614957604253d51296e06c97daced699a0a9deHans Boehm        }
50584614957604253d51296e06c97daced699a0a9deHans Boehm    }
506017de9893efd33e179db10bc71189e150bc3486dHans Boehm
507017de9893efd33e179db10bc71189e150bc3486dHans Boehm    /**
5080b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm     * Add exponent to the constant at the end of the expression.
5090b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm     * Assumes there is a constant at the end of the expression.
5100b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm     */
5110b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm    void addExponent(int exp) {
5120b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        Token lastTok = mExpr.get(mExpr.size() - 1);
5130b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        ((Constant) lastTok).addExponent(exp);
5140b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm    }
5150b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm
5160b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm    /**
517017de9893efd33e179db10bc71189e150bc3486dHans Boehm     * Remove trailing op_add and op_sub operators.
518017de9893efd33e179db10bc71189e150bc3486dHans Boehm     */
519017de9893efd33e179db10bc71189e150bc3486dHans Boehm    void removeTrailingAdditiveOperators() {
520017de9893efd33e179db10bc71189e150bc3486dHans Boehm        while (true) {
521017de9893efd33e179db10bc71189e150bc3486dHans Boehm            int s = mExpr.size();
5223666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (s == 0) {
5233666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
5243666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
525017de9893efd33e179db10bc71189e150bc3486dHans Boehm            Token lastTok = mExpr.get(s-1);
5263666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (!(lastTok instanceof Operator)) {
5273666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
5283666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
5293666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            int lastOp = ((Operator) lastTok).id;
5303666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (lastOp != R.id.op_add && lastOp != R.id.op_sub) {
5313666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
5323666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
533017de9893efd33e179db10bc71189e150bc3486dHans Boehm            delete();
534017de9893efd33e179db10bc71189e150bc3486dHans Boehm        }
535017de9893efd33e179db10bc71189e150bc3486dHans Boehm    }
53684614957604253d51296e06c97daced699a0a9deHans Boehm
5373666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
5383666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Append the contents of the argument expression.
5393666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * It is assumed that the argument expression will not change, and thus its pieces can be
5403666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * reused directly.
5413666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
5423666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public void append(CalculatorExpr expr2) {
543fbcef7005de4436682072927f83000b502928d25Hans Boehm        int s = mExpr.size();
54484614957604253d51296e06c97daced699a0a9deHans Boehm        int s2 = expr2.mExpr.size();
5453666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        // Check that we're not concatenating Constant or PreEval tokens, since the result would
5463666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        // look like a single constant, with very mysterious results for the user.
547fbcef7005de4436682072927f83000b502928d25Hans Boehm        if (s != 0 && s2 != 0) {
548fbcef7005de4436682072927f83000b502928d25Hans Boehm            Token last = mExpr.get(s-1);
549fbcef7005de4436682072927f83000b502928d25Hans Boehm            Token first = expr2.mExpr.get(0);
550fbcef7005de4436682072927f83000b502928d25Hans Boehm            if (!(first instanceof Operator) && !(last instanceof Operator)) {
5513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                // Fudge it by adding an explicit multiplication.  We would have interpreted it as
5523666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                // such anyway, and this makes it recognizable to the user.
553fbcef7005de4436682072927f83000b502928d25Hans Boehm                mExpr.add(new Operator(R.id.op_mul));
554fbcef7005de4436682072927f83000b502928d25Hans Boehm            }
555fbcef7005de4436682072927f83000b502928d25Hans Boehm        }
55684614957604253d51296e06c97daced699a0a9deHans Boehm        for (int i = 0; i < s2; ++i) {
55784614957604253d51296e06c97daced699a0a9deHans Boehm            mExpr.add(expr2.mExpr.get(i));
55884614957604253d51296e06c97daced699a0a9deHans Boehm        }
55984614957604253d51296e06c97daced699a0a9deHans Boehm    }
56084614957604253d51296e06c97daced699a0a9deHans Boehm
5613666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
5623666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Undo the last key addition, if any.
5633666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Or possibly remove a trailing exponent digit.
5643666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
5653666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public void delete() {
5663666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        final int s = mExpr.size();
5673666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        if (s == 0) {
5683666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return;
5693666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        }
57084614957604253d51296e06c97daced699a0a9deHans Boehm        Token last = mExpr.get(s-1);
57184614957604253d51296e06c97daced699a0a9deHans Boehm        if (last instanceof Constant) {
57284614957604253d51296e06c97daced699a0a9deHans Boehm            Constant c = (Constant)last;
57384614957604253d51296e06c97daced699a0a9deHans Boehm            c.delete();
5743666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (!c.isEmpty()) {
5753666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                return;
5763666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
57784614957604253d51296e06c97daced699a0a9deHans Boehm        }
57884614957604253d51296e06c97daced699a0a9deHans Boehm        mExpr.remove(s-1);
57984614957604253d51296e06c97daced699a0a9deHans Boehm    }
58084614957604253d51296e06c97daced699a0a9deHans Boehm
5813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
5823666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Remove all tokens from the expression.
5833666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
5843666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public void clear() {
58584614957604253d51296e06c97daced699a0a9deHans Boehm        mExpr.clear();
58684614957604253d51296e06c97daced699a0a9deHans Boehm    }
58784614957604253d51296e06c97daced699a0a9deHans Boehm
5883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public boolean isEmpty() {
58984614957604253d51296e06c97daced699a0a9deHans Boehm        return mExpr.isEmpty();
59084614957604253d51296e06c97daced699a0a9deHans Boehm    }
59184614957604253d51296e06c97daced699a0a9deHans Boehm
5923666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
5933666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Returns a logical deep copy of the CalculatorExpr.
5943666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Operator and PreEval tokens are immutable, and thus aren't really copied.
5953666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
59684614957604253d51296e06c97daced699a0a9deHans Boehm    public Object clone() {
5973666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        CalculatorExpr result = new CalculatorExpr();
59884614957604253d51296e06c97daced699a0a9deHans Boehm        for (Token t: mExpr) {
59984614957604253d51296e06c97daced699a0a9deHans Boehm            if (t instanceof Constant) {
6003666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                result.mExpr.add((Token)(((Constant)t).clone()));
60184614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
6023666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                result.mExpr.add(t);
60384614957604253d51296e06c97daced699a0a9deHans Boehm            }
60484614957604253d51296e06c97daced699a0a9deHans Boehm        }
6053666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return result;
60684614957604253d51296e06c97daced699a0a9deHans Boehm    }
60784614957604253d51296e06c97daced699a0a9deHans Boehm
60884614957604253d51296e06c97daced699a0a9deHans Boehm    // Am I just a constant?
6093666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public boolean isConstant() {
6103666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        if (mExpr.size() != 1) {
6113666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return false;
6123666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        }
61384614957604253d51296e06c97daced699a0a9deHans Boehm        return mExpr.get(0) instanceof Constant;
61484614957604253d51296e06c97daced699a0a9deHans Boehm    }
61584614957604253d51296e06c97daced699a0a9deHans Boehm
6163666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
6173666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Return a new expression consisting of a single token representing the current pre-evaluated
6183666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * expression.
6193666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * The caller supplies the value, degree mode, and short string representation, which must
6203666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * have been previously computed.  Thus this is guaranteed to terminate reasonably quickly.
6213666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
6223666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public CalculatorExpr abbreviate(CR val, BoundedRational ratVal,
62384614957604253d51296e06c97daced699a0a9deHans Boehm                              boolean dm, String sr) {
62484614957604253d51296e06c97daced699a0a9deHans Boehm        CalculatorExpr result = new CalculatorExpr();
6253666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        Token t = new PreEval(val, ratVal, new CalculatorExpr((ArrayList<Token>) mExpr.clone()),
6263666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                new EvalContext(dm, mExpr.size()), sr);
62784614957604253d51296e06c97daced699a0a9deHans Boehm        result.mExpr.add(t);
62884614957604253d51296e06c97daced699a0a9deHans Boehm        return result;
62984614957604253d51296e06c97daced699a0a9deHans Boehm    }
63084614957604253d51296e06c97daced699a0a9deHans Boehm
6313666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
6323666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Internal evaluation functions return an EvalRet triple.
6333666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * We compute rational (BoundedRational) results when possible, both as a performance
6343666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * optimization, and to detect errors exactly when we can.
6353666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
6363666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    private static class EvalRet {
6373666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public int pos; // Next position (expression index) to be parsed.
6383666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final CR val; // Constructive Real result of evaluating subexpression.
6393666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final BoundedRational ratVal;  // Exact Rational value or null.
640682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm        EvalRet(int p, CR v, BoundedRational r) {
6413666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            pos = p;
6423666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            val = v;
6433666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = r;
64484614957604253d51296e06c97daced699a0a9deHans Boehm        }
64584614957604253d51296e06c97daced699a0a9deHans Boehm    }
64684614957604253d51296e06c97daced699a0a9deHans Boehm
6473666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
6483666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Internal evaluation functions take an EvalContext argument.
6493666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
65084614957604253d51296e06c97daced699a0a9deHans Boehm    private static class EvalContext {
6513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final int mPrefixLength; // Length of prefix to evaluate. Not explicitly saved.
652c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        public final boolean mDegreeMode;
65384614957604253d51296e06c97daced699a0a9deHans Boehm        // If we add any other kinds of evaluation modes, they go here.
654c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        EvalContext(boolean degreeMode, int len) {
65584614957604253d51296e06c97daced699a0a9deHans Boehm            mDegreeMode = degreeMode;
656c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            mPrefixLength = len;
65784614957604253d51296e06c97daced699a0a9deHans Boehm        }
658c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        EvalContext(DataInput in, int len) throws IOException {
65984614957604253d51296e06c97daced699a0a9deHans Boehm            mDegreeMode = in.readBoolean();
660c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            mPrefixLength = len;
66184614957604253d51296e06c97daced699a0a9deHans Boehm        }
66284614957604253d51296e06c97daced699a0a9deHans Boehm        void write(DataOutput out) throws IOException {
66384614957604253d51296e06c97daced699a0a9deHans Boehm            out.writeBoolean(mDegreeMode);
66484614957604253d51296e06c97daced699a0a9deHans Boehm        }
66584614957604253d51296e06c97daced699a0a9deHans Boehm    }
66684614957604253d51296e06c97daced699a0a9deHans Boehm
66784614957604253d51296e06c97daced699a0a9deHans Boehm    private final CR RADIANS_PER_DEGREE = CR.PI.divide(CR.valueOf(180));
66884614957604253d51296e06c97daced699a0a9deHans Boehm
66984614957604253d51296e06c97daced699a0a9deHans Boehm    private final CR DEGREES_PER_RADIAN = CR.valueOf(180).divide(CR.PI);
67084614957604253d51296e06c97daced699a0a9deHans Boehm
67184614957604253d51296e06c97daced699a0a9deHans Boehm    private CR toRadians(CR x, EvalContext ec) {
67284614957604253d51296e06c97daced699a0a9deHans Boehm        if (ec.mDegreeMode) {
67384614957604253d51296e06c97daced699a0a9deHans Boehm            return x.multiply(RADIANS_PER_DEGREE);
67484614957604253d51296e06c97daced699a0a9deHans Boehm        } else {
67584614957604253d51296e06c97daced699a0a9deHans Boehm            return x;
67684614957604253d51296e06c97daced699a0a9deHans Boehm        }
67784614957604253d51296e06c97daced699a0a9deHans Boehm    }
67884614957604253d51296e06c97daced699a0a9deHans Boehm
67984614957604253d51296e06c97daced699a0a9deHans Boehm    private CR fromRadians(CR x, EvalContext ec) {
68084614957604253d51296e06c97daced699a0a9deHans Boehm        if (ec.mDegreeMode) {
68184614957604253d51296e06c97daced699a0a9deHans Boehm            return x.multiply(DEGREES_PER_RADIAN);
68284614957604253d51296e06c97daced699a0a9deHans Boehm        } else {
68384614957604253d51296e06c97daced699a0a9deHans Boehm            return x;
68484614957604253d51296e06c97daced699a0a9deHans Boehm        }
68584614957604253d51296e06c97daced699a0a9deHans Boehm    }
68684614957604253d51296e06c97daced699a0a9deHans Boehm
6873666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // The following methods can all throw IndexOutOfBoundsException in the event of a syntax
6883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // error.  We expect that to be caught in eval below.
68984614957604253d51296e06c97daced699a0a9deHans Boehm
690c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    private boolean isOperatorUnchecked(int i, int op) {
69184614957604253d51296e06c97daced699a0a9deHans Boehm        Token t = mExpr.get(i);
6923666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        if (!(t instanceof Operator)) {
6933666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return false;
6943666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        }
6953666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return ((Operator)(t)).id == op;
69684614957604253d51296e06c97daced699a0a9deHans Boehm    }
69784614957604253d51296e06c97daced699a0a9deHans Boehm
698c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    private boolean isOperator(int i, int op, EvalContext ec) {
6993666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        if (i >= ec.mPrefixLength) {
7003666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return false;
7013666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        }
702c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        return isOperatorUnchecked(i, op);
703c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    }
704c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm
7053666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public static class SyntaxException extends Exception {
706c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        public SyntaxException() {
70784614957604253d51296e06c97daced699a0a9deHans Boehm            super();
70884614957604253d51296e06c97daced699a0a9deHans Boehm        }
709c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        public SyntaxException(String s) {
71084614957604253d51296e06c97daced699a0a9deHans Boehm            super(s);
71184614957604253d51296e06c97daced699a0a9deHans Boehm        }
71284614957604253d51296e06c97daced699a0a9deHans Boehm    }
71384614957604253d51296e06c97daced699a0a9deHans Boehm
7143666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // The following functions all evaluate some kind of expression starting at position i in
7153666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // mExpr in a specified evaluation context.  They return both the expression value (as
7163666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // constructive real and, if applicable, as BoundedRational) and the position of the next token
71784614957604253d51296e06c97daced699a0a9deHans Boehm    // that was not used as part of the evaluation.
7183666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // This is essentially a simple recursive descent parser combined with expression evaluation.
7193666e6390cea89085f65ba507f6eac346abbf009Hans Boehm
720c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    private EvalRet evalUnary(int i, EvalContext ec) throws SyntaxException {
7213666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        final Token t = mExpr.get(i);
7220b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm        BoundedRational ratVal;
72384614957604253d51296e06c97daced699a0a9deHans Boehm        if (t instanceof Constant) {
72484614957604253d51296e06c97daced699a0a9deHans Boehm            Constant c = (Constant)t;
7250b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm            ratVal = c.toRational();
7263666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(i+1, ratVal.CRValue(), ratVal);
72784614957604253d51296e06c97daced699a0a9deHans Boehm        }
72884614957604253d51296e06c97daced699a0a9deHans Boehm        if (t instanceof PreEval) {
7293666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            final PreEval p = (PreEval)t;
7303666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(i+1, p.value, p.ratValue);
73184614957604253d51296e06c97daced699a0a9deHans Boehm        }
73284614957604253d51296e06c97daced699a0a9deHans Boehm        EvalRet argVal;
7333666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        switch(((Operator)(t)).id) {
73484614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.const_pi:
73584614957604253d51296e06c97daced699a0a9deHans Boehm            return new EvalRet(i+1, CR.PI, null);
73684614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.const_e:
7374db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm            return new EvalRet(i+1, REAL_E, null);
73884614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.op_sqrt:
739fbcef7005de4436682072927f83000b502928d25Hans Boehm            // Seems to have highest precedence.
740fbcef7005de4436682072927f83000b502928d25Hans Boehm            // Does not add implicit paren.
741fbcef7005de4436682072927f83000b502928d25Hans Boehm            // Does seem to accept a leading minus.
742c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            if (isOperator(i+1, R.id.op_sub, ec)) {
743fbcef7005de4436682072927f83000b502928d25Hans Boehm                argVal = evalUnary(i+2, ec);
7443666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                ratVal = BoundedRational.sqrt(BoundedRational.negate(argVal.ratVal));
7453666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                if (ratVal != null) {
7463666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    break;
7473666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                }
7483666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                return new EvalRet(argVal.pos,
7493666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                   argVal.val.negate().sqrt(), null);
750fbcef7005de4436682072927f83000b502928d25Hans Boehm            } else {
751fbcef7005de4436682072927f83000b502928d25Hans Boehm                argVal = evalUnary(i+1, ec);
7523666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                ratVal = BoundedRational.sqrt(argVal.ratVal);
7533666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                if (ratVal != null) {
7543666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    break;
7553666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                }
7563666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                return new EvalRet(argVal.pos, argVal.val.sqrt(), null);
757fbcef7005de4436682072927f83000b502928d25Hans Boehm            }
75884614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.lparen:
75984614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
7603666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
7613666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
7623666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
7633666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos, argVal.val, argVal.ratVal);
76484614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_sin:
76584614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
7663666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
7673666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
7683666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
7693666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = ec.mDegreeMode ? BoundedRational.degreeSin(argVal.ratVal)
7703666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                     : BoundedRational.sin(argVal.ratVal);
7713666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
7723666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
7733666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
7743666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos, toRadians(argVal.val,ec).sin(), null);
77584614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_cos:
77684614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
7773666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
7783666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
7793666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
7803666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = ec.mDegreeMode ? BoundedRational.degreeCos(argVal.ratVal)
7813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                     : BoundedRational.cos(argVal.ratVal);
7823666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
7833666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
7843666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
7853666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos, toRadians(argVal.val,ec).cos(), null);
78684614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_tan:
78784614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
7883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
7893666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
7903666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
7913666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = ec.mDegreeMode ? BoundedRational.degreeTan(argVal.ratVal)
7923666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                     : BoundedRational.tan(argVal.ratVal);
7933666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
7943666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
7953666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
7963666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            CR argCR = toRadians(argVal.val, ec);
7973666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos, argCR.sin().divide(argCR.cos()), null);
79884614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_ln:
79984614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
8003666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
8013666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
8023666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8033666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = BoundedRational.ln(argVal.ratVal);
8043666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
8053666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
8063666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8073666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos, argVal.val.ln(), null);
8084db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm        case R.id.fun_exp:
8094db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm            argVal = evalExpr(i+1, ec);
8103666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
8113666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
8123666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8133666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = BoundedRational.exp(argVal.ratVal);
8143666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
8153666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
8163666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8173666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos, argVal.val.exp(), null);
81884614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_log:
81984614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
8203666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
8213666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
8223666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8233666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = BoundedRational.log(argVal.ratVal);
8243666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
8253666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
8263666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8273666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos, argVal.val.ln().divide(CR.valueOf(10).ln()), null);
82884614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_arcsin:
82984614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
8303666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
8313666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
8323666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8333666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = ec.mDegreeMode ? BoundedRational.degreeAsin(argVal.ratVal)
8343666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                     : BoundedRational.asin(argVal.ratVal);
8353666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
8363666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
8373666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8383666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos,
8393666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    fromRadians(UnaryCRFunction.asinFunction.execute(argVal.val),ec), null);
84084614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_arccos:
84184614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
8423666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
8433666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
8443666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8453666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = ec.mDegreeMode ? BoundedRational.degreeAcos(argVal.ratVal)
8463666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                     : BoundedRational.acos(argVal.ratVal);
8473666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
8483666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
8493666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8503666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos,
8513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    fromRadians(UnaryCRFunction.acosFunction.execute(argVal.val),ec), null);
85284614957604253d51296e06c97daced699a0a9deHans Boehm        case R.id.fun_arctan:
85384614957604253d51296e06c97daced699a0a9deHans Boehm            argVal = evalExpr(i+1, ec);
8543666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (isOperator(argVal.pos, R.id.rparen, ec)) {
8553666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                argVal.pos++;
8563666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8573666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = ec.mDegreeMode ? BoundedRational.degreeAtan(argVal.ratVal)
8583666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                     : BoundedRational.atan(argVal.ratVal);
8593666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (ratVal != null) {
8603666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                break;
8613666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            }
8623666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalRet(argVal.pos,
8633666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    fromRadians(UnaryCRFunction.atanFunction.execute(argVal.val),ec), null);
86484614957604253d51296e06c97daced699a0a9deHans Boehm        default:
865c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            throw new SyntaxException("Unrecognized token in expression");
86684614957604253d51296e06c97daced699a0a9deHans Boehm        }
867682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm        // We have a rational value.
8683666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return new EvalRet(argVal.pos, ratVal.CRValue(), ratVal);
86984614957604253d51296e06c97daced699a0a9deHans Boehm    }
87084614957604253d51296e06c97daced699a0a9deHans Boehm
8713666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
8723666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Compute an integral power of a constructive real.
8733666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Unlike the "general" case using logarithms, this handles a negative base.
8743666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
87584614957604253d51296e06c97daced699a0a9deHans Boehm    private static CR pow(CR base, BigInteger exp) {
87684614957604253d51296e06c97daced699a0a9deHans Boehm        if (exp.compareTo(BigInteger.ZERO) < 0) {
87784614957604253d51296e06c97daced699a0a9deHans Boehm            return pow(base, exp.negate()).inverse();
87884614957604253d51296e06c97daced699a0a9deHans Boehm        }
8793666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        if (exp.equals(BigInteger.ONE)) {
8803666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return base;
8813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        }
88284614957604253d51296e06c97daced699a0a9deHans Boehm        if (exp.and(BigInteger.ONE).intValue() == 1) {
88384614957604253d51296e06c97daced699a0a9deHans Boehm            return pow(base, exp.subtract(BigInteger.ONE)).multiply(base);
88484614957604253d51296e06c97daced699a0a9deHans Boehm        }
88584614957604253d51296e06c97daced699a0a9deHans Boehm        if (exp.equals(BigInteger.ZERO)) {
88684614957604253d51296e06c97daced699a0a9deHans Boehm            return CR.valueOf(1);
88784614957604253d51296e06c97daced699a0a9deHans Boehm        }
88884614957604253d51296e06c97daced699a0a9deHans Boehm        CR tmp = pow(base, exp.shiftRight(1));
88984614957604253d51296e06c97daced699a0a9deHans Boehm        return tmp.multiply(tmp);
89084614957604253d51296e06c97daced699a0a9deHans Boehm    }
89184614957604253d51296e06c97daced699a0a9deHans Boehm
8923666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    // Number of bits past binary point to test for integer-ness.
893682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm    private static final int TEST_PREC = -100;
894682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm    private static final BigInteger MASK =
895682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            BigInteger.ONE.shiftLeft(-TEST_PREC).subtract(BigInteger.ONE);
8964db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm    private static final CR REAL_E = CR.valueOf(1).exp();
8974db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm    private static final CR REAL_ONE_HUNDREDTH = CR.valueOf(100).inverse();
8983666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    private static final BoundedRational RATIONAL_ONE_HUNDREDTH = new BoundedRational(1,100);
899682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm    private static boolean isApprInt(CR x) {
900682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm        BigInteger appr = x.get_appr(TEST_PREC);
901682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm        return appr.and(MASK).signum() == 0;
902682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm    }
903682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm
9044db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm    private EvalRet evalSuffix(int i, EvalContext ec) throws SyntaxException {
9053666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        final EvalRet tmp = evalUnary(i, ec);
9063666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        int cpos = tmp.pos;
9073666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        CR crVal = tmp.val;
9083666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        BoundedRational ratVal = tmp.ratVal;
9094db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm        boolean isFact;
9104db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm        boolean isSquared = false;
9114db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm        while ((isFact = isOperator(cpos, R.id.op_fact, ec)) ||
9124db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                (isSquared = isOperator(cpos, R.id.op_sqr, ec)) ||
9134db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                isOperator(cpos, R.id.op_pct, ec)) {
9144db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm            if (isFact) {
9154db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                if (ratVal == null) {
9163666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    // Assume it was an integer, but we didn't figure it out.
9174db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                    // KitKat may have used the Gamma function.
9183666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    if (!isApprInt(crVal)) {
9194db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                        throw new ArithmeticException("factorial(non-integer)");
9204db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                    }
9213666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    ratVal = new BoundedRational(crVal.BigIntegerValue());
9224db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                }
9234db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                ratVal = BoundedRational.fact(ratVal);
9243666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                crVal = ratVal.CRValue();
9254db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm            } else if (isSquared) {
9264db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                ratVal = BoundedRational.multiply(ratVal, ratVal);
9274db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                if (ratVal == null) {
9283666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = crVal.multiply(crVal);
9294db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                } else {
9303666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = ratVal.CRValue();
9314db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                }
9324db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm            } else /* percent */ {
9334db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                ratVal = BoundedRational.multiply(ratVal, RATIONAL_ONE_HUNDREDTH);
9344db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                if (ratVal == null) {
9353666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = crVal.multiply(REAL_ONE_HUNDREDTH);
9364db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm                } else {
9373666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = ratVal.CRValue();
938682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                }
93984614957604253d51296e06c97daced699a0a9deHans Boehm            }
94084614957604253d51296e06c97daced699a0a9deHans Boehm            ++cpos;
94184614957604253d51296e06c97daced699a0a9deHans Boehm        }
9423666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return new EvalRet(cpos, crVal, ratVal);
94384614957604253d51296e06c97daced699a0a9deHans Boehm    }
94484614957604253d51296e06c97daced699a0a9deHans Boehm
945c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    private EvalRet evalFactor(int i, EvalContext ec) throws SyntaxException {
9464db31b490443e4454d98a5ae2bc44b87149accfeHans Boehm        final EvalRet result1 = evalSuffix(i, ec);
9473666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        int cpos = result1.pos;  // current position
9483666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        CR crVal = result1.val;   // value so far
9493666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        BoundedRational ratVal = result1.ratVal;  // int value so far
950c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        if (isOperator(cpos, R.id.op_pow, ec)) {
9513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            final EvalRet exp = evalSignedFactor(cpos + 1, ec);
9523666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            cpos = exp.pos;
953682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            // Try completely rational evaluation first.
9543666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = BoundedRational.pow(ratVal, exp.ratVal);
955682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            if (ratVal != null) {
956682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                return new EvalRet(cpos, ratVal.CRValue(), ratVal);
95784614957604253d51296e06c97daced699a0a9deHans Boehm            }
958682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            // Power with integer exponent is defined for negative base.
959682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            // Thus we handle that case separately.
960682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            // We punt if the exponent is an integer computed from irrational
961682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            // values.  That wouldn't work reliably with floating point either.
9623666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            BigInteger int_exp = BoundedRational.asBigInteger(exp.ratVal);
963682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            if (int_exp != null) {
9643666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                crVal = pow(crVal, int_exp);
96584614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
9663666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                crVal = crVal.ln().multiply(exp.val).exp();
96784614957604253d51296e06c97daced699a0a9deHans Boehm            }
968682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm            ratVal = null;
96984614957604253d51296e06c97daced699a0a9deHans Boehm        }
9703666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return new EvalRet(cpos, crVal, ratVal);
97184614957604253d51296e06c97daced699a0a9deHans Boehm    }
97284614957604253d51296e06c97daced699a0a9deHans Boehm
973c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    private EvalRet evalSignedFactor(int i, EvalContext ec) throws SyntaxException {
974c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        final boolean negative = isOperator(i, R.id.op_sub, ec);
97508e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm        int cpos = negative ? i + 1 : i;
97684614957604253d51296e06c97daced699a0a9deHans Boehm        EvalRet tmp = evalFactor(cpos, ec);
9773666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        cpos = tmp.pos;
9783666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        CR crVal = negative ? tmp.val.negate() : tmp.val;
9793666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        BoundedRational ratVal = negative ? BoundedRational.negate(tmp.ratVal)
9803666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                                         : tmp.ratVal;
9813666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return new EvalRet(cpos, crVal, ratVal);
98284614957604253d51296e06c97daced699a0a9deHans Boehm    }
98384614957604253d51296e06c97daced699a0a9deHans Boehm
98484614957604253d51296e06c97daced699a0a9deHans Boehm    private boolean canStartFactor(int i) {
98584614957604253d51296e06c97daced699a0a9deHans Boehm        if (i >= mExpr.size()) return false;
98684614957604253d51296e06c97daced699a0a9deHans Boehm        Token t = mExpr.get(i);
98784614957604253d51296e06c97daced699a0a9deHans Boehm        if (!(t instanceof Operator)) return true;
9883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        int id = ((Operator)(t)).id;
98984614957604253d51296e06c97daced699a0a9deHans Boehm        if (KeyMaps.isBinary(id)) return false;
99084614957604253d51296e06c97daced699a0a9deHans Boehm        switch (id) {
99184614957604253d51296e06c97daced699a0a9deHans Boehm            case R.id.op_fact:
99284614957604253d51296e06c97daced699a0a9deHans Boehm            case R.id.rparen:
99384614957604253d51296e06c97daced699a0a9deHans Boehm                return false;
99484614957604253d51296e06c97daced699a0a9deHans Boehm            default:
99584614957604253d51296e06c97daced699a0a9deHans Boehm                return true;
99684614957604253d51296e06c97daced699a0a9deHans Boehm        }
99784614957604253d51296e06c97daced699a0a9deHans Boehm    }
99884614957604253d51296e06c97daced699a0a9deHans Boehm
999c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    private EvalRet evalTerm(int i, EvalContext ec) throws SyntaxException {
100084614957604253d51296e06c97daced699a0a9deHans Boehm        EvalRet tmp = evalSignedFactor(i, ec);
100184614957604253d51296e06c97daced699a0a9deHans Boehm        boolean is_mul = false;
100284614957604253d51296e06c97daced699a0a9deHans Boehm        boolean is_div = false;
10033666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        int cpos = tmp.pos;   // Current position in expression.
10043666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        CR crVal = tmp.val;    // Current value.
10053666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        BoundedRational ratVal = tmp.ratVal; // Current rational value.
1006c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        while ((is_mul = isOperator(cpos, R.id.op_mul, ec))
1007c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm               || (is_div = isOperator(cpos, R.id.op_div, ec))
100884614957604253d51296e06c97daced699a0a9deHans Boehm               || canStartFactor(cpos)) {
100984614957604253d51296e06c97daced699a0a9deHans Boehm            if (is_mul || is_div) ++cpos;
10104a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm            tmp = evalSignedFactor(cpos, ec);
101184614957604253d51296e06c97daced699a0a9deHans Boehm            if (is_div) {
10123666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                ratVal = BoundedRational.divide(ratVal, tmp.ratVal);
1013682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                if (ratVal == null) {
10143666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = crVal.divide(tmp.val);
1015682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                } else {
10163666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = ratVal.CRValue();
101784614957604253d51296e06c97daced699a0a9deHans Boehm                }
101884614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
10193666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                ratVal = BoundedRational.multiply(ratVal, tmp.ratVal);
1020682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                if (ratVal == null) {
10213666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = crVal.multiply(tmp.val);
1022682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                } else {
10233666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = ratVal.CRValue();
102484614957604253d51296e06c97daced699a0a9deHans Boehm                }
102584614957604253d51296e06c97daced699a0a9deHans Boehm            }
10263666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            cpos = tmp.pos;
102784614957604253d51296e06c97daced699a0a9deHans Boehm            is_mul = is_div = false;
102884614957604253d51296e06c97daced699a0a9deHans Boehm        }
10293666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return new EvalRet(cpos, crVal, ratVal);
103084614957604253d51296e06c97daced699a0a9deHans Boehm    }
103184614957604253d51296e06c97daced699a0a9deHans Boehm
10328afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm    /**
10338afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * Is the subexpression starting at pos a simple percent constant?
10348afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * This is used to recognize exppressions like 200+10%, which we handle specially.
10358afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * This is defined as a Constant or PreEval token, followed by a percent sign, and followed
10368afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * by either nothing or an additive operator.
10378afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * Note that we are intentionally far more restrictive in recognizing such expressions than
10388afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * e.g. http://blogs.msdn.com/b/oldnewthing/archive/2008/01/10/7047497.aspx .
10398afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * When in doubt, we fall back to the the naive interpretation of % as 1/100.
10408afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * Note that 100+(10)% yields 100.1 while 100+10% yields 110.  This may be controversial,
10418afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * but is consistent with Google web search.
10428afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     */
10438afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm    private boolean isPercent(int pos) {
10448afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        if (mExpr.size() < pos + 2 || !isOperatorUnchecked(pos + 1, R.id.op_pct)) {
10458afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            return false;
10468afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        }
10478afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        Token number = mExpr.get(pos);
10488afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        if (number instanceof Operator) {
10498afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            return false;
10508afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        }
10518afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        if (mExpr.size() == pos + 2) {
10528afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            return true;
10538afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        }
10548afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        if (!(mExpr.get(pos + 2) instanceof Operator)) {
10558afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            return false;
10568afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        }
10578afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        Operator op = (Operator) mExpr.get(pos + 2);
10588afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        return op.id == R.id.op_add || op.id == R.id.op_sub;
10598afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm    }
10608afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm
10618afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm    /**
10628afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * Compute the multiplicative factor corresponding to an N% addition or subtraction.
10638afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * @param pos position of Constant or PreEval expression token corresponding to N
10648afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * @param isSubtraction this is a subtraction, as opposed to addition
10658afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * @param ec usable evaluation contex; only length matters
10668afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     * @return Rational and CR values; position is pos + 2, i.e. after percent sign
10678afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm     */
10688afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm    private EvalRet getPercentFactor(int pos, boolean isSubtraction, EvalContext ec)
10698afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            throws SyntaxException {
10708afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        EvalRet tmp = evalUnary(pos, ec);
10718afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        BoundedRational ratVal = isSubtraction ? BoundedRational.negate(tmp.ratVal)
10728afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                : tmp.ratVal;
10738afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        CR crVal = isSubtraction ? tmp.val.negate() : tmp.val;
10748afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        ratVal = BoundedRational.add(BoundedRational.ONE,
10758afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                BoundedRational.multiply(ratVal, RATIONAL_ONE_HUNDREDTH));
10768afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        if (ratVal == null) {
10778afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            crVal = CR.ONE.add(crVal.multiply(REAL_ONE_HUNDREDTH));
10788afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        } else {
10798afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            crVal = ratVal.CRValue();
10808afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        }
10818afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm        return new EvalRet(pos + 2 /* after percent sign */, crVal, ratVal);
10828afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm    }
10838afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm
1084c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    private EvalRet evalExpr(int i, EvalContext ec) throws SyntaxException {
108584614957604253d51296e06c97daced699a0a9deHans Boehm        EvalRet tmp = evalTerm(i, ec);
108684614957604253d51296e06c97daced699a0a9deHans Boehm        boolean is_plus;
10873666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        int cpos = tmp.pos;
10883666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        CR crVal = tmp.val;
10893666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        BoundedRational ratVal = tmp.ratVal;
1090c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        while ((is_plus = isOperator(cpos, R.id.op_add, ec))
1091c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm               || isOperator(cpos, R.id.op_sub, ec)) {
10928afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm            if (isPercent(cpos + 1)) {
10938afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                tmp = getPercentFactor(cpos + 1, !is_plus, ec);
10948afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                ratVal = BoundedRational.multiply(ratVal, tmp.ratVal);
1095682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                if (ratVal == null) {
10968afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    crVal = crVal.multiply(tmp.val);
1097682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                } else {
10983666e6390cea89085f65ba507f6eac346abbf009Hans Boehm                    crVal = ratVal.CRValue();
109984614957604253d51296e06c97daced699a0a9deHans Boehm                }
110084614957604253d51296e06c97daced699a0a9deHans Boehm            } else {
11018afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                tmp = evalTerm(cpos + 1, ec);
11028afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                if (is_plus) {
11038afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    ratVal = BoundedRational.add(ratVal, tmp.ratVal);
11048afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    if (ratVal == null) {
11058afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                        crVal = crVal.add(tmp.val);
11068afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    } else {
11078afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                        crVal = ratVal.CRValue();
11088afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    }
1109682ff5e8ad465d74b289590e5c88e0cf129ca90bHans Boehm                } else {
11108afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    ratVal = BoundedRational.subtract(ratVal, tmp.ratVal);
11118afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    if (ratVal == null) {
11128afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                        crVal = crVal.subtract(tmp.val);
11138afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    } else {
11148afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                        crVal = ratVal.CRValue();
11158afd0f85ed0b9fa1c96297c540cb74e6d8b9a64dHans Boehm                    }
111684614957604253d51296e06c97daced699a0a9deHans Boehm                }
111784614957604253d51296e06c97daced699a0a9deHans Boehm            }
11183666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            cpos = tmp.pos;
111984614957604253d51296e06c97daced699a0a9deHans Boehm        }
11203666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        return new EvalRet(cpos, crVal, ratVal);
112184614957604253d51296e06c97daced699a0a9deHans Boehm    }
112284614957604253d51296e06c97daced699a0a9deHans Boehm
11233666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
11243666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Externally visible evaluation result.
11253666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
11263666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    public static class EvalResult {
11273666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final CR val;
11283666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        public final BoundedRational ratVal;
11293666e6390cea89085f65ba507f6eac346abbf009Hans Boehm        EvalResult (CR v, BoundedRational rv) {
11303666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            val = v;
11313666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            ratVal = rv;
113284614957604253d51296e06c97daced699a0a9deHans Boehm        }
113384614957604253d51296e06c97daced699a0a9deHans Boehm    }
113484614957604253d51296e06c97daced699a0a9deHans Boehm
1135e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm    /**
1136e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm     * Return the starting position of the sequence of trailing binary operators.
1137e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm     */
1138e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm    private int trailingBinaryOpsStart() {
1139c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        int result = mExpr.size();
1140c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        while (result > 0) {
1141c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            Token last = mExpr.get(result - 1);
1142c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            if (!(last instanceof Operator)) break;
1143c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            Operator o = (Operator)last;
11443666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (!KeyMaps.isBinary(o.id)) break;
1145c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            --result;
1146c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        }
1147c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        return result;
1148c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    }
1149c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm
11503666e6390cea89085f65ba507f6eac346abbf009Hans Boehm    /**
11513666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Is the current expression worth evaluating?
11523666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     */
1153c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    public boolean hasInterestingOps() {
1154e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm        int last = trailingBinaryOpsStart();
1155c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        int first = 0;
1156c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        if (last > first && isOperatorUnchecked(first, R.id.op_sub)) {
1157c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            // Leading minus is not by itself interesting.
1158c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            first++;
1159c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        }
1160c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        for (int i = first; i < last; ++i) {
1161c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            Token t1 = mExpr.get(i);
1162187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm            if (t1 instanceof Operator
1163187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm                    || t1 instanceof PreEval && ((PreEval)t1).hasEllipsis()) {
1164187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm                return true;
1165187d3e93b13bf0d8711ad5ecaab2deb9909b5f23Hans Boehm            }
1166c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        }
1167c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm        return false;
1168c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm    }
1169c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm
1170e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm    /**
1171e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm     * Evaluate the expression excluding trailing binary operators.
11723666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * Errors result in exceptions, most of which are unchecked.  Should not be called
11733666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * concurrently with modification of the expression.  May take a very long time; avoid calling
11743666e6390cea89085f65ba507f6eac346abbf009Hans Boehm     * from UI thread.
1175e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm     *
1176e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm     * @param degreeMode use degrees rather than radians
1177e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm     */
1178e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm    EvalResult eval(boolean degreeMode) throws SyntaxException
1179c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                        // And unchecked exceptions thrown by CR
1180c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                        // and BoundedRational.
118184614957604253d51296e06c97daced699a0a9deHans Boehm    {
118284614957604253d51296e06c97daced699a0a9deHans Boehm        try {
11833666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            // We currently never include trailing binary operators, but include other trailing
11843666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            // operators.  Thus we usually, but not always, display results for prefixes of valid
11853666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            // expressions, and don't generate an error where we previously displayed an instant
11863666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            // result.  This reflects the Android L design.
1187e8553769f4b06650da0ca22a1bf741d0aad96e74Hans Boehm            int prefixLen = trailingBinaryOpsStart();
1188c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            EvalContext ec = new EvalContext(degreeMode, prefixLen);
118984614957604253d51296e06c97daced699a0a9deHans Boehm            EvalRet res = evalExpr(0, ec);
11903666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            if (res.pos != prefixLen) {
1191c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm                throw new SyntaxException("Failed to parse full expression");
1192fbcef7005de4436682072927f83000b502928d25Hans Boehm            }
11933666e6390cea89085f65ba507f6eac346abbf009Hans Boehm            return new EvalResult(res.val, res.ratVal);
119484614957604253d51296e06c97daced699a0a9deHans Boehm        } catch (IndexOutOfBoundsException e) {
1195c023b734310f231244f17189788ae9c17c90b9a8Hans Boehm            throw new SyntaxException("Unexpected expression end");
119684614957604253d51296e06c97daced699a0a9deHans Boehm        }
119784614957604253d51296e06c97daced699a0a9deHans Boehm    }
119884614957604253d51296e06c97daced699a0a9deHans Boehm
119984614957604253d51296e06c97daced699a0a9deHans Boehm    // Produce a string representation of the expression itself
12008a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm    SpannableStringBuilder toSpannableStringBuilder(Context context) {
12018a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm        SpannableStringBuilder ssb = new SpannableStringBuilder();
120284614957604253d51296e06c97daced699a0a9deHans Boehm        for (Token t: mExpr) {
12038a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm            ssb.append(t.toCharSequence(context));
120484614957604253d51296e06c97daced699a0a9deHans Boehm        }
12058a4f81c5b30edd4e62d222a17f4e0e2140bfd99dHans Boehm        return ssb;
120684614957604253d51296e06c97daced699a0a9deHans Boehm    }
120784614957604253d51296e06c97daced699a0a9deHans Boehm}
1208