From a1c8b752d3a21458dea1195cab70d2a8b23d1b9e Mon Sep 17 00:00:00 2001 From: AnthonyFermin Date: Thu, 21 May 2015 18:58:25 -0400 Subject: [PATCH 01/12] project --- CalcProj/.gitignore | 32 + CalcProj/app/.gitignore | 1 + CalcProj/app/build.gradle | 25 + CalcProj/app/proguard-rules.pro | 17 + .../calculatorproject/ApplicationTest.java | 13 + CalcProj/app/src/main/AndroidManifest.xml | 21 + CalcProj/app/src/main/ic_launcher-web.png | Bin 0 -> 65389 bytes .../c4q/nyc/calculatorproject/Expression.java | 1134 +++++++++++++++++ .../nyc/calculatorproject/MainActivity.java | 845 ++++++++++++ .../main/res/drawable-v21/number_button.xml | 18 + .../src/main/res/drawable/function_button.xml | 13 + .../src/main/res/drawable/number_button.xml | 18 + .../src/main/res/drawable/orange_button.xml | 13 + .../src/main/res/drawable/white_button.xml | 13 + .../main/res/layout-land/activity_main.xml | 484 +++++++ .../app/src/main/res/layout/activity_main.xml | 303 +++++ CalcProj/app/src/main/res/menu/menu_main.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2436 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1510 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3608 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6873 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 11161 bytes .../app/src/main/res/values-w820dp/dimens.xml | 6 + CalcProj/app/src/main/res/values/dimens.xml | 5 + CalcProj/app/src/main/res/values/strings.xml | 7 + CalcProj/app/src/main/res/values/styles.xml | 8 + CalcProj/build.gradle | 19 + CalcProj/gradle.properties | 18 + .../gradle/wrapper/gradle-wrapper.properties | 6 + CalcProj/gradlew | 164 +++ CalcProj/gradlew.bat | 90 ++ CalcProj/settings.gradle | 1 + 32 files changed, 3280 insertions(+) create mode 100644 CalcProj/.gitignore create mode 100644 CalcProj/app/.gitignore create mode 100644 CalcProj/app/build.gradle create mode 100644 CalcProj/app/proguard-rules.pro create mode 100644 CalcProj/app/src/androidTest/java/doubleabatteries/c4q/nyc/calculatorproject/ApplicationTest.java create mode 100644 CalcProj/app/src/main/AndroidManifest.xml create mode 100644 CalcProj/app/src/main/ic_launcher-web.png create mode 100644 CalcProj/app/src/main/java/doubleabatteries/c4q/nyc/calculatorproject/Expression.java create mode 100644 CalcProj/app/src/main/java/doubleabatteries/c4q/nyc/calculatorproject/MainActivity.java create mode 100644 CalcProj/app/src/main/res/drawable-v21/number_button.xml create mode 100644 CalcProj/app/src/main/res/drawable/function_button.xml create mode 100644 CalcProj/app/src/main/res/drawable/number_button.xml create mode 100644 CalcProj/app/src/main/res/drawable/orange_button.xml create mode 100644 CalcProj/app/src/main/res/drawable/white_button.xml create mode 100644 CalcProj/app/src/main/res/layout-land/activity_main.xml create mode 100644 CalcProj/app/src/main/res/layout/activity_main.xml create mode 100644 CalcProj/app/src/main/res/menu/menu_main.xml create mode 100644 CalcProj/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 CalcProj/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 CalcProj/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 CalcProj/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 CalcProj/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 CalcProj/app/src/main/res/values-w820dp/dimens.xml create mode 100644 CalcProj/app/src/main/res/values/dimens.xml create mode 100644 CalcProj/app/src/main/res/values/strings.xml create mode 100644 CalcProj/app/src/main/res/values/styles.xml create mode 100644 CalcProj/build.gradle create mode 100644 CalcProj/gradle.properties create mode 100644 CalcProj/gradle/wrapper/gradle-wrapper.properties create mode 100755 CalcProj/gradlew create mode 100644 CalcProj/gradlew.bat create mode 100644 CalcProj/settings.gradle diff --git a/CalcProj/.gitignore b/CalcProj/.gitignore new file mode 100644 index 0000000..dbdc53c --- /dev/null +++ b/CalcProj/.gitignore @@ -0,0 +1,32 @@ +# Built application files +*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio File +*.iml +.idea/ + +.DS_Store diff --git a/CalcProj/app/.gitignore b/CalcProj/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/CalcProj/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/CalcProj/app/build.gradle b/CalcProj/app/build.gradle new file mode 100644 index 0000000..aeb085a --- /dev/null +++ b/CalcProj/app/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "doubleabatteries.c4q.nyc.calculatorproject" + minSdkVersion 15 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:22.1.1' +} diff --git a/CalcProj/app/proguard-rules.pro b/CalcProj/app/proguard-rules.pro new file mode 100644 index 0000000..d30fc3e --- /dev/null +++ b/CalcProj/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/alvin2/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/CalcProj/app/src/androidTest/java/doubleabatteries/c4q/nyc/calculatorproject/ApplicationTest.java b/CalcProj/app/src/androidTest/java/doubleabatteries/c4q/nyc/calculatorproject/ApplicationTest.java new file mode 100644 index 0000000..305809d --- /dev/null +++ b/CalcProj/app/src/androidTest/java/doubleabatteries/c4q/nyc/calculatorproject/ApplicationTest.java @@ -0,0 +1,13 @@ +package doubleabatteries.c4q.nyc.calculatorproject; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/CalcProj/app/src/main/AndroidManifest.xml b/CalcProj/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8215dc8 --- /dev/null +++ b/CalcProj/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/CalcProj/app/src/main/ic_launcher-web.png b/CalcProj/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac7b57322f0423ff6e43cd3d1e51eb73155ab9f GIT binary patch literal 65389 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 - Java Expression Evaluator + * + *

Introduction

+ * EvalEx is a handy expression evaluator for Java, that allows to evaluate simple mathematical and boolean expressions. + *
+ * Key Features: + *
    + *
  • Uses BigDecimal for calculation and result
  • + *
  • Single class implementation, very compact
  • + *
  • No dependencies to external libraries
  • + *
  • Precision and rounding mode can be set
  • + *
  • Supports variables
  • + *
  • Standard boolean and mathematical operators
  • + *
  • Standard basic mathematical and boolean functions
  • + *
  • Custom functions and operators can be added at runtime
  • + *
+ *
+ *

Examples

+ *
+     *  BigDecimal result = null;
+     *
+     *  Expression expression = new Expression("1+1/3");
+     *  result = expression.eval():
+     *  expression.setPrecision(2);
+     *  result = expression.eval():
+     *
+     *  result = new Expression("(3.4 + -4.1)/2").eval();
+     *
+     *  result = new Expression("SQRT(a^2 + b^2").with("a","2.4").and("b","9.253").eval();
+     *
+     *  BigDecimal a = new BigDecimal("2.4");
+     *  BigDecimal b = new BigDecimal("9.235");
+     *  result = new Expression("SQRT(a^2 + b^2").with("a",a).and("b",b).eval();
+     *
+     *  result = new Expression("2.4/PI").setPrecision(128).setRoundingMode(RoundingMode.UP).eval();
+     *
+     *  result = new Expression("random() > 0.5").eval();
+     *
+     *  result = new Expression("not(x<7 || sqrt(max(x,9)) <= 3))").with("x","22.9").eval();
+     * 
+ *
+ *

Supported Operators

+ * + * + * + * + * + * + * + * + * + *
Mathematical Operators
OperatorDescription
+Additive operator
-Subtraction operator
*Multiplication operator
/Division operator
%Remainder operator (Modulo)
^Power operator
+ *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
Boolean Operators*
OperatorDescription
=Equals
==Equals
!=Not equals
<>Not equals
<Less than
<=Less than or equal to
>Greater than
>=Greater than or equal to
&&Boolean and
||Boolean or
+ * *Boolean operators result always in a BigDecimal value of 1 or 0 (zero). Any non-zero value is treated as a _true_ value. Boolean _not_ is implemented by a function. + *
+ *

Supported Functions

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Function*Description
NOT(expression)Boolean negation, 1 (means true) if the expression is not zero
IF(condition,value_if_true,value_if_false)Returns one value if the condition evaluates to true or the other if it evaluates to false
RANDOM()Produces a random number between 0 and 1
MIN(e1,e2)Returns the smaller of both expressions
MAX(e1,e2)Returns the bigger of both expressions
ABS(expression)Returns the absolute (non-negative) value of the expression
ROUND(expression,precision)Rounds a value to a certain number of digits, uses the current rounding mode
FLOOR(expression)Rounds the value down to the nearest integer
CEILING(expression)Rounds the value up to the nearest integer
LOG(expression)Returns the natural logarithm (base e) of an expression
LOG10(expression)Returns the common logarithm (base 10) of an expression
SQRT(expression)Returns the square root of an expression
SIN(expression)Returns the trigonometric sine of an angle (in degrees)
COS(expression)Returns the trigonometric cosine of an angle (in degrees)
TAN(expression)Returns the trigonometric tangens of an angle (in degrees)
ASIN(expression)Returns the angle of asin (in degrees)
ACOS(expression)Returns the angle of acos (in degrees)
ATAN(expression)Returns the angle of atan (in degrees)
SINH(expression)Returns the hyperbolic sine of a value
COSH(expression)Returns the hyperbolic cosine of a value
TANH(expression)Returns the hyperbolic tangens of a value
RAD(expression)Converts an angle measured in degrees to an approximately equivalent angle measured in radians
DEG(expression)Converts an angle measured in radians to an approximately equivalent angle measured in degrees
+ * *Functions names are case insensitive. + *
+ *

Supported Constants

+ * + * + * + * + * + *
ConstantDescription
PIThe value of PI, exact to 100 digits
TRUEThe value one
FALSEThe value zero
+ * + *

Add Custom Operators

+ * + * Custom operators can be added easily, simply create an instance of `Expression.Operator` and add it to the expression. + * Parameters are the operator string, its precedence and if it is left associative. The operators `eval()` method will be called with the BigDecimal values of the operands. + * All existing operators can also be overridden. + *
+ * For example, add an operator `x >> n`, that moves the decimal point of _x_ _n_ digits to the right: + * + *
+     * Expression e = new Expression("2.1234 >> 2");
+     *
+     * e.addOperator(e.new Operator(">>", 30, true) {
+     *     {@literal @}Override
+     *     public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
+     *         return v1.movePointRight(v2.toBigInteger().intValue());
+     *     }
+     * });
+     *
+     * e.eval(); // returns 212.34
+     * 
+ *
+ *

Add Custom Functions

+ * + * Adding custom functions is as easy as adding custom operators. Create an instance of `Expression.Function`and add it to the expression. + * Parameters are the function name and the count of required parameters. The functions `eval()` method will be called with a list of the BigDecimal parameters. + * All existing functions can also be overridden. + *
+ * For example, add a function `average(a,b,c)`, that will calculate the average value of a, b and c: + *
+ *
+     * Expression e = new Expression("2 * average(12,4,8)");
+     *
+     * e.addFunction(e.new Function("average", 3) {
+     *     {@literal @}Override
+     *     public BigDecimal eval(List parameters) {
+     *         BigDecimal sum = parameters.get(0).add(parameters.get(1)).add(parameters.get(2));
+     *         return sum.divide(new BigDecimal(3));
+     *     }
+     * });
+     *
+     * e.eval(); // returns 16
+     * 
+ * The software is licensed under the MIT Open Source license (see LICENSE file). + *
+ *
    + *
  • The *power of* operator (^) implementation was copied from [Stack Overflow](http://stackoverflow.com/questions/3579779/how-to-do-a-fractional-power-on-bigdecimal-in-java) Thanks to Gene Marin
  • + *
  • The SQRT() function implementation was taken from the book [The Java Programmers Guide To numerical Computing](http://www.amazon.de/Java-Number-Cruncher-Programmers-Numerical/dp/0130460419) (Ronald Mak, 2002)
  • + *
+ * + *@author Udo Klimaschewski (http://about.me/udo.klimaschewski) + */ + public class Expression { + + /** + * Definition of PI as a constant, can be used in expressions as variable. + */ + public static final BigDecimal PI = new BigDecimal( + "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"); + public static final BigDecimal E = new BigDecimal("2.71828182845904523536028747135266249775724709369995"); + + /** + * The {@link MathContext} to use for calculations. + */ + private MathContext mc = MathContext.DECIMAL32; + + /** + * The original infix expression. + */ + private String expression = null; + + /** + * The cached RPN (Reverse Polish Notation) of the expression. + */ + private List rpn = null; + + /** + * All defined operators with name and implementation. + */ + private Map operators = new HashMap(); + + /** + * All defined functions with name and implementation. + */ + private Map functions = new HashMap(); + + /** + * All defined variables with name and value. + */ + private Map variables = new HashMap(); + + /** + * What character to use for decimal separators. + */ + private final char decimalSeparator = '.'; + + /** + * What character to use for minus sign (negative values). + */ + private final char minusSign = '-'; + + /** + * The expression evaluators exception class. + */ + public class ExpressionException extends RuntimeException { + private static final long serialVersionUID = 1118142866870779047L; + + public ExpressionException(String message) { + super(message); + } + } + + /** + * Abstract definition of a supported expression function. A function is + * defined by a name, the number of parameters and the actual processing + * implementation. + */ + public abstract class Function { + /** + * Name of this function. + */ + private String name; + /** + * Number of parameters expected for this function. + */ + private int numParams; + + /** + * Creates a new function with given name and parameter count. + * + * @param name + * The name of the function. + * @param numParams + * The number of parameters for this function. + */ + public Function(String name, int numParams) { + this.name = name.toUpperCase(); + this.numParams = numParams; + } + + public String getName() { + return name; + } + + public int getNumParams() { + return numParams; + } + + /** + * Implementation for this function. + * + * @param parameters + * Parameters will be passed by the expression evaluator as a + * {@link List} of {@link BigDecimal} values. + * @return The function must return a new {@link BigDecimal} value as a + * computing result. + */ + public abstract BigDecimal eval(List parameters); + } + + /** + * Abstract definition of a supported operator. An operator is defined by + * its name (pattern), precedence and if it is left- or right associative. + */ + public abstract class Operator { + /** + * This operators name (pattern). + */ + private String oper; + /** + * Operators precedence. + */ + private int precedence; + /** + * Operator is left associative. + */ + private boolean leftAssoc; + + /** + * Creates a new operator. + * + * @param oper + * The operator name (pattern). + * @param precedence + * The operators precedence. + * @param leftAssoc + * true if the operator is left associative, + * else false. + */ + public Operator(String oper, int precedence, boolean leftAssoc) { + this.oper = oper; + this.precedence = precedence; + this.leftAssoc = leftAssoc; + } + + public String getOper() { + return oper; + } + + public int getPrecedence() { + return precedence; + } + + public boolean isLeftAssoc() { + return leftAssoc; + } + + /** + * Implementation for this operator. + * + * @param v1 + * Operand 1. + * @param v2 + * Operand 2. + * @return The result of the operation. + */ + public abstract BigDecimal eval(BigDecimal v1, BigDecimal v2); + } + + /** + * Expression tokenizer that allows to iterate over a {@link String} + * expression token by token. Blank characters will be skipped. + */ + private class Tokenizer implements Iterator { + + /** + * Actual position in expression string. + */ + private int pos = 0; + + /** + * The original input expression. + */ + private String input; + /** + * The previous token or null if none. + */ + private String previousToken; + + /** + * Creates a new tokenizer for an expression. + * + * @param input + * The expression string. + */ + public Tokenizer(String input) { + this.input = input.trim(); + } + + @Override + public boolean hasNext() { + return (pos < input.length()); + } + + /** + * Peek at the next character, without advancing the iterator. + * + * @return The next character or character 0, if at end of string. + */ + private char peekNextChar() { + if (pos < (input.length() - 1)) { + return input.charAt(pos + 1); + } else { + return 0; + } + } + + @Override + public String next() { + StringBuilder token = new StringBuilder(); + if (pos >= input.length()) { + return previousToken = null; + } + char ch = input.charAt(pos); + while (Character.isWhitespace(ch) && pos < input.length()) { + ch = input.charAt(++pos); + } + if (Character.isDigit(ch)) { + while ((Character.isDigit(ch) || ch == decimalSeparator) + && (pos < input.length())) { + token.append(input.charAt(pos++)); + ch = pos == input.length() ? 0 : input.charAt(pos); + } + } else if (ch == minusSign + && Character.isDigit(peekNextChar()) + && ("(".equals(previousToken) || ",".equals(previousToken) + || previousToken == null || operators + .containsKey(previousToken))) { + token.append(minusSign); + pos++; + token.append(next()); + } else if (Character.isLetter(ch) || (ch == '_')) { + while ((Character.isLetter(ch) || Character.isDigit(ch) || (ch == '_')) && (pos < input.length())) { + token.append(input.charAt(pos++)); + ch = pos == input.length() ? 0 : input.charAt(pos); + } + } else if (ch == '(' || ch == ')' || ch == ',') { + token.append(ch); + pos++; + } else { + while (!Character.isLetter(ch) && !Character.isDigit(ch) && ch != '_' + && !Character.isWhitespace(ch) && ch != '(' + && ch != ')' && ch != ',' && (pos < input.length())) { + token.append(input.charAt(pos)); + pos++; + ch = pos == input.length() ? 0 : input.charAt(pos); + if (ch == minusSign) { + break; + } + } + if (!operators.containsKey(token.toString())) { + throw new ExpressionException("Unknown operator '" + token + + "' at position " + (pos - token.length() + 1)); + } + } + return previousToken = token.toString(); + } + + @Override + public void remove() { + throw new ExpressionException("remove() not supported"); + } + + /** + * Get the actual character position in the string. + * + * @return The actual character position. + */ + public int getPos() { + return pos; + } + + } + + /** + * Creates a new expression instance from an expression string. + * + * @param expression + * The expression. E.g. "2.4*sin(3)/(2-4)" or + * "sin(y)>0 & max(z, 3)>3" + */ + public Expression(String expression) { + this.expression = expression; + addOperator(new Operator("+", 20, true) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.add(v2, mc); + } + }); + addOperator(new Operator("-", 20, true) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.subtract(v2, mc); + } + }); + addOperator(new Operator("*", 30, true) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.multiply(v2, mc); + } + }); + addOperator(new Operator("/", 30, true) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.divide(v2, mc); + } + }); + addOperator(new Operator("%", 30, true) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.remainder(v2, mc); + } + }); + addOperator(new Operator("^", 40, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + /*- + * Thanks to Gene Marin: + * http://stackoverflow.com/questions/3579779/how-to-do-a-fractional-power-on-bigdecimal-in-java + */ + int signOf2 = v2.signum(); + double dn1 = v1.doubleValue(); + v2 = v2.multiply(new BigDecimal(signOf2)); // n2 is now positive + BigDecimal remainderOf2 = v2.remainder(BigDecimal.ONE); + BigDecimal n2IntPart = v2.subtract(remainderOf2); + BigDecimal intPow = v1.pow(n2IntPart.intValueExact(), mc); + BigDecimal doublePow = new BigDecimal(Math.pow(dn1, + remainderOf2.doubleValue())); + + BigDecimal result = intPow.multiply(doublePow, mc); + if (signOf2 == -1) { + result = BigDecimal.ONE.divide(result, mc.getPrecision(), + RoundingMode.HALF_UP); + } + return result; + } + }); + addOperator(new Operator("&&", 4, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + boolean b1 = !v1.equals(BigDecimal.ZERO); + boolean b2 = !v2.equals(BigDecimal.ZERO); + return b1 && b2 ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + + addOperator(new Operator("||", 2, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + boolean b1 = !v1.equals(BigDecimal.ZERO); + boolean b2 = !v2.equals(BigDecimal.ZERO); + return b1 || b2 ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + + addOperator(new Operator(">", 10, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.compareTo(v2) == 1 ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + + addOperator(new Operator(">=", 10, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.compareTo(v2) >= 0 ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + + addOperator(new Operator("<", 10, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.compareTo(v2) == -1 ? BigDecimal.ONE + : BigDecimal.ZERO; + } + }); + + addOperator(new Operator("<=", 10, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.compareTo(v2) <= 0 ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + + addOperator(new Operator("=", 7, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.compareTo(v2) == 0 ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + addOperator(new Operator("==", 7, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return operators.get("=").eval(v1, v2); + } + }); + + addOperator(new Operator("!=", 7, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return v1.compareTo(v2) != 0 ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + addOperator(new Operator("<>", 7, false) { + @Override + public BigDecimal eval(BigDecimal v1, BigDecimal v2) { + return operators.get("!=").eval(v1, v2); + } + }); + + addFunction(new Function("NOT", 1) { + @Override + public BigDecimal eval(List parameters) { + boolean zero = parameters.get(0).compareTo(BigDecimal.ZERO) == 0; + return zero ? BigDecimal.ONE : BigDecimal.ZERO; + } + }); + + addFunction(new Function("IF", 3) { + @Override + public BigDecimal eval(List parameters) { + boolean isTrue = !parameters.get(0).equals(BigDecimal.ZERO); + return isTrue ? parameters.get(1) : parameters.get(2); + } + }); + + addFunction(new Function("RANDOM", 0) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.random(); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("SIN", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.sin(Math.toRadians(parameters.get(0) + .doubleValue())); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("COS", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.cos(Math.toRadians(parameters.get(0) + .doubleValue())); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("TAN", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.tan(Math.toRadians(parameters.get(0) + .doubleValue())); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("ASIN", 1) { // added by av + @Override + public BigDecimal eval(List parameters) { + double d = Math.toDegrees(Math.asin(parameters.get(0) + .doubleValue())); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("ACOS", 1) { // added by av + @Override + public BigDecimal eval(List parameters) { + double d = Math.toDegrees(Math.acos(parameters.get(0) + .doubleValue())); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("ATAN", 1) { // added by av + @Override + public BigDecimal eval(List parameters) { + double d = Math.toDegrees(Math.atan(parameters.get(0) + .doubleValue())); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("SINH", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.sinh(parameters.get(0).doubleValue()); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("COSH", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.cosh(parameters.get(0).doubleValue()); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("TANH", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.tanh(parameters.get(0).doubleValue()); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("RAD", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.toRadians(parameters.get(0).doubleValue()); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("DEG", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.toDegrees(parameters.get(0).doubleValue()); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("MAX", 2) { + @Override + public BigDecimal eval(List parameters) { + BigDecimal v1 = parameters.get(0); + BigDecimal v2 = parameters.get(1); + return v1.compareTo(v2) > 0 ? v1 : v2; + } + }); + addFunction(new Function("MIN", 2) { + @Override + public BigDecimal eval(List parameters) { + BigDecimal v1 = parameters.get(0); + BigDecimal v2 = parameters.get(1); + return v1.compareTo(v2) < 0 ? v1 : v2; + } + }); + addFunction(new Function("ABS", 1) { + @Override + public BigDecimal eval(List parameters) { + return parameters.get(0).abs(mc); + } + }); + addFunction(new Function("LOG", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.log(parameters.get(0).doubleValue()); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("LOG10", 1) { + @Override + public BigDecimal eval(List parameters) { + double d = Math.log10(parameters.get(0).doubleValue()); + return new BigDecimal(d, mc); + } + }); + addFunction(new Function("ROUND", 2) { + @Override + public BigDecimal eval(List parameters) { + BigDecimal toRound = parameters.get(0); + int precision = parameters.get(1).intValue(); + return toRound.setScale(precision, mc.getRoundingMode()); + } + }); + addFunction(new Function("FLOOR", 1) { + @Override + public BigDecimal eval(List parameters) { + BigDecimal toRound = parameters.get(0); + return toRound.setScale(0, RoundingMode.FLOOR); + } + }); + addFunction(new Function("CEILING", 1) { + @Override + public BigDecimal eval(List parameters) { + BigDecimal toRound = parameters.get(0); + return toRound.setScale(0, RoundingMode.CEILING); + } + }); + addFunction(new Function("SQRT", 1) { + @Override + public BigDecimal eval(List parameters) { + /* + * From The Java Programmers Guide To numerical Computing + * (Ronald Mak, 2003) + */ + BigDecimal x = parameters.get(0); + if (x.compareTo(BigDecimal.ZERO) == 0) { + return new BigDecimal(0); + } + if (x.signum() < 0) { + throw new ExpressionException( + "Argument to SQRT() function must not be negative"); + } + BigInteger n = x.movePointRight(mc.getPrecision() << 1) + .toBigInteger(); + + int bits = (n.bitLength() + 1) >> 1; + BigInteger ix = n.shiftRight(bits); + BigInteger ixPrev; + + do { + ixPrev = ix; + ix = ix.add(n.divide(ix)).shiftRight(1); + // Give other threads a chance to work; + Thread.yield(); + } while (ix.compareTo(ixPrev) != 0); + + return new BigDecimal(ix, mc.getPrecision()); + } + }); + + variables.put("PI", PI); + variables.put("e", E); + variables.put("TRUE", BigDecimal.ONE); + variables.put("FALSE", BigDecimal.ZERO); + + } + + /** + * Is the string a number? + * + * @param st + * The string. + * @return true, if the input string is a number. + */ + private boolean isNumber(String st) { + if (st.charAt(0) == minusSign && st.length() == 1) + return false; + for (char ch : st.toCharArray()) { + if (!Character.isDigit(ch) && ch != minusSign + && ch != decimalSeparator) + return false; + } + return true; + } + + /** + * Implementation of the Shunting Yard algorithm to transform an + * infix expression to a RPN expression. + * + * @param expression + * The input expression in infx. + * @return A RPN representation of the expression, with each token as a list + * member. + */ + private List shuntingYard(String expression) { + List outputQueue = new ArrayList(); + Stack stack = new Stack(); + + Tokenizer tokenizer = new Tokenizer(expression); + + String lastFunction = null; + String previousToken = null; + while (tokenizer.hasNext()) { + String token = tokenizer.next(); + if (isNumber(token)) { + outputQueue.add(token); + } else if (variables.containsKey(token)) { + outputQueue.add(token); + } else if (functions.containsKey(token.toUpperCase())) { + stack.push(token); + lastFunction = token; + } else if (Character.isLetter(token.charAt(0))) { + stack.push(token); + } else if (",".equals(token)) { + while (!stack.isEmpty() && !"(".equals(stack.peek())) { + outputQueue.add(stack.pop()); + } + if (stack.isEmpty()) { + throw new ExpressionException("Parse error for function '" + + lastFunction + "'"); + } + } else if (operators.containsKey(token)) { + Operator o1 = operators.get(token); + String token2 = stack.isEmpty() ? null : stack.peek(); + while (operators.containsKey(token2) + && ((o1.isLeftAssoc() && o1.getPrecedence() <= operators + .get(token2).getPrecedence()) || (o1 + .getPrecedence() < operators.get(token2) + .getPrecedence()))) { + outputQueue.add(stack.pop()); + token2 = stack.isEmpty() ? null : stack.peek(); + } + stack.push(token); + } else if ("(".equals(token)) { + if (previousToken != null) { + if (isNumber(previousToken)) { + throw new ExpressionException("Missing operator at character position " + tokenizer.getPos()); + } + } + stack.push(token); + } else if (")".equals(token)) { + while (!stack.isEmpty() && !"(".equals(stack.peek())) { + outputQueue.add(stack.pop()); + } + if (stack.isEmpty()) { + throw new RuntimeException("Mismatched parentheses"); + } + stack.pop(); + if (!stack.isEmpty() + && functions.containsKey(stack.peek().toUpperCase())) { + outputQueue.add(stack.pop()); + } + } + previousToken = token; + } + while (!stack.isEmpty()) { + String element = stack.pop(); + if ("(".equals(element) || ")".equals(element)) { + throw new RuntimeException("Mismatched parentheses"); + } + if (!operators.containsKey(element)) { + throw new RuntimeException("Unknown operator or function: " + + element); + } + outputQueue.add(element); + } + return outputQueue; + } + + /** + * Evaluates the expression. + * + * @return The result of the expression. + */ + public BigDecimal eval() { + + Stack stack = new Stack(); + + for (String token : getRPN()) { + if (operators.containsKey(token)) { + BigDecimal v1 = stack.pop(); + BigDecimal v2 = stack.pop(); + stack.push(operators.get(token).eval(v2, v1)); + } else if (variables.containsKey(token)) { + stack.push(variables.get(token).round(mc)); + } else if (functions.containsKey(token.toUpperCase())) { + Function f = functions.get(token.toUpperCase()); + ArrayList p = new ArrayList( + f.getNumParams()); + for (int i = 0; i < f.numParams; i++) { + p.add(0,stack.pop()); + } + BigDecimal fResult = f.eval(p); + stack.push(fResult); + } else { + stack.push(new BigDecimal(token, mc)); + } + } + return stack.pop().stripTrailingZeros(); + } + + /** + * Sets the precision for expression evaluation. + * + * @param precision + * The new precision. + * + * @return The expression, allows to chain methods. + */ + public Expression setPrecision(int precision) { + this.mc = new MathContext(precision); + return this; + } + + /** + * Sets the rounding mode for expression evaluation. + * + * @param roundingMode + * The new rounding mode. + * @return The expression, allows to chain methods. + */ + public Expression setRoundingMode(RoundingMode roundingMode) { + this.mc = new MathContext(mc.getPrecision(), roundingMode); + return this; + } + + /** + * Adds an operator to the list of supported operators. + * + * @param operator + * The operator to add. + * @return The previous operator with that name, or null if + * there was none. + */ + public Operator addOperator(Operator operator) { + return operators.put(operator.getOper(), operator); + } + + /** + * Adds a function to the list of supported functions + * + * @param function + * The function to add. + * @return The previous operator with that name, or null if + * there was none. + */ + public Function addFunction(Function function) { + return functions.put(function.getName(), function); + } + + /** + * Sets a variable value. + * + * @param variable + * The variable name. + * @param value + * The variable value. + * @return The expression, allows to chain methods. + */ + public Expression setVariable(String variable, BigDecimal value) { + variables.put(variable, value); + return this; + } + + /** + * Sets a variable value. + * + * @param variable + * The variable to set. + * @param value + * The variable value. + * @return The expression, allows to chain methods. + */ + public Expression setVariable(String variable, String value) { + if (isNumber(value)) + variables.put(variable, new BigDecimal(value)); + else { + expression = expression.replaceAll("\\b" + variable + "\\b", "(" + value + ")"); + rpn = null; + } + return this; + } + + /** + * Sets a variable value. + * + * @param variable + * The variable to set. + * @param value + * The variable value. + * @return The expression, allows to chain methods. + */ + public Expression with(String variable, BigDecimal value) { + return setVariable(variable, value); + } + + /** + * Sets a variable value. + * + * @param variable + * The variable to set. + * @param value + * The variable value. + * @return The expression, allows to chain methods. + */ + public Expression and(String variable, String value) { + return setVariable(variable, value); + } + + /** + * Sets a variable value. + * + * @param variable + * The variable to set. + * @param value + * The variable value. + * @return The expression, allows to chain methods. + */ + public Expression and(String variable, BigDecimal value) { + return setVariable(variable, value); + } + + /** + * Sets a variable value. + * + * @param variable + * The variable to set. + * @param value + * The variable value. + * @return The expression, allows to chain methods. + */ + public Expression with(String variable, String value) { + return setVariable(variable, value); + } + + /** + * Get an iterator for this expression, allows iterating over an expression + * token by token. + * + * @return A new iterator instance for this expression. + */ + public Iterator getExpressionTokenizer() { + return new Tokenizer(this.expression); + } + + /** + * Cached access to the RPN notation of this expression, ensures only one + * calculation of the RPN per expression instance. If no cached instance + * exists, a new one will be created and put to the cache. + * + * @return The cached RPN instance. + */ + private List getRPN() { + if (rpn == null) { + rpn = shuntingYard(this.expression); + } + return rpn; + } + + /** + * Get a string representation of the RPN (Reverse Polish Notation) for this + * expression. + * + * @return A string with the RPN representation for this expression. + */ + public String toRPN() { + String result = new String(); + for (String st : getRPN()) { + result = result.isEmpty() ? result : result + " "; + result += st; + } + return result; + } + + } + + diff --git a/CalcProj/app/src/main/java/doubleabatteries/c4q/nyc/calculatorproject/MainActivity.java b/CalcProj/app/src/main/java/doubleabatteries/c4q/nyc/calculatorproject/MainActivity.java new file mode 100644 index 0000000..a4c8f3b --- /dev/null +++ b/CalcProj/app/src/main/java/doubleabatteries/c4q/nyc/calculatorproject/MainActivity.java @@ -0,0 +1,845 @@ +package doubleabatteries.c4q.nyc.calculatorproject; +import android.support.v7.app.ActionBarActivity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import java.math.BigDecimal; +import java.util.ArrayList; + + +// TODO: y√x // X! // PERCENTAGE // DECIMAL // INV code! + +// TODO: implement HORIZONTAL scrollview in TextView! +// TODO: change color + shape of buttons ['=' and 'rad' and 'deg']! +// TODO: change app icon for calculator! + + +// TODO: 1) PERCENTAGE && 2) DECIMAL + +public class MainActivity extends ActionBarActivity { + + Expression expression; +// boolean isRadian; + + boolean enterPressed = false; + String ans = ""; + TextView textview; + TextView prevView; + int parenOpenCount = 0; + + Button buttonRadian; + Button buttonDegree; + Button buttonSin; + Button buttonLn; + Button buttonCos; + Button buttonLog; + Button buttonTan; + Button buttonSquareRoot; + Button buttonAns; + Button buttonExponent; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + prevView = (TextView) findViewById(R.id.previousView); + textview = (TextView) findViewById(R.id.textView); + textview.setTextSize(30); + + if(savedInstanceState != null){ + textview.setText(savedInstanceState.getString("equation")); + prevView.setText(savedInstanceState.getString("previous view")); + } + + + + Button button7 = (Button) findViewById(R.id.seven); + button7.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("7"); + } + }); + Button button8 = (Button) findViewById(R.id.eight); + button8.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("8"); + } + }); + Button button9 = (Button) findViewById(R.id.nine); + button9.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("9"); + } + }); + Button button4 = (Button) findViewById(R.id.four); + button4.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("4"); + } + }); + Button button5 = (Button) findViewById(R.id.five); + button5.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + int id = view.getId(); + enterClear(enterPressed); + textview.append("5"); + } + }); + Button button6 = (Button) findViewById(R.id.six); + button6.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("6"); + } + }); + Button button1 = (Button) findViewById(R.id.one); + button1.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("1"); + } + }); + Button button2 = (Button) findViewById(R.id.two); + button2.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("2"); + } + }); + Button button3 = (Button) findViewById(R.id.three); + button3.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("3"); + } + }); + + // TODO!! + Button buttonParen = (Button) findViewById(R.id.parentheses); + buttonParen.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + boolean shouldClose; + + if(textview.getText().toString().equals("")){ + shouldClose = false; + }else{ + String equation = textview.getText().toString(); + char lastChar = equation.charAt(equation.length() - 1); + + if(isOperator(Character.toString(lastChar))){ + shouldClose = false; + }else if(lastChar == '('){ + shouldClose = false; + }else if(lastChar == ')' && parenOpenCount == 0){ + shouldClose = false; + textview.append("*"); + }else if(lastChar == ')'){ + shouldClose = true; + }else if(parenOpenCount == 0){ + shouldClose = false; + textview.append("*"); + }else{ + shouldClose = true; + } + + } + if(shouldClose){ + enterClear(enterPressed); + textview.append(" ) "); + parenOpenCount--; + }else{ + enterClear(enterPressed); + textview.append(" ( "); + parenOpenCount++; + } + + + + + } + }); + Button button0 = (Button) findViewById(R.id.zero); + button0.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + textview.append("0"); + } + }); + + // TODO !! + Button buttonDecimal = (Button) findViewById(R.id.decimal); + buttonDecimal.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + String text = textview.getText().toString(); + if(text.equals("")){ + textview.append("0."); + }else if(!isOperator(text) && !(text.charAt(text.length() - 1) == '.')) + textview.append("."); + } + }); + Button buttonClear = (Button) findViewById(R.id.clear); + buttonClear.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + textview.setText(""); + } + }); + Button buttonAdd = (Button) findViewById(R.id.add); + buttonAdd.setOnClickListener(new View.OnClickListener() { + + + @Override + public void onClick(View view) { + if (textview.getText().equals("")) { + textview.setText(ans+ "+"); + } else if (!isOperator(textview.getText().toString())){ + textview.append("+"); + } + + enterClear(enterPressed); + + } + }); + Button buttonSubtract = (Button) findViewById(R.id.subtract); + buttonSubtract.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (textview.getText().equals("")) { + textview.setText(ans+ "-"); + } else if (!isOperator(textview.getText().toString())){ + textview.append("-"); + } + + enterClear(enterPressed); + + } + }); + Button buttonPercentage = (Button) findViewById(R.id.percentage); + buttonPercentage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String text = textview.getText().toString(); + if (textview.getText().equals("")) { + }else if(isNumber(text.charAt(text.length() - 1))){ + textview.append("%"); + } + } + }); + Button buttonBackspace = (Button) findViewById(R.id.backspace); + buttonBackspace.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String text = textview.getText().toString(); + enterClear(enterPressed); + if (text.equals("")) { + // do nothing + } else { + char lastChar = text.charAt(text.length() - 1); + if(lastChar == '('){ + parenOpenCount--; + }else if(lastChar == ')'){ + parenOpenCount++; + } + textview.setText(textview.getText().toString().substring(0, textview.getText().toString().length() - 1)); + } + } + }); + Button buttonMultiply = (Button) findViewById(R.id.multiply); + buttonMultiply.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (textview.getText().equals("")) { + textview.setText(ans+ "*"); + } else if (!isOperator(textview.getText().toString())){ + + textview.append("*"); + } + + enterClear(enterPressed); + } + }); + Button buttonDivide = (Button) findViewById(R.id.divide); + buttonDivide.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (textview.getText().equals("")) { + textview.setText(ans+ "/"); + } else if (!isOperator(textview.getText().toString())){ + textview.append("/"); + } + + enterClear(enterPressed); + + } + }); + + +// ENTER BUTTON HERE !! + + Button buttonEnter = (Button) findViewById(R.id.enter); + buttonEnter.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (textview.getText().equals("")) { + } else if (parenOpenCount > 0 ) { + textview.setText("SYNTAX ERROR"); + textview.postDelayed(new Runnable() { + @Override + public void run() { + textview.setText(""); + } + }, 1250); + } else { + try { + expression = new Expression(parseEquation(textview.getText().toString())); + BigDecimal answer = expression.eval(); + prevView.append(textview.getText().toString() + "\n"); + prevView.append(answer.toPlainString() + "\n"); + textview.setText(answer.toPlainString()); + ans = answer.toPlainString(); + enterPressed = true; + } catch (RuntimeException e){ + textview.setText("ERROR" + " "); + textview.postDelayed(new Runnable() { + @Override + public void run() { + textview.setText(""); + } + }, 1250); + } + } + } + }); + + + // + // + // + // + // LANDSCAPE SCIENTIFIC BUTTON CODE + // + // + // + // + // + + + buttonRadian = (Button) findViewById(R.id.Radian); + if (buttonRadian != null) { + buttonRadian.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // isRadian = true; + buttonDegree.setEnabled(true); + buttonRadian.setEnabled(false); + enterClear(enterPressed); + } + }); + } + + buttonDegree = (Button) findViewById(R.id.Degree); + if (buttonDegree != null) { + buttonDegree.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { +// isRadian = false; + buttonDegree.setEnabled(false); + buttonRadian.setEnabled(true); + enterClear(enterPressed); + } + }); + } + + + Button buttonFactorial = (Button) findViewById(R.id.factorial); + if (buttonFactorial != null) { + buttonFactorial.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + String text = textview.getText().toString(); + if(text.equals("")){ + + }else if(!isOperator(text)){ + textview.append("!"); + } + } + }); + } + + Button buttonInverse = (Button) findViewById(R.id.inverse); + if (buttonInverse != null) { + buttonInverse.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + buttonSin.setText("sin^-1"); + buttonLn.setText("e^"); + buttonCos.setText("ros^-1"); + buttonLog.setText("10^x"); + buttonTan.setText("ran^-1"); + buttonSquareRoot.setText("x^2"); + buttonAns.setText("rnd"); + buttonExponent.setText("y√x"); + } + }); + } + + buttonSin = (Button) findViewById(R.id.sin); + if (buttonSin != null) { + buttonSin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + if (buttonSin.getText().toString().equalsIgnoreCase("sin")) { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("sin("); + } + } else if (buttonSin.getText().toString().equalsIgnoreCase("sin^-1")) { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("asin("); + } + } + } + }); + } + + buttonLn = (Button) findViewById(R.id.naturalLog); + if (buttonLn != null) { + buttonLn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + + if (buttonLn.getText().toString().equalsIgnoreCase("ln") || textview.getText().toString().length() - 1 == '(') { + if (isOperator(textview.getText().toString())) { + parenOpenCount++; + textview.append("log("); + } + } else if (buttonLn.getText().toString().equalsIgnoreCase("e^")) { + if (isOperator(textview.getText().toString())) { + parenOpenCount++; + textview.append("e^("); + } + } + + } + }); + } + + Button buttonPi = (Button) findViewById(R.id.pi); + if (buttonPi != null) { + buttonPi.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String text = textview.getText().toString(); + enterClear(enterPressed); + if (isOperator(textview.getText().toString()) || textview.getText().toString().charAt(text.length()-1) == '(') { + textview.append("PI"); + } + } + }); + } + + buttonCos = (Button) findViewById(R.id.cos); + if (buttonCos != null) { + buttonCos.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + + if (buttonCos.getText().toString().equalsIgnoreCase("cos") || textview.getText().toString().length() - 1 == '(') { + if (isOperator(textview.getText().toString())) { + parenOpenCount++; + textview.append("cos("); + } + } else if (buttonCos.getText().toString().equalsIgnoreCase("cos^-1") || textview.getText().toString().length() - 1 == '(') { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("acos("); + } + } + + } + }); + } + + buttonLog = (Button) findViewById(R.id.log); + if (buttonLog != null) { + buttonLog.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + + if (buttonLog.getText().toString().equalsIgnoreCase("log")) { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("log10("); + } + } else if (buttonLog.getText().toString().equalsIgnoreCase("10^")) { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("10^("); + } + } + + } + }); + } + + Button buttonE = (Button) findViewById(R.id.e); + if (buttonE != null) { + buttonE.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + textview.append("e"); + } + + } + }); + } + + buttonTan = (Button) findViewById(R.id.tan); + if (buttonTan != null) { + buttonTan.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + + if (buttonTan.getText().toString().equalsIgnoreCase("tan")) { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("tan("); + } + } else if (buttonTan.getText().toString().equalsIgnoreCase("tan^-1") ) { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("atan("); + } + } + + } + }); + } + + buttonSquareRoot = (Button) findViewById(R.id.squareRoot); + if (buttonSquareRoot != null) { + buttonSquareRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + + if (buttonSquareRoot.getText().toString().equalsIgnoreCase("√")) { + if (isOperator(textview.getText().toString()) || textview.getText().toString().length() - 1 == '(') { + parenOpenCount++; + textview.append("sqrt("); + } + } else if (buttonSquareRoot.getText().toString().equalsIgnoreCase("x^2")) { + // TODO: will there be an issue with parentheses and parsing order?? + if (!isOperator(textview.getText().toString()) || !textview.getText().toString().equals("")) { + textview.append("^2"); + } + } + } + }); + } + + buttonAns = (Button) findViewById(R.id.Answer); + if (buttonAns != null) { + buttonAns.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + + if (buttonAns.getText().toString().equalsIgnoreCase("ans")) { + if (isOperator(textview.getText().toString())) { + textview.append(ans.toString()); + } + } else if (buttonAns.getText().toString().equalsIgnoreCase("rnd")) { + double num = Math.random(); + if (isOperator(textview.getText().toString())) { + textview.append(String.valueOf(num)); + } + } + + } + }); + } + + Button buttonExp = (Button) findViewById(R.id.exponent); + if (buttonExp != null) { + buttonExp.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + enterClear(enterPressed); + if (isOperator(textview.getText().toString())) { + parenOpenCount++; + textview.append("10^("); + } + + } + }); + } + + final Button buttonExponent = (Button) findViewById(R.id.exponential); + if (buttonExponent != null) { + buttonExponent.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + if (textview.getText().equals("")) { + } else if (!isOperator(textview.getText().toString())){ + enterClear(enterPressed); + + if (buttonExponent.getText().toString().equalsIgnoreCase("x^y")) { + + if (!isOperator(textview.getText().toString())) { + + parenOpenCount++; + textview.append("^("); + } + } else if (buttonExponent.getText().toString().equalsIgnoreCase("y√x")) { + + } + + } + + } + }); + } + + + } + + + @Override + protected void onSaveInstanceState(Bundle outState) { + outState.putString("equation", textview.getText().toString()); + outState.putString("previous view", prevView.getText().toString()); + + super.onSaveInstanceState(outState); + } + + + public void enterClear(boolean bool) { + if (bool == true) { + textview.setText(""); + enterPressed = false; + + } + } + + // TODO: FIX THIS !!!!! + public void Parse(boolean bool) { + if (bool == false) { +// expression = new Expression("DEG("+textview.getText().toString()+")"); +// BigDecimal answer = expression.eval(); +// textview.setText(answer.toPlainString()); +// ans = answer.toPlainString(); +// enterPressed = true; + } else { +// expression = new Expression("RAD("+textview.getText().toString()+")"); +// BigDecimal answer = expression.eval(); +// textview.setText(answer.toPlainString()); +// ans = answer.toPlainString(); +// enterPressed = true; + } + } + + + public boolean isOperator(String input) { + + if (textview.getText().toString().equals("")) { + return true; + } + + char lastC = input.charAt(input.length()-1); + char[] operators = {'+', '-', '*', '÷'}; + + + + for(char operator : operators){ + if (lastC == operator) { + return true; + } + } + + return false; + + } + + public boolean isOperator(char chara){ + return isOperator(Character.toString(chara)); + } + + public boolean isNumber(char chara){ + int aValue = (int) chara; + return aValue >= 48 && aValue <= 57; + } + + public String parseEquation(String equation){ + + ArrayList equationList = new ArrayList(); + String segment = ""; + + for(int i = 0; i < equation.length(); i++){ + char currentChar = equation.charAt(i); + if(isOperator(currentChar)){ + equationList.add(segment); + segment = ""; + equationList.add(Character.toString(currentChar)); + }else{ + segment += currentChar; + } + } + equationList.add(segment); + + equation = ""; + for(String eqSegment : equationList){ + eqSegment = parseEqSegment(eqSegment); + equation += eqSegment; + } + + equation = parsePercentage(equation); + + return equation; + } + + public String parseEqSegment(String segment){ + + for(int i = 0; i < segment.length(); i++){ + if(segment.charAt(i) == '!'){ + int factIndex = i; + int numStart = 0; + + for(int j = factIndex - 1; j >= 0; j--){ + + if(!isNumber(segment.charAt(j))){ + numStart = j+1; + break; + } + } + + String numString = segment.substring(numStart,factIndex); + int num = Integer.parseInt(numString); + + String factString = "(1"; + + for(int j = 2; j <= num; j++){ + factString += "*" + j; + } + + factString += ")"; + + String newSegment = ""; + + for(int j = 0; j < numStart; j++){ + newSegment += segment.charAt(j); + } + newSegment += factString; + for(int j = factIndex+1; j < segment.length(); j++){ + newSegment += segment.charAt(j); + } + + return newSegment; + + } + } + + return segment; + } + + public String parsePercentage(String equation){ + + for(int i = 0; i < equation.length(); i++){ + if(equation.charAt(i) == '%'){ + int percentIndex = i; + int numStart = 0; + + for(int j = percentIndex - 1; j >= 0; j--){ + + if(!isNumber(equation.charAt(j))){ + numStart = j+1; + break; + } + } + + + // code for storing the number before the percentage number (numString)! + for(int k = numStart; k > 0; i--) { + if(isNumber(equation.charAt(k))) { + int prevNumEnd = k; + int prevNumStart = 0; + + for(int l = prevNumEnd; l >= 0; l--) { + if(!isNumber(equation.charAt(l))) { + prevNumStart = l+1; + break; + } + } + + String prevNumString = equation.substring(prevNumStart, prevNumEnd); + int prevNum = Integer.parseInt(prevNumString); + + String numString = equation.substring(numStart,percentIndex); + int num = Integer.parseInt(numString); + + num = (num/100) * prevNum; + + + String finString = String.valueOf(num); + + String newSegment = "("; + + for(int j = 0; j < numStart; j++){ + newSegment += equation.charAt(j); + } + newSegment += finString; + for(int j = percentIndex+1; j < equation.length(); j++){ + newSegment += equation.charAt(j); + } + newSegment += ")"; + + return newSegment; + + } + + + + } + + + } + } + + return equation; + } + +} + diff --git a/CalcProj/app/src/main/res/drawable-v21/number_button.xml b/CalcProj/app/src/main/res/drawable-v21/number_button.xml new file mode 100644 index 0000000..4fda94e --- /dev/null +++ b/CalcProj/app/src/main/res/drawable-v21/number_button.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CalcProj/app/src/main/res/drawable/function_button.xml b/CalcProj/app/src/main/res/drawable/function_button.xml new file mode 100644 index 0000000..ccd2589 --- /dev/null +++ b/CalcProj/app/src/main/res/drawable/function_button.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/CalcProj/app/src/main/res/drawable/number_button.xml b/CalcProj/app/src/main/res/drawable/number_button.xml new file mode 100644 index 0000000..4fda94e --- /dev/null +++ b/CalcProj/app/src/main/res/drawable/number_button.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CalcProj/app/src/main/res/drawable/orange_button.xml b/CalcProj/app/src/main/res/drawable/orange_button.xml new file mode 100644 index 0000000..f52ca51 --- /dev/null +++ b/CalcProj/app/src/main/res/drawable/orange_button.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/CalcProj/app/src/main/res/drawable/white_button.xml b/CalcProj/app/src/main/res/drawable/white_button.xml new file mode 100644 index 0000000..ddb75ee --- /dev/null +++ b/CalcProj/app/src/main/res/drawable/white_button.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/CalcProj/app/src/main/res/layout-land/activity_main.xml b/CalcProj/app/src/main/res/layout-land/activity_main.xml new file mode 100644 index 0000000..4e2eb7b --- /dev/null +++ b/CalcProj/app/src/main/res/layout-land/activity_main.xml @@ -0,0 +1,484 @@ + + + + + + + + + + + + + + + + + + + +