From 497077d28d885584afd1006e49c3ca3bda7f7c91 Mon Sep 17 00:00:00 2001 From: serso Date: Mon, 24 Oct 2011 23:11:50 +0400 Subject: [PATCH] symbolic calculations added --- res/layout-land/main.xml | 26 +++--- res/layout-port/main.xml | 6 ++ res/layout/calc_display.xml | 1 - res/layout/calc_empty_button.xml | 10 +++ res/layout/calc_equals_button.xml | 3 + .../calculator/CalculatorActivity.java | 10 ++- .../android/calculator/CalculatorDisplay.java | 13 +++ .../CalculatorDisplayHistoryState.java | 44 +++++----- .../calculator/CalculatorEngineControl.java | 19 +++++ .../calculator/CalculatorHistoryActivity.java | 14 +++- .../android/calculator/CalculatorModel.java | 58 +++++++------ .../calculator/CursorDragProcessor.java | 2 + .../calculator/EditorHistoryState.java | 30 +++---- .../android/calculator/EvalDragProcessor.java | 44 ++++++++++ .../calculator/model/CalculatorEngine.java | 29 ++++++- .../model/CalculatorEngineTest.java | 84 +++++++++---------- 16 files changed, 270 insertions(+), 123 deletions(-) create mode 100644 res/layout/calc_empty_button.xml create mode 100644 src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java create mode 100644 src/main/java/org/solovyev/android/calculator/EvalDragProcessor.java diff --git a/res/layout-land/main.xml b/res/layout-land/main.xml index a8a91a43..26f14128 100644 --- a/res/layout-land/main.xml +++ b/res/layout-land/main.xml @@ -25,9 +25,9 @@ calc:directionTextScale="0.5" style="?controlButtonStyle" a:onClick="moveLeftButtonClickHandler" - a:layout_width="fill_parent" + a:layout_width="0dp" a:layout_height="fill_parent" - a:layout_weight="1.5"/> + a:layout_weight="1"/> + a:layout_weight="1"/> + a:layout_weight="4"/> + a:layout_weight="1"/> + a:layout_weight="1"/> @@ -80,6 +80,7 @@ + @@ -92,19 +93,20 @@ + - + - + diff --git a/res/layout-port/main.xml b/res/layout-port/main.xml index 26f34451..1cb6f49a 100644 --- a/res/layout-port/main.xml +++ b/res/layout-port/main.xml @@ -16,7 +16,13 @@ + + diff --git a/res/layout/calc_display.xml b/res/layout/calc_display.xml index 220bc7fc..4660891d 100644 --- a/res/layout/calc_display.xml +++ b/res/layout/calc_display.xml @@ -10,7 +10,6 @@ xmlns:a="http://schemas.android.com/apk/res/android" a:id="@+id/calculatorDisplay" style="@style/display_style" - a:layout_weight="1" a:minLines="1" a:maxLines="1" a:textStyle="bold" diff --git a/res/layout/calc_empty_button.xml b/res/layout/calc_empty_button.xml new file mode 100644 index 00000000..9d363fde --- /dev/null +++ b/res/layout/calc_empty_button.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/res/layout/calc_equals_button.xml b/res/layout/calc_equals_button.xml index c864f01d..28180d11 100644 --- a/res/layout/calc_equals_button.xml +++ b/res/layout/calc_equals_button.xml @@ -7,8 +7,11 @@ --> textHighlighter = new TextHighlighter(Color.WHITE, true); @@ -47,6 +51,15 @@ public class CalculatorDisplay extends TextView { this.valid = valid; } + public void setJsclOperation(@NotNull JsclOperation jsclOperation) { + this.jsclOperation = jsclOperation; + } + + @NotNull + public JsclOperation getJsclOperation() { + return jsclOperation; + } + @Override public void setText(CharSequence text, BufferType type) { super.setText(text, type); diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java b/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java index a4dcf0c7..8b059c72 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java @@ -6,7 +6,7 @@ package org.solovyev.android.calculator; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; /** * User: serso @@ -18,45 +18,49 @@ public class CalculatorDisplayHistoryState { private boolean valid = true; @NotNull - private final EditorHistoryState editorHistoryState; + private EditorHistoryState editorHistoryState; - public CalculatorDisplayHistoryState() { - this.editorHistoryState = new EditorHistoryState(); + @NotNull + private JsclOperation jsclOperation; + + private CalculatorDisplayHistoryState() { } - public CalculatorDisplayHistoryState(boolean valid) { - this.editorHistoryState = new EditorHistoryState(); - this.valid = valid; - } + @NotNull + public static CalculatorDisplayHistoryState newInstance(@NotNull CalculatorDisplay display) { + final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); - public CalculatorDisplayHistoryState(int cursorPosition, @Nullable String text, boolean valid) { - this.editorHistoryState = new EditorHistoryState(cursorPosition, text); - this.valid = valid; + result.editorHistoryState = EditorHistoryState.newInstance(display); + result.valid = display.isValid(); + result.jsclOperation = display.getJsclOperation(); + + return result; } public boolean isValid() { return valid; } - public void setValid(boolean valid) { - this.valid = valid; - } - @NotNull public EditorHistoryState getEditorHistoryState() { return editorHistoryState; } + @NotNull + public JsclOperation getJsclOperation() { + return jsclOperation; + } + @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof CalculatorDisplayHistoryState)) return false; + if (o == null || getClass() != o.getClass()) return false; CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; if (valid != that.valid) return false; - if (editorHistoryState != null ? !editorHistoryState.equals(that.editorHistoryState) : that.editorHistoryState != null) - return false; + if (!editorHistoryState.equals(that.editorHistoryState)) return false; + if (jsclOperation != that.jsclOperation) return false; return true; } @@ -64,7 +68,8 @@ public class CalculatorDisplayHistoryState { @Override public int hashCode() { int result = (valid ? 1 : 0); - result = 31 * result + (editorHistoryState != null ? editorHistoryState.hashCode() : 0); + result = 31 * result + editorHistoryState.hashCode(); + result = 31 * result + jsclOperation.hashCode(); return result; } @@ -72,6 +77,7 @@ public class CalculatorDisplayHistoryState { public String toString() { return "CalculatorDisplayHistoryState{" + "valid=" + valid + + "jsclOperation=" + jsclOperation + ", editorHistoryState=" + editorHistoryState + '}'; } diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java b/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java new file mode 100644 index 00000000..6de56347 --- /dev/null +++ b/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator; + +/** + * User: serso + * Date: 10/24/11 + * Time: 9:55 PM + */ +public interface CalculatorEngineControl { + + void evaluate(); + + void simplify(); +} diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java b/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java index 2be32566..5c952df6 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorHistoryActivity.java @@ -12,6 +12,7 @@ import android.os.Bundle; import android.view.*; import android.widget.*; import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.common.utils.Filter; import org.solovyev.common.utils.FilterRule; import org.solovyev.common.utils.FilterRulesChain; @@ -53,14 +54,16 @@ public class CalculatorHistoryActivity extends ListActivity { final int position, final long id) { + final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position); + CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() { @Override public void doOperation(@NotNull EditText editor) { - final EditorHistoryState editorState = ((CalculatorHistoryState) parent.getItemAtPosition(position)).getEditorState(); + final EditorHistoryState editorState = historyState.getEditorState(); editor.setText(editorState.getText()); editor.setSelection(editorState.getCursorPosition()); } - }, false); + }, false, historyState.getDisplayState().getJsclOperation(), true); CalculatorModel.instance.setCursorOnEnd(); @@ -108,10 +111,15 @@ public class CalculatorHistoryActivity extends ListActivity { time.setText(new SimpleDateFormat().format(state.getTime())); final TextView editor = (TextView) result.findViewById(R.id.history_item); - editor.setText(state.getEditorState().getText() + "=" + state.getDisplayState().getEditorHistoryState().getText()); + editor.setText(state.getEditorState().getText() + getIdentitySign(state.getDisplayState().getJsclOperation()) + state.getDisplayState().getEditorHistoryState().getText()); return result; } + + @NotNull + private String getIdentitySign(@NotNull JsclOperation jsclOperation) { + return jsclOperation == JsclOperation.simplify ? "≡" : "="; + } } @Override diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java index 1c6d2c9b..8c5cc009 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -34,7 +34,7 @@ import org.solovyev.common.utils.history.HistoryAction; * Date: 9/12/11 * Time: 11:15 PM */ -public enum CalculatorModel implements CursorControl, HistoryControl { +public enum CalculatorModel implements CursorControl, HistoryControl, CalculatorEngineControl { instance; @@ -51,6 +51,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl pendingOperation = new MutableObject(); - private void evaluate(boolean delayEvaluate, @NotNull final String expression) { + private void evaluate(boolean delayEvaluate, @NotNull final String expression, @NotNull final JsclOperation operation) { final CalculatorHistoryState historyState = getCurrentHistoryState(); pendingOperation.setObject(new Runnable() { @@ -147,7 +153,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl mr) throws EvalError, ParseException { synchronized (lock) { @@ -190,7 +213,7 @@ public enum CalculatorEngine { throw new ParseException("Too long calculation for: " + jsclExpression); } - return operation.getFromProcessor().process(result); + return new Result(operation.getFromProcessor().process(result), operation); } } diff --git a/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java b/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java index c250b410..18c6e0c6 100644 --- a/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java +++ b/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java @@ -66,54 +66,54 @@ public class CalculatorEngineTest { public void testEvaluate() throws Exception { final CalculatorEngine cm = CalculatorEngine.instance; - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2")); - Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)")); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)")); - Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)")); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)sqrt(2)")); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)")); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2")); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2")); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)")); - Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+sqrt(-1)")); - Assert.assertEquals("0.921+3.142i", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))")); - Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)")); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)")); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)")); - Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i")); - Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i")); - Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i")); - Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i")); - Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)")); - Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)")); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getResult()); + Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getResult()); + Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)sqrt(2)").getResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getResult()); + Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+sqrt(-1)").getResult()); + Assert.assertEquals("0.921+3.142i", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getResult()); + Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getResult()); + Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getResult()); + Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getResult()); + Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getResult()); + Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getResult()); + Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getResult()); + Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getResult()); CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("si", 5d)); - Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)")); - Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si")); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si")); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si")); - Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si")); + Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getResult()); + Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getResult()); + Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getResult()); CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("s", 1d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si")); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("k", 3.5d)); CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("k1", 4d)); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11")); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getResult()); CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("t", (String) null)); - Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11")); - Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e")); - Assert.assertEquals("11×Infinityt", cm.evaluate(JsclOperation.numeric, "t11∞")); - Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)")); + Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getResult()); + Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getResult()); + Assert.assertEquals("11×Infinityt", cm.evaluate(JsclOperation.numeric, "t11∞").getResult()); + Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°")); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°")); - Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2")); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult()); + Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult()); try { - Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2")); + Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult()); junit.framework.Assert.fail(); } catch (ParseException e) { if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { @@ -130,16 +130,16 @@ public class CalculatorEngineTest { Assert.fail(); } catch (ParseException e) { } - Assert.assertEquals("NaN", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))")); + Assert.assertEquals("NaN", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult()); try { cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); Assert.fail(); } catch (ParseException e) { } - Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))")); + Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getResult()); CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("si", 5d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si")); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); try { cm.evaluate(JsclOperation.numeric, "sin"); Assert.fail(); @@ -156,11 +156,11 @@ public class CalculatorEngineTest { decimalGroupSymbols.setGroupingSeparator('\''); cm.setDecimalGroupSymbols(decimalGroupSymbols); cm.setPrecision(2); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7")); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); cm.setPrecision(10); - Assert.assertEquals("12'345'678.899999999", cm.evaluate(JsclOperation.numeric, "1.23456789E7")); - Assert.assertEquals("123'456'788.99999999", cm.evaluate(JsclOperation.numeric, "1.234567890E8")); - Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9")); + Assert.assertEquals("12'345'678.899999999", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); + Assert.assertEquals("123'456'788.99999999", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getResult()); + Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getResult()); }