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