var activity

This commit is contained in:
serso 2011-10-03 02:02:14 +04:00
parent 7fa8eb6f66
commit 454789a408
12 changed files with 376 additions and 131 deletions

View File

@ -37,7 +37,7 @@
<EditText a:id="@+id/var_edit_value"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:inputType="number"
a:inputType="numberDecimal"
a:textSize="20dp">
</EditText>

View File

@ -26,7 +26,10 @@ import org.solovyev.util.math.Complex;
public class CalculatorModel {
@NotNull
private final Interpreter interpreter;
private Interpreter interpreter;
@NotNull
private final Object interpreterMonitor = new Object();
private int numberOfFractionDigits = 5;
@ -38,30 +41,41 @@ public class CalculatorModel {
private static CalculatorModel instance;
private CalculatorModel(@Nullable Context context) throws EvalError {
private CalculatorModel(@Nullable Context context) {
load(context);
reset();
}
public void reset() {
synchronized (interpreterMonitor) {
try {
interpreter = new Interpreter();
interpreter.eval(ToJsclPreprocessor.wrap(JsclOperation.importCommands, "/jscl/editorengine/commands"));
/*for (Var var : varsRegister.getVars()) {
if (!var.isSystem()) {
exec(var.getName() + "=" + var.getValue() + ";");
}
}*/
} catch (EvalError evalError) {
throw new RuntimeException(evalError);
}
}
}
public String evaluate(@NotNull JsclOperation operation, @NotNull String expression) throws EvalError, ParseException {
final StringBuilder sb = new StringBuilder();
/*
for (Var var : varsRegister.getVars()) {
if (!var.isSystem()) {
sb.append(var.getName()).append("=").append(var.getValue()).append(";");
}
}
*/
sb.append(preprocessor.process(expression));
//Log.d(CalculatorModel.class.getName(), "Preprocessed expression: " + preprocessedExpression);
Object evaluationObject = interpreter.eval(ToJsclPreprocessor.wrap(operation, sb.toString()));
final Object evaluationObject;
synchronized (interpreterMonitor) {
evaluationObject = interpreter.eval(ToJsclPreprocessor.wrap(operation, sb.toString()));
}
String result = String.valueOf(evaluationObject).trim();
try {
@ -165,8 +179,34 @@ public class CalculatorModel {
return instance != null;
}
private void exec(String str) throws EvalError {
interpreter.eval(str);
}
private String eval(String str) throws EvalError {
return interpreter.eval(commands(str)).toString();
}
@NotNull
public VarsRegister getVarsRegister() {
return varsRegister;
}
String commands(String str) {
return commands(str, false);
}
String commands(String str, boolean found) {
for (int i = 0; i < cmds.length; i++) {
int n = str.length() - cmds[i].length() - 1;
if (n >= 0 && (" " + cmds[i].toLowerCase()).equals(str.substring(n)))
return commands(str.substring(0, n), true) + "." + cmds[i] + "()";
}
str = str.replaceAll("\n", "");
return found ? "jscl.math.Expression.valueOf(\"" + str + "\")" : str;
}
static final String cmds[] = new String[]{"expand", "factorize", "elementary", "simplify", "numeric", "toMathML", "toJava"};
}

View File

@ -177,10 +177,10 @@ public class CalculatorVarsActivity extends ListActivity {
Toast.makeText(CalculatorVarsActivity.this, error, Toast.LENGTH_LONG).show();
createEditVariableDialog(editedInstance, name, value, description);
} else {
if (editedInstance != null && !editedInstance.getName().equals(name)) {
varsRegister.addVar(editedInstance.getName(), varBuilder);
} else {
if ( editedInstance == null ) {
CalculatorVarsActivity.this.adapter.add(varsRegister.addVar(null, varBuilder));
} else {
varsRegister.addVar(editedInstance.getName(), varBuilder);
}
varsRegister.save(CalculatorVarsActivity.this);

View File

@ -192,7 +192,7 @@ public class CalculatorView implements CursorControl, HistoryControl<CalculatorH
@Override
public void doOperation(@NotNull EditText editor) {
final MathEntityType type = MathEntityType.getType(text);
final MathEntityType type = MathEntityType.getType(text, 0);
int cursorPositionOffset = 0;
final StringBuilder textToBeInserted = new StringBuilder(text);

View File

@ -0,0 +1,38 @@
/*
* 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;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Finder;
/**
* User: serso
* Date: 10/3/11
* Time: 12:54 AM
*/
public class CharacterAtPositionFinder implements Finder<Character> {
private int i;
@NotNull
private final String targetString;
public CharacterAtPositionFinder(@NotNull String targetString, int i) {
this.targetString = targetString;
this.i = i;
}
@Override
public boolean isFound(@Nullable Character s) {
return s != null && s.equals(targetString.charAt(i));
}
public void setI(int i) {
this.i = i;
}
}

View File

@ -0,0 +1,38 @@
/*
* 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;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Finder;
/**
* User: serso
* Date: 10/3/11
* Time: 12:49 AM
*/
public class StartsWithFinder implements Finder<String> {
private int i;
@NotNull
private final String targetString;
public StartsWithFinder(@NotNull String targetString, int i) {
this.targetString = targetString;
this.i = i;
}
@Override
public boolean isFound(@Nullable String s) {
return targetString.startsWith(s, i);
}
public void setI(int i) {
this.i = i;
}
}

View File

@ -19,46 +19,88 @@ public class ToJsclPreprocessor implements Preprocessor {
@Override
@NotNull
public String process(@NotNull String s) {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder sb = new StringBuilder();
boolean constantBefore = false;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if ( MathEntityType.getType(ch) == MathEntityType.postfix_function ) {
int start = getPostfixFunctionStart(s, i - 1);
}
}
startsWithFinder.setI(i);
final StartsWithFinder startsWithFinder = new StartsWithFinder(s);
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
checkMultiplicationSignBeforeFunction(sb, s, i);
checkMultiplicationSignBeforeFunction(sb, s, i, constantBefore);
constantBefore = false;
if (MathEntityType.openGroupSymbols.contains(ch)) {
sb.append('(');
} else if (MathEntityType.closeGroupSymbols.contains(ch)) {
sb.append(')');
} else if (ch == 'π') {
sb.append("pi");
} else if (ch == '×' || ch == '∙') {
sb.append("*");
} else {
startsWithFinder.setI(i);
final String function = CollectionsUtils.get(MathEntityType.prefixFunctions, startsWithFinder);
if (function != null) {
sb.append(toJsclFunction(function));
i += function.length() - 1;
} else if (ch == 'e') {
sb.append("exp(1)");
} else if (ch == 'i') {
sb.append("sqrt(-1)");
} else {
String entity = CollectionsUtils.get(MathEntityType.prefixFunctions, startsWithFinder);
if (entity == null) {
entity = CollectionsUtils.get(CalculatorModel.getInstance().getVarsRegister().getVarNames(), startsWithFinder);
if (entity == null) {
sb.append(ch);
} else {
sb.append(entity);
i += entity.length() - 1;
constantBefore = true;
}
} else {
sb.append(toJsclFunction(entity));
i += entity.length() - 1;
}
}
}
return sb.toString();
return replaceVariables(sb.toString());
}
private String replaceVariables(@NotNull final String s) {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
startsWithFinder.setI(i);
int offset = 0;
String functionName = CollectionsUtils.get(MathEntityType.prefixFunctions, startsWithFinder);
if (functionName == null) {
String varName = CollectionsUtils.get(CalculatorModel.getInstance().getVarsRegister().getVarNames(), startsWithFinder);
if (varName != null) {
final Var var = CalculatorModel.getInstance().getVarsRegister().getVar(varName);
if (var != null) {
result.append(var.getValue());
offset = varName.length();
}
}
} else {
result.append(functionName);
offset = functionName.length();
}
if (offset == 0) {
result.append(s.charAt(i));
} else {
i += offset - 1;
}
}
return result.toString();
}
private void replaceVariables(StringBuilder sb, String s, int i, @NotNull StartsWithFinder startsWithFinder) {
for (Var var : CalculatorModel.getInstance().getVarsRegister().getVars()) {
if (!var.isSystem()) {
if (s.startsWith(var.getName(), i)) {
if (CollectionsUtils.get(MathEntityType.prefixFunctions, startsWithFinder) == null) {
}
}
}
}
}
public int getPostfixFunctionStart(@NotNull String s, int position) {
@ -67,16 +109,15 @@ public class ToJsclPreprocessor implements Preprocessor {
int numberOfOpenGroups = 0;
int result = position;
for (; result >= 0; result--) {
char ch = s.charAt(result);
final MathEntityType mathEntityType = MathEntityType.getType(ch);
final MathEntityType mathEntityType = MathEntityType.getType(s, result);
if (mathEntityType != null) {
if (CollectionsUtils.contains(mathEntityType, MathEntityType.digit, MathEntityType.dot)) {
// continue
} else if (MathEntityType.closeGroupSymbols.contains(ch)) {
} else if (MathEntityType.closeGroupSymbols.contains(s.charAt(result))) {
numberOfOpenGroups++;
} else if (MathEntityType.openGroupSymbols.contains(ch)) {
} else if (MathEntityType.openGroupSymbols.contains(s.charAt(result))) {
numberOfOpenGroups--;
} else {
if (stop(s, numberOfOpenGroups, result)) break;
@ -95,7 +136,7 @@ public class ToJsclPreprocessor implements Preprocessor {
final EndsWithFinder endsWithFinder = new EndsWithFinder(s);
endsWithFinder.setI(i + 1);
if (!CollectionsUtils.contains(MathEntityType.prefixFunctions, FilterType.included, endsWithFinder)) {
MathEntityType type = MathEntityType.getType(s.charAt(i));
MathEntityType type = MathEntityType.getType(s, i);
if (type != null && type != MathEntityType.constant) {
return true;
}
@ -123,27 +164,6 @@ public class ToJsclPreprocessor implements Preprocessor {
return result;
}
private static class StartsWithFinder implements Finder<String> {
private int i;
@NotNull
private final String targetString;
private StartsWithFinder(@NotNull String targetString) {
this.targetString = targetString;
}
@Override
public boolean isFound(@Nullable String s) {
return targetString.startsWith(s, i);
}
public void setI(int i) {
this.i = i;
}
}
private static class EndsWithFinder implements Finder<String> {
private int i;
@ -165,25 +185,25 @@ public class ToJsclPreprocessor implements Preprocessor {
}
}
private static void checkMultiplicationSignBeforeFunction(@NotNull StringBuilder sb, @NotNull String s, int i) {
private static void checkMultiplicationSignBeforeFunction(@NotNull StringBuilder sb, @NotNull String s, int i, boolean constantBefore) {
if (i > 0) {
// get character before function
char chBefore = s.charAt(i - 1);
char ch = s.charAt(i);
final MathEntityType mathTypeBefore = MathEntityType.getType(String.valueOf(chBefore));
final MathEntityType mathType = MathEntityType.getType(String.valueOf(ch));
final MathEntityType mathType = MathEntityType.getType(s, i);
if (mathTypeBefore != MathEntityType.binary_operation &&
if (constantBefore || (mathTypeBefore != MathEntityType.binary_operation &&
mathTypeBefore != MathEntityType.unary_operation &&
mathTypeBefore != MathEntityType.function &&
!MathEntityType.openGroupSymbols.contains(chBefore)) {
!MathEntityType.openGroupSymbols.contains(chBefore))) {
if (mathType == MathEntityType.constant) {
sb.append("*");
} else if (MathEntityType.openGroupSymbols.contains(ch) && mathTypeBefore != null) {
sb.append("*");
} else if (mathType == MathEntityType.digit && mathTypeBefore != MathEntityType.digit && mathTypeBefore != MathEntityType.dot) {
} else if (mathType == MathEntityType.digit && ((mathTypeBefore != MathEntityType.digit && mathTypeBefore != MathEntityType.dot) || constantBefore) ) {
sb.append("*");
} else {
for (String function : MathEntityType.prefixFunctions) {

View File

@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.solovyev.android.calculator.math.MathEntityComparator;
import org.solovyev.android.calculator.math.MathEntityType;
import org.solovyev.common.utils.CollectionsUtils;
import org.solovyev.common.utils.Finder;
@ -46,20 +47,34 @@ public class VarsRegister {
public Var addVar(@Nullable String name, @NotNull Var.Builder builder) {
final Var var = builder.create();
final Var varFromRegister = getVar(name == null ? var.getName() : name);
Var varFromRegister = getVar(name == null ? var.getName() : name);
if (varFromRegister == null) {
varFromRegister = var;
vars.add(var);
} else {
varFromRegister.copy(var);
}
return var;
return varFromRegister;
}
public void remove (@NotNull Var var) {
this.vars.remove(var);
}
@NotNull
public List<String> getVarNames () {
final List<String> result = new ArrayList<String>();
for (Var var : vars) {
result.add(var.getName());
}
Collections.sort(result, new MathEntityComparator());
return result;
}
@Nullable
public Var getVar(@NotNull final String name) {
return CollectionsUtils.get(vars, new Finder<Var>() {
@ -121,7 +136,7 @@ public class VarsRegister {
} else if (systemVarName.equals("π")) {
systemVar = new Var.Builder(systemVarName, Math.PI).setSystem(true).create();
} else if (systemVarName.equals("i")) {
systemVar = new Var.Builder(systemVarName, "(-1)").setSystem(true).create();
systemVar = new Var.Builder(systemVarName, "sqrt(-1)").setSystem(true).create();
} else {
throw new IllegalArgumentException(systemVarName + " is not supported yet!");
}

View File

@ -5,41 +5,48 @@
package org.solovyev.android.calculator.math;
import org.jetbrains.annotations.NonNls;
import java.util.Arrays;
import java.util.List;
import java.util.*;
/**
* User: serso
* Date: 9/17/11
* Time: 10:01 PM
*/
public interface Functions {
public class Functions {
String SIN = "sin";
String SINH = "sinh";
String ASIN = "asin";
String ASINH = "asinh";
String COS = "cos";
String COSH = "cosh";
String ACOS = "acos";
String ACOSH = "acosh";
String TAN = "tan";
String TANH = "tanh";
String ATAN = "atan";
String ATANH = "atanh";
String LOG = "log";
String LN = "ln";
String MOD = "mod";
String EXP = "exp";
String SQRT_SIGN = "";
String SQRT = "sqrt";
public final static String SIN = "sin";
public final static String SINH = "sinh";
public final static String ASIN = "asin";
public final static String ASINH = "asinh";
public final static String COS = "cos";
public final static String COSH = "cosh";
public final static String ACOS = "acos";
public final static String ACOSH = "acosh";
public final static String TAN = "tan";
public final static String TANH = "tanh";
public final static String ATAN = "atan";
public final static String ATANH = "atanh";
public final static String LOG = "log";
public final static String LN = "ln";
public final static String MOD = "mod";
public final static String EXP = "exp";
public final static String SQRT_SIGN = "";
public final static String SQRT = "sqrt";
public static final List<String> allPrefix = Arrays.asList(SIN, SINH, ASIN, ASINH, COS, COSH, ACOS, ACOSH, TAN, TANH, ATAN, ATANH, LOG, LN, MOD, SQRT, SQRT_SIGN, EXP);
public static final List<String> allPrefix;
Character FACT = '!';
Character DEGREE = '°';
static {
final List<String> functions = new ArrayList<String>(Arrays.asList(SIN, SINH, ASIN, ASINH, COS, COSH, ACOS, ACOSH, TAN, TANH, ATAN, ATANH, LOG, LN, MOD, SQRT, SQRT_SIGN, EXP));
Collections.sort(functions, new MathEntityComparator());
allPrefix = functions;
}
public final static Character FACT = '!';
public final static Character DEGREE = '°';
public static final List<Character> allPostfix = Arrays.asList(FACT, DEGREE);
private Functions() {
throw new AssertionError("Not allowed!");
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.math;
import java.util.Comparator;
/**
* User: serso
* Date: 10/3/11
* Time: 12:30 AM
*/
public class MathEntityComparator implements Comparator<String> {
@Override
public int compare(String s, String s1) {
return s1.length() - s.length();
}
}

View File

@ -8,6 +8,8 @@ package org.solovyev.android.calculator.math;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.CharacterAtPositionFinder;
import org.solovyev.android.calculator.StartsWithFinder;
import org.solovyev.android.calculator.Var;
import org.solovyev.common.utils.CollectionsUtils;
import org.solovyev.common.utils.Finder;
@ -31,6 +33,7 @@ public enum MathEntityType {
public static final List<String> constants = Arrays.asList("e", "π", "i");
public static final List<String> digits = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
public static final List<Character> dots = Arrays.asList('.');
public static final List<Character> unaryOperations = Arrays.asList('-', '=', '!');
@ -48,6 +51,7 @@ public enum MathEntityType {
public static final List<Character> closeGroupSymbols = Arrays.asList(']', ')', '}');
public static final List<Character> singleGroupSymbols;
static {
final List<Character> list = new ArrayList<Character>();
list.addAll(openGroupSymbols);
@ -64,10 +68,10 @@ public enum MathEntityType {
}
if (result == null) {
if ( isConstant(s) ) {
result = MathEntityType.constant;
} else if ( prefixFunctions.contains(s) ) {
if (prefixFunctions.contains(s)) {
result = MathEntityType.function;
} else if (isConstant(s)) {
result = MathEntityType.constant;
} else if (groupSymbols.contains(s)) {
result = MathEntityType.group_symbols;
}
@ -113,4 +117,46 @@ public enum MathEntityType {
}
}) != null;
}
public static MathEntityType getType(String s, int i) {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, i);
final CharacterAtPositionFinder characterStartWithFinder = new CharacterAtPositionFinder(s, i);
return getType(startsWithFinder, characterStartWithFinder);
}
@Nullable
private static MathEntityType getType(@NotNull Finder<String> finder, @NotNull CharacterAtPositionFinder characterStartWithFinder) {
MathEntityType result = null;
if (contains(digits, finder)) {
result = MathEntityType.digit;
} else if (contains(postfixFunctions, characterStartWithFinder)) {
result = MathEntityType.postfix_function;
} else if (contains(unaryOperations, characterStartWithFinder)) {
result = MathEntityType.unary_operation;
} else if (contains(binaryOperations, characterStartWithFinder)) {
result = MathEntityType.binary_operation;
} else if (contains(groupSymbols, finder)) {
result = MathEntityType.group_symbols;
} else if (contains(singleGroupSymbols, characterStartWithFinder)) {
result = MathEntityType.group_symbol;
} else if (contains(prefixFunctions, finder)) {
result = MathEntityType.function;
} else if (contains(CalculatorModel.getInstance().getVarsRegister().getVarNames(), finder)) {
result = MathEntityType.constant;
} else if (contains(dots, characterStartWithFinder)) {
result = MathEntityType.dot;
}
return result;
}
private static boolean contains(@NotNull List<String> list, @NotNull final Finder<String> startsWithFinder) {
return CollectionsUtils.get(list, startsWithFinder) != null;
}
private static boolean contains(@NotNull List<Character> list, @NotNull final CharacterAtPositionFinder atPositionFinder) {
return CollectionsUtils.get(list, atPositionFinder) != null;
}
}

View File

@ -5,8 +5,8 @@
package org.solovyev.android.calculator;
import bsh.EvalError;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -47,6 +47,25 @@ public class CalculatorModelTest {
Assert.assertEquals("-3.41007+3.41007i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)"));
Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)"));
CalculatorModel.getInstance().getVarsRegister().addVar(null, new Var.Builder("si", 5d));
Assert.assertEquals("5.0", cm.evaluate(JsclOperation.numeric, "si"));
try {
cm.evaluate(JsclOperation.numeric, "sin");
Assert.fail();
} catch (EvalError e) {
}
Assert.assertEquals("-0.95892", cm.evaluate(JsclOperation.numeric, "sin(5)"));
Assert.assertEquals("-4.79462", cm.evaluate(JsclOperation.numeric, "sin(5)si"));
Assert.assertEquals("-23.97311", cm.evaluate(JsclOperation.numeric, "sisin(5)si"));
Assert.assertEquals("-23.97311", cm.evaluate(JsclOperation.numeric, "si*sin(5)si"));
Assert.assertEquals("-3.30879", cm.evaluate(JsclOperation.numeric, "sisin(5si)si"));
CalculatorModel.getInstance().getVarsRegister().addVar(null, new Var.Builder("s", 1d));
Assert.assertEquals("5.0", cm.evaluate(JsclOperation.numeric, "si"));
CalculatorModel.getInstance().getVarsRegister().addVar(null, new Var.Builder("k", 3.5d));
CalculatorModel.getInstance().getVarsRegister().addVar(null, new Var.Builder("k1", 4d));
Assert.assertEquals("4.0", cm.evaluate(JsclOperation.numeric, "k11"));
}
@Test